- 浏览: 3368422 次
- 性别:
- 来自: 珠海
文章分类
- 全部博客 (1633)
- Java (250)
- Android&HTML5 (111)
- Struts (10)
- Spring (236)
- Hibernate&MyBatis (115)
- SSH (49)
- jQuery插件收集 (55)
- Javascript (145)
- PHP (77)
- REST&WebService (18)
- BIRT (27)
- .NET (7)
- Database (105)
- 设计模式 (16)
- 自动化和测试 (19)
- Maven&Ant (43)
- 工作流 (36)
- 开源应用 (156)
- 其他 (16)
- 前台&美工 (119)
- 工作积累 (0)
- OS&Docker (83)
- Python&爬虫 (28)
- 工具软件 (157)
- 问题收集 (61)
- OFbiz (6)
- noSQL (12)
最新评论
-
HEZR曾嶸:
你好博主,这个不是很理解,能解释一下嘛//左边+1,上边+1, ...
java 两字符串相似度计算算法 -
天使建站:
写得不错,可以看这里,和这里的这篇文章一起看,有 ...
jquery 遍历对象、数组、集合 -
xue88ming:
很有用,谢谢
@PathVariable映射出现错误: Name for argument type -
jnjeC:
厉害,困扰了我很久
MyBatis排序时使用order by 动态参数时需要注意,用$而不是# -
TopLongMan:
非常好,很实用啊。。
PostgreSQL递归查询实现树状结构查询
http://my.oschina.net/u/178116/blog/391559
一、摘要
上两篇文章分别介绍了Spring3.3 整合 Hibernate3、MyBatis3.2 配置多数据源/动态切换数据源 方法 http://www.cnblogs.com/hoojo/p/Spring_Hibernate_MyBatis_MultipleDataSource_switchDataSource.html 和 Spring3 整合Hibernate3.5 动态切换SessionFactory (切换数据库方言)http://www.cnblogs.com/hoojo/p/dynamic_switch_sessionfactory_muliteSessionFactory.html,这篇文章将介绍Spring整合Mybatis 如何完成SqlSessionFactory的动态切换的。并且会简单的介绍下MyBatis整合Spring中的官方的相关代码。
Spring整合MyBatis切换SqlSessionFactory有两种方法,第一、 继承SqlSessionDaoSupport,重写获取SqlSessionFactory的方法。第二、继承SqlSessionTemplate 重写getSqlSessionFactory、getConfiguration和SqlSessionInterceptor这个拦截器。其中最为关键还是继承SqlSessionTemplate 并重写里面的方法。
而Spring整合MyBatis也有两种方式,一种是配置MapperFactoryBean,另一种则是利用MapperScannerConfigurer进行扫描接口或包完成对象的自动创建。相对来说后者更方便些。MapperFactoryBean继承了SqlSessionDaoSupport也就是动态切换SqlSessionFactory的第一种方法,我们需要重写和实现SqlSessionDaoSupport方法,或者是继承MapperFactoryBean来重写覆盖相关方法。如果利用MapperScannerConfigurer的配置整合来切换SqlSessionFactory,那么我们就需要继承SqlSessionTemplate,重写上面提到的方法。在整合的配置中很多地方都是可以注入SqlSessionTemplate代替SqlSessionFactory的注入的。因为SqlSessionTemplate的创建也是需要注入SqlSessionFactory的。
二、实现代码
1、继承SqlSessionTemplate 重写getSqlSessionFactory、getConfiguration和SqlSessionInterceptor
重写后的getSqlSessionFactory方法会从我们配置的SqlSessionFactory集合targetSqlSessionFactorys或默认的defaultTargetSqlSessionFactory中获取Session对象。而改写的SqlSessionInterceptor 是这个MyBatis整合Spring的关键,所有的SqlSessionFactory对象的session都将在这里完成创建、提交、关闭等操作。所以我们改写这里的代码,在这里获取getSqlSessionFactory的时候,从多个SqlSessionFactory中获取我们设置的那个即可。
上面添加了targetSqlSessionFactorys、defaultTargetSqlSessionFactory两个属性来配置多个SqlSessionFactory对象和默认的SqlSessionFactory对象。
CustomerContextHolder 设置SqlSessionFactory的类型
2、配置相关的文件applicationContext-session-factory.xml
上面的配置关键是在MapperScannerConfigurer中注入sqlSessionTemplate,这个要注意。当我们配置了多个SqlSessionFactoryBean的时候,就需要为MapperScannerConfigurer指定一个sqlSessionFactoryBeanName或是sqlSessionTemplateBeanName。一般情况下注入了sqlSessionTemplateBeanName对象,那sqlSessionFactory也就有值了。如果单独的注入了sqlSessionFactory那么程序会创建一个sqlSessionTemplate对象。我们可以看看代码SqlSessionFactoryDaoSupport对象的代码。如果你不喜欢使用扫描的方式,也可以注入sqlSessionTemplate或继承sqlSessionTemplate完成数据库操作。
这段代码很明显,如果注入了sqlSessionTemplate上面的注入也就不会执行了。如果没有注入sqlSessionTemplate,那么会自动new一个sqlSessionTemplate对象。
3、编写相关测试接口和实现的mapper.xml
multiple-datasource-mapper.xml
上面分别查询oracle和mysql两个数据库中的table
4、测试代码
运行后发现能够顺利查询出数据。
如果你是重写SqlSessionDaoSupport,那么方法如下
主要重写getSqlSession方法,上面获取SqlSessionFactory的方法。
重写好了后就可以配置这个对象,配置代码如下
在上面的配置文件中加入配置
就这样也可以利用DynamicSqlSessionDaoSupport来完成动态切换sqlSessionFactory对象,只需用在注入userMapperDao调用方法的时候设置下CustomerContextHolder的contextType即可。
三、总结
为了实现这个功能看了mybatis-spring-1.2.0.jar这个包的部分源代码,代码内容不是很多。所以看了下主要的代码,下面做些简单的介绍。
MapperScannerConfigurer这个类就是我们要扫描的Mapper接口的类,也就是basePackage中继承markerInterface配置的接口。可以看看ClassPathBeanDefinitionScanner、ClassPathMapperScanner中的doScan这个方法。它会扫描basePackage这个包下所有接口,在ClassPathScanningCandidateComponentProvider中有这个方法findCandidateComponents,它会找到所有的BeanDefinition。
最重要的一点是ClassPathMapperScanner中的doScan这个方法它会给这些接口创建一个MapperFactoryBean。并且会检查sqlSessionFactory和sqlSessionTemplate对象的注入情况。
image
所以我们配置扫描的方式也就相当于我们在配置文件中给每一个Mapper配置一个MapperFactoryBean一样。而这个MapperFactoryBean又继承SqlSessionDaoSupport。所以当初我想重写MapperScannerConfigurer中的postProcessBeanDefinitionRegistry方法,然后重写方法中的ClassPathMapperScanner中的doScan方法,将definition.setBeanClass(MapperFactoryBean.class);改成自己定义的MapperFactoryBean。最后以失败告终,因为这里是Spring装载扫描对象的时候都已经为这些对象创建好了代理、设置好了mapperInterface和注入需要的类。所以在调用相关操作数据库的API方法的时候,设置对应的SqlSessionFactory也是无效的。
辗转反侧我看到了SqlSessionTemplate这个类,它的功能相当于SqlSessionDaoSupport的实现类MapperFactoryBean。最为关键的是SqlSessionTemplate有一个拦截器SqlSessionInterceptor,它复制所有SqlSession的创建、提交、关闭,而且是在每个方法之前。这点在上面也提到过了!所以我们只需要在SqlSessionInterceptor方法中获取SqlSessionFactory的时候,在这之前调用下CustomerContextHolder.setContextType方法即可完成数据库的SqlSessionFactory的切换。而在MapperScannerConfigurer提供了注入SqlSessionFactory和sqlSessionTemplate的方法,如果注入了SqlSessionFactory系统将会new一个sqlSessionTemplate,而注入了sqlSessionTemplate就不会创建其他对象(见下面代码)。所以我们配置一个sqlSessionTemplate并注入到MapperScannerConfigurer中,程序将会使用这个sqlSessionTemplate。本文最后的实现方式就是这样完成的。
一、摘要
上两篇文章分别介绍了Spring3.3 整合 Hibernate3、MyBatis3.2 配置多数据源/动态切换数据源 方法 http://www.cnblogs.com/hoojo/p/Spring_Hibernate_MyBatis_MultipleDataSource_switchDataSource.html 和 Spring3 整合Hibernate3.5 动态切换SessionFactory (切换数据库方言)http://www.cnblogs.com/hoojo/p/dynamic_switch_sessionfactory_muliteSessionFactory.html,这篇文章将介绍Spring整合Mybatis 如何完成SqlSessionFactory的动态切换的。并且会简单的介绍下MyBatis整合Spring中的官方的相关代码。
Spring整合MyBatis切换SqlSessionFactory有两种方法,第一、 继承SqlSessionDaoSupport,重写获取SqlSessionFactory的方法。第二、继承SqlSessionTemplate 重写getSqlSessionFactory、getConfiguration和SqlSessionInterceptor这个拦截器。其中最为关键还是继承SqlSessionTemplate 并重写里面的方法。
而Spring整合MyBatis也有两种方式,一种是配置MapperFactoryBean,另一种则是利用MapperScannerConfigurer进行扫描接口或包完成对象的自动创建。相对来说后者更方便些。MapperFactoryBean继承了SqlSessionDaoSupport也就是动态切换SqlSessionFactory的第一种方法,我们需要重写和实现SqlSessionDaoSupport方法,或者是继承MapperFactoryBean来重写覆盖相关方法。如果利用MapperScannerConfigurer的配置整合来切换SqlSessionFactory,那么我们就需要继承SqlSessionTemplate,重写上面提到的方法。在整合的配置中很多地方都是可以注入SqlSessionTemplate代替SqlSessionFactory的注入的。因为SqlSessionTemplate的创建也是需要注入SqlSessionFactory的。
二、实现代码
1、继承SqlSessionTemplate 重写getSqlSessionFactory、getConfiguration和SqlSessionInterceptor
package com.hoo.framework.mybatis.support; import static java.lang.reflect.Proxy.newProxyInstance; import static org.apache.ibatis.reflection.ExceptionUtil.unwrapThrowable; import static org.mybatis.spring.SqlSessionUtils.closeSqlSession; import static org.mybatis.spring.SqlSessionUtils.getSqlSession; import static org.mybatis.spring.SqlSessionUtils.isSqlSessionTransactional; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.sql.Connection; import java.util.List; import java.util.Map; import org.apache.ibatis.exceptions.PersistenceException; import org.apache.ibatis.executor.BatchResult; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.session.ExecutorType; import org.apache.ibatis.session.ResultHandler; import org.apache.ibatis.session.RowBounds; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.MyBatisExceptionTranslator; import org.mybatis.spring.SqlSessionTemplate; import org.springframework.dao.support.PersistenceExceptionTranslator; import org.springframework.util.Assert; /** * <b>function:</b> 继承SqlSessionTemplate 重写相关方法 * @author hoojo * @createDate 2013-10-18 下午03:07:46 * @file CustomSqlSessionTemplate.java * @package com.hoo.framework.mybatis.support * @project SHMB * @blog http://blog.csdn.net/IBM_hoojo * @email hoojo_@126.com * @version 1.0 */ public class CustomSqlSessionTemplate extends SqlSessionTemplate { private final SqlSessionFactory sqlSessionFactory; private final ExecutorType executorType; private final SqlSession sqlSessionProxy; private final PersistenceExceptionTranslator exceptionTranslator; private Map<Object, SqlSessionFactory> targetSqlSessionFactorys; private SqlSessionFactory defaultTargetSqlSessionFactory; public void setTargetSqlSessionFactorys(Map<Object, SqlSessionFactory> targetSqlSessionFactorys) { this.targetSqlSessionFactorys = targetSqlSessionFactorys; } public void setDefaultTargetSqlSessionFactory(SqlSessionFactory defaultTargetSqlSessionFactory) { this.defaultTargetSqlSessionFactory = defaultTargetSqlSessionFactory; } public CustomSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) { this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType()); } public CustomSqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) { this(sqlSessionFactory, executorType, new MyBatisExceptionTranslator(sqlSessionFactory.getConfiguration() .getEnvironment().getDataSource(), true)); } public CustomSqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) { super(sqlSessionFactory, executorType, exceptionTranslator); this.sqlSessionFactory = sqlSessionFactory; this.executorType = executorType; this.exceptionTranslator = exceptionTranslator; this.sqlSessionProxy = (SqlSession) newProxyInstance( SqlSessionFactory.class.getClassLoader(), new Class[] { SqlSession.class }, new SqlSessionInterceptor()); this.defaultTargetSqlSessionFactory = sqlSessionFactory; } @Override public SqlSessionFactory getSqlSessionFactory() { SqlSessionFactory targetSqlSessionFactory = targetSqlSessionFactorys.get(CustomerContextHolder.getContextType()); if (targetSqlSessionFactory != null) { return targetSqlSessionFactory; } else if (defaultTargetSqlSessionFactory != null) { return defaultTargetSqlSessionFactory; } else { Assert.notNull(targetSqlSessionFactorys, "Property 'targetSqlSessionFactorys' or 'defaultTargetSqlSessionFactory' are required"); Assert.notNull(defaultTargetSqlSessionFactory, "Property 'defaultTargetSqlSessionFactory' or 'targetSqlSessionFactorys' are required"); } return this.sqlSessionFactory; } @Override public Configuration getConfiguration() { return this.getSqlSessionFactory().getConfiguration(); } public ExecutorType getExecutorType() { return this.executorType; } public PersistenceExceptionTranslator getPersistenceExceptionTranslator() { return this.exceptionTranslator; } /** * {@inheritDoc} */ public <T> T selectOne(String statement) { return this.sqlSessionProxy.<T> selectOne(statement); } /** * {@inheritDoc} */ public <T> T selectOne(String statement, Object parameter) { return this.sqlSessionProxy.<T> selectOne(statement, parameter); } /** * {@inheritDoc} */ public <K, V> Map<K, V> selectMap(String statement, String mapKey) { return this.sqlSessionProxy.<K, V> selectMap(statement, mapKey); } /** * {@inheritDoc} */ public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey) { return this.sqlSessionProxy.<K, V> selectMap(statement, parameter, mapKey); } /** * {@inheritDoc} */ public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) { return this.sqlSessionProxy.<K, V> selectMap(statement, parameter, mapKey, rowBounds); } /** * {@inheritDoc} */ public <E> List<E> selectList(String statement) { return this.sqlSessionProxy.<E> selectList(statement); } /** * {@inheritDoc} */ public <E> List<E> selectList(String statement, Object parameter) { return this.sqlSessionProxy.<E> selectList(statement, parameter); } /** * {@inheritDoc} */ public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) { return this.sqlSessionProxy.<E> selectList(statement, parameter, rowBounds); } /** * {@inheritDoc} */ public void select(String statement, ResultHandler handler) { this.sqlSessionProxy.select(statement, handler); } /** * {@inheritDoc} */ public void select(String statement, Object parameter, ResultHandler handler) { this.sqlSessionProxy.select(statement, parameter, handler); } /** * {@inheritDoc} */ public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) { this.sqlSessionProxy.select(statement, parameter, rowBounds, handler); } /** * {@inheritDoc} */ public int insert(String statement) { return this.sqlSessionProxy.insert(statement); } /** * {@inheritDoc} */ public int insert(String statement, Object parameter) { return this.sqlSessionProxy.insert(statement, parameter); } /** * {@inheritDoc} */ public int update(String statement) { return this.sqlSessionProxy.update(statement); } /** * {@inheritDoc} */ public int update(String statement, Object parameter) { return this.sqlSessionProxy.update(statement, parameter); } /** * {@inheritDoc} */ public int delete(String statement) { return this.sqlSessionProxy.delete(statement); } /** * {@inheritDoc} */ public int delete(String statement, Object parameter) { return this.sqlSessionProxy.delete(statement, parameter); } /** * {@inheritDoc} */ public <T> T getMapper(Class<T> type) { return getConfiguration().getMapper(type, this); } /** * {@inheritDoc} */ public void commit() { throw new UnsupportedOperationException("Manual commit is not allowed over a Spring managed SqlSession"); } /** * {@inheritDoc} */ public void commit(boolean force) { throw new UnsupportedOperationException("Manual commit is not allowed over a Spring managed SqlSession"); } /** * {@inheritDoc} */ public void rollback() { throw new UnsupportedOperationException("Manual rollback is not allowed over a Spring managed SqlSession"); } /** * {@inheritDoc} */ public void rollback(boolean force) { throw new UnsupportedOperationException("Manual rollback is not allowed over a Spring managed SqlSession"); } /** * {@inheritDoc} */ public void close() { throw new UnsupportedOperationException("Manual close is not allowed over a Spring managed SqlSession"); } /** * {@inheritDoc} */ public void clearCache() { this.sqlSessionProxy.clearCache(); } /** * {@inheritDoc} */ public Connection getConnection() { return this.sqlSessionProxy.getConnection(); } /** * {@inheritDoc} * @since 1.0.2 */ public List<BatchResult> flushStatements() { return this.sqlSessionProxy.flushStatements(); } /** * Proxy needed to route MyBatis method calls to the proper SqlSession got from Spring's Transaction Manager It also * unwraps exceptions thrown by {@code Method#invoke(Object, Object...)} to pass a {@code PersistenceException} to * the {@code PersistenceExceptionTranslator}. */ private class SqlSessionInterceptor implements InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { final SqlSession sqlSession = getSqlSession( CustomSqlSessionTemplate.this.getSqlSessionFactory(), CustomSqlSessionTemplate.this.executorType, CustomSqlSessionTemplate.this.exceptionTranslator); try { Object result = method.invoke(sqlSession, args); if (!isSqlSessionTransactional(sqlSession, CustomSqlSessionTemplate.this.getSqlSessionFactory())) { // force commit even on non-dirty sessions because some databases require // a commit/rollback before calling close() sqlSession.commit(true); } return result; } catch (Throwable t) { Throwable unwrapped = unwrapThrowable(t); if (CustomSqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) { Throwable translated = CustomSqlSessionTemplate.this.exceptionTranslator .translateExceptionIfPossible((PersistenceException) unwrapped); if (translated != null) { unwrapped = translated; } } throw unwrapped; } finally { closeSqlSession(sqlSession, CustomSqlSessionTemplate.this.getSqlSessionFactory()); } } } }
重写后的getSqlSessionFactory方法会从我们配置的SqlSessionFactory集合targetSqlSessionFactorys或默认的defaultTargetSqlSessionFactory中获取Session对象。而改写的SqlSessionInterceptor 是这个MyBatis整合Spring的关键,所有的SqlSessionFactory对象的session都将在这里完成创建、提交、关闭等操作。所以我们改写这里的代码,在这里获取getSqlSessionFactory的时候,从多个SqlSessionFactory中获取我们设置的那个即可。
上面添加了targetSqlSessionFactorys、defaultTargetSqlSessionFactory两个属性来配置多个SqlSessionFactory对象和默认的SqlSessionFactory对象。
CustomerContextHolder 设置SqlSessionFactory的类型
package com.hoo.framework.mybatis.support; /** * <b>function:</b> 多数据源 * @author hoojo * @createDate 2013-9-27 上午11:36:57 * @file CustomerContextHolder.java * @package com.hoo.framework.spring.support * @project SHMB * @blog http://blog.csdn.net/IBM_hoojo * @email hoojo_@126.com * @version 1.0 */ public abstract class CustomerContextHolder { public final static String SESSION_FACTORY_MYSQL = "mysql"; public final static String SESSION_FACTORY_ORACLE = "oracle"; private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>(); public static void setContextType(String contextType) { contextHolder.set(contextType); } public static String getContextType() { return contextHolder.get(); } public static void clearContextType() { contextHolder.remove(); } }
2、配置相关的文件applicationContext-session-factory.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd "> <!-- 配置c3p0数据源 --> <bean id="dataSourceOracle" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> <property name="driverClass" value="${datasource.driver}"/> <property name="jdbcUrl" value="${datasource.url}"/> <property name="user" value="${datasource.username}"/> <property name="password" value="${datasource.password}"/> <property name="acquireIncrement" value="${c3p0.acquireIncrement}"/> <property name="initialPoolSize" value="${c3p0.initialPoolSize}"/> <property name="minPoolSize" value="${c3p0.minPoolSize}"/> <property name="maxPoolSize" value="${c3p0.maxPoolSize}"/> <property name="maxIdleTime" value="${c3p0.maxIdleTime}"/> <property name="idleConnectionTestPeriod" value="${c3p0.idleConnectionTestPeriod}"/> <property name="maxStatements" value="${c3p0.maxStatements}"/> <property name="numHelperThreads" value="${c3p0.numHelperThreads}"/> <property name="preferredTestQuery" value="${c3p0.preferredTestQuery}"/> <property name="testConnectionOnCheckout" value="${c3p0.testConnectionOnCheckout}"/> </bean> <bean id="dataSourceMySQL" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> <property name="driverClass" value="com.mysql.jdbc.Driver"/> <property name="jdbcUrl" value="jdbc:mysql://172.31.108.178:3306/world?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull"/> <property name="user" value="root"/> <property name="password" value="jp2011"/> <property name="acquireIncrement" value="${c3p0.acquireIncrement}"/> <property name="initialPoolSize" value="${c3p0.initialPoolSize}"/> <property name="minPoolSize" value="${c3p0.minPoolSize}"/> <property name="maxPoolSize" value="${c3p0.maxPoolSize}"/> <property name="maxIdleTime" value="${c3p0.maxIdleTime}"/> <property name="idleConnectionTestPeriod" value="${c3p0.idleConnectionTestPeriod}"/> <property name="maxStatements" value="${c3p0.maxStatements}"/> <property name="numHelperThreads" value="${c3p0.numHelperThreads}"/> <property name="preferredTestQuery" value="${c3p0.preferredTestQuery}"/> <property name="testConnectionOnCheckout" value="${c3p0.testConnectionOnCheckout}"/> </bean> <!-- 配置SqlSessionFactoryBean --> <bean id="oracleSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSourceOracle"/> <property name="configLocation" value="classpath:mybatis.xml"/> <!-- mapper和resultmap配置路径 --> <property name="mapperLocations"> <list> <!-- 表示在com.hoo目录下的任意包下的resultmap包目录中,以-resultmap.xml或-mapper.xml结尾所有文件 --> <value>classpath:com/hoo/framework/mybatis/mybatis-common.xml</value> <value>classpath:com/hoo/**/resultmap/*-resultmap.xml</value> <value>classpath:com/hoo/**/mapper/*-mapper.xml</value> <value>classpath:com/hoo/**/mapper/**/*-mapper.xml</value> </list> </property> </bean> <!-- 配置SqlSessionFactoryBean --> <bean id="mysqlSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSourceMySQL"/> <property name="configLocation" value="classpath:mybatis.xml"/> <!-- mapper和resultmap配置路径 --> <property name="mapperLocations"> <list> <!-- 表示在com.hoo目录下的任意包下的resultmap包目录中,以-resultmap.xml或-mapper.xml结尾所有文件 (oracle和mysql扫描的配置和路径不一样,如果是公共的都扫描 这里要区分下,不然就报错 找不到对应的表、视图)--> <value>classpath:com/hoo/framework/mybatis/mybatis-common.xml</value> <value>classpath:com/hoo/**/resultmap/*-mysql-resultmap.xml</value> <value>classpath:com/hoo/**/mapper/*-mysql-mapper.xml</value> <value>classpath:com/hoo/**/mapper/**/*-mysql-mapper.xml</value> <value>classpath:com/hoo/**/mapper/**/multiple-datasource-mapper.xml</value> </list> </property> </bean> <!-- 配置自定义的SqlSessionTemplate模板,注入相关配置 --> <bean id="sqlSessionTemplate" class="com.hoo.framework.mybatis.support.CustomSqlSessionTemplate"> <constructor-arg ref="oracleSqlSessionFactory" /> <property name="targetSqlSessionFactorys"> <map> <entry value-ref="oracleSqlSessionFactory" key="oracle"/> <entry value-ref="mysqlSqlSessionFactory" key="mysql"/> </map> </property> </bean> <!-- 通过扫描的模式,扫描目录在com/hoo/任意目录下的mapper目录下,所有的mapper都需要继承SqlMapper接口的接口 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.hoo.**.mapper"/> <!-- 注意注入sqlSessionTemplate --> <property name="sqlSessionTemplateBeanName" value="sqlSessionTemplate"/> <property name="markerInterface" value="com.hoo.framework.mybatis.SqlMapper"/> </bean> </beans>
上面的配置关键是在MapperScannerConfigurer中注入sqlSessionTemplate,这个要注意。当我们配置了多个SqlSessionFactoryBean的时候,就需要为MapperScannerConfigurer指定一个sqlSessionFactoryBeanName或是sqlSessionTemplateBeanName。一般情况下注入了sqlSessionTemplateBeanName对象,那sqlSessionFactory也就有值了。如果单独的注入了sqlSessionFactory那么程序会创建一个sqlSessionTemplate对象。我们可以看看代码SqlSessionFactoryDaoSupport对象的代码。如果你不喜欢使用扫描的方式,也可以注入sqlSessionTemplate或继承sqlSessionTemplate完成数据库操作。
public abstract class SqlSessionDaoSupport extends DaoSupport { private SqlSession sqlSession; private boolean externalSqlSession; public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) { if (!this.externalSqlSession) { this.sqlSession = new SqlSessionTemplate(sqlSessionFactory); } } public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) { this.sqlSession = sqlSessionTemplate; this.externalSqlSession = true; } ......
这段代码很明显,如果注入了sqlSessionTemplate上面的注入也就不会执行了。如果没有注入sqlSessionTemplate,那么会自动new一个sqlSessionTemplate对象。
3、编写相关测试接口和实现的mapper.xml
package com.hoo.server.datasource.mapper; import java.util.List; import java.util.Map; import com.hoo.framework.mybatis.SqlMapper; /** * <b>function:</b> MyBatis 多数据源 测试查询接口 * @author hoojo * @createDate 2013-10-10 下午04:18:08 * @file MultipleDataSourceMapper.java * @package com.hoo.server.datasource.mapper * @project SHMB * @blog http://blog.csdn.net/IBM_hoojo * @email hoojo_@126.com * @version 1.0 */ public interface MultipleDataSourceMapper extends SqlMapper { public List<Map<String, Object>> execute4MySQL() throws Exception; public List<Map<String, Object>> execute4Oracle() throws Exception; }
multiple-datasource-mapper.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.hoo.server.datasource.mapper.MultipleDataSourceMapper"> <select id="execute4Oracle" resultType="map"> <![CDATA[ SELECT * FROM deviceInfo_tab t where rownum < 10 ]]> </select> <select id="execute4MySQL" resultType="map"> <![CDATA[ SELECT * FROM city limit 2 ]]> </select> </mapper>
上面分别查询oracle和mysql两个数据库中的table
4、测试代码
@Autowired @Qualifier("multipleDataSourceMapper") private MultipleDataSourceMapper mapper; @Test public void testMapper() { CustomerContextHolder.setContextType(CustomerContextHolder.SESSION_FACTORY_MYSQL); try { trace(mapper.execute4MySQL()); } catch (Exception e1) { e1.printStackTrace(); } CustomerContextHolder.setContextType(CustomerContextHolder.SESSION_FACTORY_ORACLE); try { trace(mapper.execute4Oracle()); } catch (Exception e) { e.printStackTrace(); } }
运行后发现能够顺利查询出数据。
如果你是重写SqlSessionDaoSupport,那么方法如下
package com.hoo.framework.mybatis.support; import java.util.Map; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionUtils; import org.mybatis.spring.support.SqlSessionDaoSupport; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; /** * <b>function:</b> MyBatis 动态SqlSessionFactory * @author hoojo * @createDate 2013-10-14 下午02:32:19 * @file DynamicSqlSessionDaoSupport.java * @package com.hoo.framework.mybatis.support * @project SHMB * @blog http://blog.csdn.net/IBM_hoojo * @email hoojo_@126.com * @version 1.0 */ public class DynamicSqlSessionDaoSupport extends SqlSessionDaoSupport implements ApplicationContextAware { private ApplicationContext applicationContext; private Map<Object, SqlSessionFactory> targetSqlSessionFactorys; private SqlSessionFactory defaultTargetSqlSessionFactory; private SqlSession sqlSession; @Override public final SqlSession getSqlSession() { SqlSessionFactory targetSqlSessionFactory = targetSqlSessionFactorys.get(CustomerContextHolder.getContextType()); if (targetSqlSessionFactory != null) { setSqlSessionFactory(targetSqlSessionFactory); } else if (defaultTargetSqlSessionFactory != null) { setSqlSessionFactory(defaultTargetSqlSessionFactory); targetSqlSessionFactory = defaultTargetSqlSessionFactory; } else { targetSqlSessionFactory = (SqlSessionFactory) applicationContext.getBean(CustomerContextHolder.getContextType()); setSqlSessionFactory(targetSqlSessionFactory); } this.sqlSession = SqlSessionUtils.getSqlSession(targetSqlSessionFactory); return this.sqlSession; } @Override protected void checkDaoConfig() { //Assert.notNull(getSqlSession(), "Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required"); } public void setTargetSqlSessionFactorys(Map<Object, SqlSessionFactory> targetSqlSessionFactorys) { this.targetSqlSessionFactorys = targetSqlSessionFactorys; } public void setDefaultTargetSqlSessionFactory(SqlSessionFactory defaultTargetSqlSessionFactory) { this.defaultTargetSqlSessionFactory = defaultTargetSqlSessionFactory; } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } }
主要重写getSqlSession方法,上面获取SqlSessionFactory的方法。
重写好了后就可以配置这个对象,配置代码如下
//每一个DAO由继承SqlSessionDaoSupport全部改为DynamicSqlSessionDaoSupport public class UserMapperDaoImpl extends DynamicSqlSessionDaoSupport implements UserDao { public int addUser(User user) { return this.getSqlSession().insert("com.hoo.user.dao.UserDao.addUser", user); } }
在上面的配置文件中加入配置
<bean id="baseDao" class="com.hoo.framework.mybatis.support.DynamicSqlSessionDaoSupport" abstract="true" lazy-init="true"> <property name="targetSqlSessionFactorys"> <map> <entry value-ref="oracleSqlSessionFactory" key="oracle"/> <entry value-ref="mysqlSqlSessionFactory" key="mysql"/> </map> </property> <property name="defaultTargetSqlSessionFactory" ref="oracleSqlSessionFactory"/> </bean> <bean id="userMapperDao" class="com.hoo.user.dao.impl.UserMapperDaoImpl" parent="baseDao"/>
就这样也可以利用DynamicSqlSessionDaoSupport来完成动态切换sqlSessionFactory对象,只需用在注入userMapperDao调用方法的时候设置下CustomerContextHolder的contextType即可。
三、总结
为了实现这个功能看了mybatis-spring-1.2.0.jar这个包的部分源代码,代码内容不是很多。所以看了下主要的代码,下面做些简单的介绍。
MapperScannerConfigurer这个类就是我们要扫描的Mapper接口的类,也就是basePackage中继承markerInterface配置的接口。可以看看ClassPathBeanDefinitionScanner、ClassPathMapperScanner中的doScan这个方法。它会扫描basePackage这个包下所有接口,在ClassPathScanningCandidateComponentProvider中有这个方法findCandidateComponents,它会找到所有的BeanDefinition。
最重要的一点是ClassPathMapperScanner中的doScan这个方法它会给这些接口创建一个MapperFactoryBean。并且会检查sqlSessionFactory和sqlSessionTemplate对象的注入情况。
image
所以我们配置扫描的方式也就相当于我们在配置文件中给每一个Mapper配置一个MapperFactoryBean一样。而这个MapperFactoryBean又继承SqlSessionDaoSupport。所以当初我想重写MapperScannerConfigurer中的postProcessBeanDefinitionRegistry方法,然后重写方法中的ClassPathMapperScanner中的doScan方法,将definition.setBeanClass(MapperFactoryBean.class);改成自己定义的MapperFactoryBean。最后以失败告终,因为这里是Spring装载扫描对象的时候都已经为这些对象创建好了代理、设置好了mapperInterface和注入需要的类。所以在调用相关操作数据库的API方法的时候,设置对应的SqlSessionFactory也是无效的。
辗转反侧我看到了SqlSessionTemplate这个类,它的功能相当于SqlSessionDaoSupport的实现类MapperFactoryBean。最为关键的是SqlSessionTemplate有一个拦截器SqlSessionInterceptor,它复制所有SqlSession的创建、提交、关闭,而且是在每个方法之前。这点在上面也提到过了!所以我们只需要在SqlSessionInterceptor方法中获取SqlSessionFactory的时候,在这之前调用下CustomerContextHolder.setContextType方法即可完成数据库的SqlSessionFactory的切换。而在MapperScannerConfigurer提供了注入SqlSessionFactory和sqlSessionTemplate的方法,如果注入了SqlSessionFactory系统将会new一个sqlSessionTemplate,而注入了sqlSessionTemplate就不会创建其他对象(见下面代码)。所以我们配置一个sqlSessionTemplate并注入到MapperScannerConfigurer中,程序将会使用这个sqlSessionTemplate。本文最后的实现方式就是这样完成的。
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) { if (!this.externalSqlSession) { this.sqlSession = new SqlSessionTemplate(sqlSessionFactory); } } public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) { this.sqlSession = sqlSessionTemplate; this.externalSqlSession = true; }
发表评论
-
Spring Boot 属性配置
2016-06-24 11:04 1151Spring Boot 属性配置和使用 http://blog ... -
Spring Boot 集成MyBatis
2016-06-24 10:55 1994Spring Boot 集成MyBatis http://bl ... -
Spring MVC防重复提交
2016-06-17 15:47 1593http://my.oschina.net/zyqjustin ... -
Spring容器加载完之后执行特定任务
2016-06-17 15:36 2245http://my.oschina.net/simpleton ... -
使用spring-session和shiro来代理session的配置
2016-06-16 11:21 12000使用spring-session和redis来代理sessio ... -
JSTL 的 if else : 有 c:if 没有 else 的处理
2016-06-14 09:52 1296http://blog.csdn.net/xiyuan1999 ... -
spring mvc 请求转发和重定向
2016-06-14 09:48 1353http://blog.csdn.net/jackpk/art ... -
mvc:view-controller
2016-05-18 10:26 1051http://blog.csdn.net/lzwglory/a ... -
spring配置事物的方式:注解和aop配置
2016-05-14 00:26 4066参考: Spring AOP中pointcut express ... -
分布式任务调度组件 Uncode-Schedule
2016-05-13 14:47 2246http://www.oschina.net/p/uncode ... -
写个mybatis的拦截插件,实现将所有执行的sql写入文件里
2016-05-12 15:59 5065原文 http://3131854.blog.51cto.co ... -
Mybatis分库分表扩展插件
2016-05-12 15:47 1589http://fangjialong.iteye.com/bl ... -
spring+mybatis+atomikos 实现JTA事务
2016-05-11 22:00 5476sping配置多个数据源 不同用户操作不同数据库 http:/ ... -
Spring中使用注解 @Scheduled执行定时任务
2016-05-10 09:39 1537原文:http://dwf07223.blog.51cto.c ... -
Spring中配置Websocket
2016-05-05 16:55 1249spring+websocket整合(springMVC+sp ... -
redis 集群中Session解决方案之Spring Session
2016-05-04 08:54 1277集群中Session解决方案之Spring Session h ... -
使用Spring-data进行Redis操作
2016-05-04 08:54 4737使用Spring-data进行Redis操作 http://z ... -
Spring4新特性——集成Bean Validation 1.1(JSR-349)到SpringMVC
2016-05-03 13:35 1032Spring4新特性——集成Bean Validation 1 ... -
SpringMVC介绍之Validation
2016-05-03 13:10 955SpringMVC介绍之Validation http://h ... -
spring 注解方式下使用commons-validator 验证表单
2016-05-03 11:08 3041原文: http://www.programgo.com/ar ...
相关推荐
主要介绍了Spring3 整合MyBatis3 配置多数据源动态选择SqlSessionFactory详细教程,需要的朋友可以参考下
mybatis实战教程mybatis in action之五与spring3集成附源码 mybatis实战教程mybatis in action之六与Spring MVC 的集成 mybatis实战教程mybatis in action之七实现mybatis分页源码下载 mybatis实战教程mybatis in ...
配置多数据源时,要让spring-boot不自动注入data-source和sqlSessionFactory。 。常问问题1,出现了如下BUG org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.reflection....
在mybatis中删除数据源相关的配置。 5.采用包扫描的方式创建mapper对象 第三步:单独测试springMVC 1.引入springMVC的依赖 spring-webmvc 2.在web.xml中配置前端控制器DispatcherServlet 配置乱码过滤器 3....
引入属性文件,注解扫描包,druid(德鲁伊)配置数据源 (c3p0 dbcp druid),监控数据库,配置sqlSessionFactory 将spring与mybatis结合,装配dao或mapper接口,拦截器方式配置事物——事务通知
1、确保生产了逆向代码2、配置spring数据源1、引入实体类和Examply <bean id="sqlSessionFactory" class="org.
①JDK8 ②Java直接整合MyBatis在Java中直接使用MyBatis框架需要读取配置,手动构造SqlSessionFactory / SqlSession ③Spring整合MyBatis(传统写法)事务 ss_mybatis ①JDK6 ②Spring/ SpringMVC整合原生MyBatis...
Spring 4.0.2+Spring MVC 4.0.2+MyBatis ...3、添加spring配置文件application-root.xml(根文件)、spring-mybatis.xml 4、添加数据源 5、配置mybatis sqlSessionFactory 6、将mybatis自动生成的接口交给spring管理
SSM(Spring+SpringMVC+MyBatis)框架集由Spring、MyBatis两个开源框架整合而成(SpringMVC是Spring中的部分内容)。常作为数据源较简单的web项目的框架。 Spring Spring就像是整个项目中装配bean的大工厂,在...
SSM(Spring+SpringMVC+MyBatis)框架集由Spring、MyBatis两个开源框架整合而成(SpringMVC是Spring中的部分内容),常作为数据源较简单的web项目的框架。Spring就像是整个项目中装配bean的大工厂,SpringMVC在项目...
SSM(Spring+SpringMVC+MyBatis)框架集由Spring、MyBatis两个开源框架整合而成(SpringMVC是Spring中的部分内容)。常作为数据源较简单的web项目的框架。 Spring Spring就像是整个项目中装配bean的大工厂,在配置...
SSM(Spring+SpringMVC+MyBatis)框架集由Spring、MyBatis两个开源框架整合而成(SpringMVC是Spring中的部分内容)。常作为数据源较简单的web项目的框架。 Spring Spring就像是整个项目中装配bean的大工厂,在...
SpringBoot MyBatis入门参考: 请在/ resources文件夹中分配一个sql以自动运行,Mapper将在此处,而无需@Autowire 我们可以使用xml / annotation进行配置... MyBatis-Spring-Boot-Starter将: 自动检测现有的数据源。
项目简介 2018年6月5日 后台管理系统工程结构: taotao-parent -- 管理依赖jar包的版本,全局,公司级别 ...配置数据源。 需要让Spring容器管理SqlSessionFactory,单例存在。 把mapper的代理对象放到spring容器中
-- spring和MyBatis完美整合,不需要mybatis的配置映射文件 --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- 添加 mybatis-config配置上去。--> ...