1、摘要html
上兩篇文章分別介紹了Spring3.3 整合 Hibernate三、MyBatis3.2 配置多數據源/動態切換數據源 方法 和 Spring3 整合Hibernate3.5 動態切換SessionFactory (切換數據庫方言),這篇文章將介紹Spring整合Mybatis 如何完成SqlSessionFactory的動態切換的。而且會簡單的介紹下MyBatis整合Spring中的官方的相關代碼。java
Spring整合MyBatis切換SqlSessionFactory有兩種方法,第1、 繼承SqlSessionDaoSupport,重寫獲取SqlSessionFactory的方法。第2、繼承SqlSessionTemplate 重寫getSqlSessionFactory、getConfiguration和SqlSessionInterceptor這個攔截器。其中最爲關鍵仍是繼承SqlSessionTemplate 並重寫裏面的方法。mysql
而Spring整合MyBatis也有兩種方式,一種是配置MapperFactoryBean,另外一種則是利用MapperScannerConfigurer進行掃描接口或包完成對象的自動建立。相對來講後者更方便些。MapperFactoryBean繼承了SqlSessionDaoSupport也就是動態切換SqlSessionFactory的第一種方法,咱們須要重寫和實現SqlSessionDaoSupport方法,或者是繼承MapperFactoryBean來重寫覆蓋相關方法。若是利用MapperScannerConfigurer的配置整合來切換SqlSessionFactory,那麼咱們就須要繼承SqlSessionTemplate,重寫上面提到的方法。在整合的配置中不少地方都是能夠注入SqlSessionTemplate代替SqlSessionFactory的注入的。由於SqlSessionTemplate的建立也是須要注入SqlSessionFactory的。spring
2、實現代碼sql
一、繼承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中獲取咱們設置的那個便可。apache
上面添加了targetSqlSessionFactorys、defaultTargetSqlSessionFactory兩個屬性來配置多個SqlSessionFactory對象和默認的SqlSessionFactory對象。session
CustomerContextHolder 設置SqlSessionFactory的類型mybatis
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(); } }
二、配置相關的文件applicationContext-session-factory.xmloracle
<?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對象。
三、編寫相關測試接口和實現的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
四、測試代碼
@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便可。
3、總結
爲了實現這個功能看了mybatis-spring-1.2.0.jar這個包的部分源代碼,代碼內容不是不少。因此看了下主要的代碼,下面作些簡單的介紹。
MapperScannerConfigurer這個類就是咱們要掃描的Mapper接口的類,也就是basePackage中繼承markerInterface配置的接口。能夠看看ClassPathBeanDefinitionScanner、ClassPathMapperScanner中的doScan這個方法。它會掃描basePackage這個包下全部接口,在ClassPathScanningCandidateComponentProvider中有這個方法findCandidateComponents,它會找到全部的BeanDefinition。
最重要的一點是ClassPathMapperScanner中的doScan這個方法它會給這些接口建立一個MapperFactoryBean。而且會檢查sqlSessionFactory和sqlSessionTemplate對象的注入狀況。
因此咱們配置掃描的方式也就至關於咱們在配置文件中給每個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; }