您的位置:首页 > 编程语言 > Java开发

Spring3 整合MyBatis3 配置多数据源 动态选择SqlSessionFactory

2015-10-21 11:18 786 查看
一、摘要

上两篇文章分别介绍了Spring3.3整合Hibernate3、MyBatis3.2配置多数据源/动态切换数据源方法和Spring3整合Hibernate3.5动态切换SessionFactory(切换数据库方言),这篇文章将介绍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

packagecom.hoo.framework.mybatis.support;


importstaticjava.lang.reflect.Proxy.newProxyInstance;

importstaticorg.apache.ibatis.reflection.ExceptionUtil.unwrapThrowable;

importstaticorg.mybatis.spring.SqlSessionUtils.closeSqlSession;

importstaticorg.mybatis.spring.SqlSessionUtils.getSqlSession;

importstaticorg.mybatis.spring.SqlSessionUtils.isSqlSessionTransactional;


importjava.lang.reflect.InvocationHandler;

importjava.lang.reflect.Method;

importjava.sql.Connection;

importjava.util.List;

importjava.util.Map;


importorg.apache.ibatis.exceptions.PersistenceException;

importorg.apache.ibatis.executor.BatchResult;

importorg.apache.ibatis.session.Configuration;

importorg.apache.ibatis.session.ExecutorType;

importorg.apache.ibatis.session.ResultHandler;

importorg.apache.ibatis.session.RowBounds;

importorg.apache.ibatis.session.SqlSession;

importorg.apache.ibatis.session.SqlSessionFactory;

importorg.mybatis.spring.MyBatisExceptionTranslator;

importorg.mybatis.spring.SqlSessionTemplate;

importorg.springframework.dao.support.PersistenceExceptionTranslator;

importorg.springframework.util.Assert;


/**

*<b>function:</b>继承SqlSessionTemplate重写相关方法

*@authorhoojo

*@createDate2013-10-18下午03:07:46

*@fileCustomSqlSessionTemplate.java

*@packagecom.hoo.framework.mybatis.support

*@projectSHMB

*@blog'target='_blank'>http://blog.csdn.net/IBM_hoojo[/code]
*@emailhoojo_@126.com

*@version1.0

*/

publicclassCustomSqlSessionTemplateextendsSqlSessionTemplate{


privatefinalSqlSessionFactorysqlSessionFactory;

privatefinalExecutorTypeexecutorType;

privatefinalSqlSessionsqlSessionProxy;

privatefinalPersistenceExceptionTranslatorexceptionTranslator;


privateMap<Object,SqlSessionFactory>targetSqlSessionFactorys;

privateSqlSessionFactorydefaultTargetSqlSessionFactory;


publicvoidsetTargetSqlSessionFactorys(Map<Object,SqlSessionFactory>targetSqlSessionFactorys){

this.targetSqlSessionFactorys=targetSqlSessionFactorys;

}


publicvoidsetDefaultTargetSqlSessionFactory(SqlSessionFactorydefaultTargetSqlSessionFactory){

this.defaultTargetSqlSessionFactory=defaultTargetSqlSessionFactory;

}


publicCustomSqlSessionTemplate(SqlSessionFactorysqlSessionFactory){

this(sqlSessionFactory,sqlSessionFactory.getConfiguration().getDefaultExecutorType());

}


publicCustomSqlSessionTemplate(SqlSessionFactorysqlSessionFactory,ExecutorTypeexecutorType){

this(sqlSessionFactory,executorType,newMyBatisExceptionTranslator(sqlSessionFactory.getConfiguration()

.getEnvironment().getDataSource(),true));

}


publicCustomSqlSessionTemplate(SqlSessionFactorysqlSessionFactory,ExecutorTypeexecutorType,

PersistenceExceptionTranslatorexceptionTranslator){


super(sqlSessionFactory,executorType,exceptionTranslator);


this.sqlSessionFactory=sqlSessionFactory;

this.executorType=executorType;

this.exceptionTranslator=exceptionTranslator;


this.sqlSessionProxy=(SqlSession)newProxyInstance(

SqlSessionFactory.class.getClassLoader(),

newClass[]{SqlSession.class},

newSqlSessionInterceptor());


this.defaultTargetSqlSessionFactory=sqlSessionFactory;

}


@Override

publicSqlSessionFactorygetSqlSessionFactory(){


SqlSessionFactorytargetSqlSessionFactory=targetSqlSessionFactorys.get(CustomerContextHolder.getContextType());

if(targetSqlSessionFactory!=null){

returntargetSqlSessionFactory;

}elseif(defaultTargetSqlSessionFactory!=null){

returndefaultTargetSqlSessionFactory;

}else{

Assert.notNull(targetSqlSessionFactorys,"Property'targetSqlSessionFactorys'or'defaultTargetSqlSessionFactory'arerequired");

Assert.notNull(defaultTargetSqlSessionFactory,"Property'defaultTargetSqlSessionFactory'or'targetSqlSessionFactorys'arerequired");

}

returnthis.sqlSessionFactory;

}


@Override

publicConfigurationgetConfiguration(){

returnthis.getSqlSessionFactory().getConfiguration();

}


publicExecutorTypegetExecutorType(){

returnthis.executorType;

}


publicPersistenceExceptionTranslatorgetPersistenceExceptionTranslator(){

returnthis.exceptionTranslator;

}


/**

*{@inheritDoc}

*/

public<T>TselectOne(Stringstatement){

returnthis.sqlSessionProxy.<T>selectOne(statement);

}


/**

*{@inheritDoc}

*/

public<T>TselectOne(Stringstatement,Objectparameter){

returnthis.sqlSessionProxy.<T>selectOne(statement,parameter);

}


/**

*{@inheritDoc}

*/

public<K,V>Map<K,V>selectMap(Stringstatement,StringmapKey){

returnthis.sqlSessionProxy.<K,V>selectMap(statement,mapKey);

}


/**

*{@inheritDoc}

*/

public<K,V>Map<K,V>selectMap(Stringstatement,Objectparameter,StringmapKey){

returnthis.sqlSessionProxy.<K,V>selectMap(statement,parameter,mapKey);

}


/**

*{@inheritDoc}

*/

public<K,V>Map<K,V>selectMap(Stringstatement,Objectparameter,StringmapKey,RowBoundsrowBounds){

returnthis.sqlSessionProxy.<K,V>selectMap(statement,parameter,mapKey,rowBounds);

}


/**

*{@inheritDoc}

*/

public<E>List<E>selectList(Stringstatement){

returnthis.sqlSessionProxy.<E>selectList(statement);

}


/**

*{@inheritDoc}

*/

public<E>List<E>selectList(Stringstatement,Objectparameter){

returnthis.sqlSessionProxy.<E>selectList(statement,parameter);

}


/**

*{@inheritDoc}

*/

public<E>List<E>selectList(Stringstatement,Objectparameter,RowBoundsrowBounds){

returnthis.sqlSessionProxy.<E>selectList(statement,parameter,rowBounds);

}


/**

*{@inheritDoc}

*/

publicvoidselect(Stringstatement,ResultHandlerhandler){

this.sqlSessionProxy.select(statement,handler);

}


/**

*{@inheritDoc}

*/

publicvoidselect(Stringstatement,Objectparameter,ResultHandlerhandler){

this.sqlSessionProxy.select(statement,parameter,handler);

}


/**

*{@inheritDoc}

*/

publicvoidselect(Stringstatement,Objectparameter,RowBoundsrowBounds,ResultHandlerhandler){

this.sqlSessionProxy.select(statement,parameter,rowBounds,handler);

}


/**

*{@inheritDoc}

*/

publicintinsert(Stringstatement){

returnthis.sqlSessionProxy.insert(statement);

}


/**

*{@inheritDoc}

*/

publicintinsert(Stringstatement,Objectparameter){

returnthis.sqlSessionProxy.insert(statement,parameter);

}


/**

*{@inheritDoc}

*/

publicintupdate(Stringstatement){

returnthis.sqlSessionProxy.update(statement);

}


/**

*{@inheritDoc}

*/

publicintupdate(Stringstatement,Objectparameter){

returnthis.sqlSessionProxy.update(statement,parameter);

}


/**

*{@inheritDoc}

*/

publicintdelete(Stringstatement){

returnthis.sqlSessionProxy.delete(statement);

}


/**

*{@inheritDoc}

*/

publicintdelete(Stringstatement,Objectparameter){

returnthis.sqlSessionProxy.delete(statement,parameter);

}


/**

*{@inheritDoc}

*/

public<T>TgetMapper(Class<T>type){

returngetConfiguration().getMapper(type,this);

}


/**

*{@inheritDoc}

*/

publicvoidcommit(){

thrownewUnsupportedOperationException("ManualcommitisnotallowedoveraSpringmanagedSqlSession");

}


/**

*{@inheritDoc}

*/

publicvoidcommit(booleanforce){

thrownewUnsupportedOperationException("ManualcommitisnotallowedoveraSpringmanagedSqlSession");

}


/**

*{@inheritDoc}

*/

publicvoidrollback(){

thrownewUnsupportedOperationException("ManualrollbackisnotallowedoveraSpringmanagedSqlSession");

}


/**

*{@inheritDoc}

*/

publicvoidrollback(booleanforce){

thrownewUnsupportedOperationException("ManualrollbackisnotallowedoveraSpringmanagedSqlSession");

}


/**

*{@inheritDoc}

*/

publicvoidclose(){

thrownewUnsupportedOperationException("ManualcloseisnotallowedoveraSpringmanagedSqlSession");

}


/**

*{@inheritDoc}

*/

publicvoidclearCache(){

this.sqlSessionProxy.clearCache();

}


/**

*{@inheritDoc}

*/

publicConnectiongetConnection(){

returnthis.sqlSessionProxy.getConnection();

}


/**

*{@inheritDoc}

*@since1.0.2

*/

publicList<BatchResult>flushStatements(){

returnthis.sqlSessionProxy.flushStatements();

}


/**

*ProxyneededtorouteMyBatismethodcallstotheproperSqlSessiongotfromSpring'sTransactionManagerItalso

*unwrapsexceptionsthrownby{@codeMethod#invoke(Object,Object...)}topassa{@codePersistenceException}to

*the{@codePersistenceExceptionTranslator}.

*/

privateclassSqlSessionInterceptorimplementsInvocationHandler{

publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{

finalSqlSessionsqlSession=getSqlSession(

CustomSqlSessionTemplate.this.getSqlSessionFactory(),

CustomSqlSessionTemplate.this.executorType,

CustomSqlSessionTemplate.this.exceptionTranslator);

try{

Objectresult=method.invoke(sqlSession,args);

if(!isSqlSessionTransactional(sqlSession,CustomSqlSessionTemplate.this.getSqlSessionFactory())){

//forcecommitevenonnon-dirtysessionsbecausesomedatabasesrequire

//acommit/rollbackbeforecallingclose()

sqlSession.commit(true);

}

returnresult;

}catch(Throwablet){

Throwableunwrapped=unwrapThrowable(t);

if(CustomSqlSessionTemplate.this.exceptionTranslator!=null&&unwrappedinstanceofPersistenceException){

Throwabletranslated=CustomSqlSessionTemplate.this.exceptionTranslator

.translateExceptionIfPossible((PersistenceException)unwrapped);

if(translated!=null){

unwrapped=translated;

}

}

throwunwrapped;

}finally{

closeSqlSession(sqlSession,CustomSqlSessionTemplate.this.getSqlSessionFactory());

}

}

}


}


重写后的getSqlSessionFactory方法会从我们配置的SqlSessionFactory集合targetSqlSessionFactorys或默认的defaultTargetSqlSessionFactory中获取Session对象。而改写的SqlSessionInterceptor是这个MyBatis整合Spring的关键,所有的SqlSessionFactory对象的session都将在这里完成创建、提交、关闭等操作。所以我们改写这里的代码,在这里获取getSqlSessionFactory的时候,从多个SqlSessionFactory中获取我们设置的那个即可。

上面添加了targetSqlSessionFactorys、defaultTargetSqlSessionFactory两个属性来配置多个SqlSessionFactory对象和默认的SqlSessionFactory对象。

CustomerContextHolder设置SqlSessionFactory的类型

packagecom.hoo.framework.mybatis.support;


/**

*<b>function:</b>多数据源

*@authorhoojo

*@createDate2013-9-27上午11:36:57

*@fileCustomerContextHolder.java

*@packagecom.hoo.framework.spring.support

*@projectSHMB

*@blog'target='_blank'>http://blog.csdn.net/IBM_hoojo[/code]
*@emailhoojo_@126.com

*@version1.0

*/

publicabstractclassCustomerContextHolder{


publicfinalstaticStringSESSION_FACTORY_MYSQL="mysql";

publicfinalstaticStringSESSION_FACTORY_ORACLE="oracle";


privatestaticfinalThreadLocal<String>contextHolder=newThreadLocal<String>();


publicstaticvoidsetContextType(StringcontextType){

contextHolder.set(contextType);

}


publicstaticStringgetContextType(){

returncontextHolder.get();

}


publicstaticvoidclearContextType(){

contextHolder.remove();

}

}


2、配置相关的文件applicationContext-session-factory.xml

<?xmlversion="1.0"encoding="UTF-8"?>

<beansxmlns="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数据源-->

<beanid="dataSourceOracle"class="com.mchange.v2.c3p0.ComboPooledDataSource"destroy-method="close">

<propertyname="driverClass"value="${datasource.driver}"/>

<propertyname="jdbcUrl"value="${datasource.url}"/>

<propertyname="user"value="${datasource.username}"/>

<propertyname="password"value="${datasource.password}"/>


<propertyname="acquireIncrement"value="${c3p0.acquireIncrement}"/>

<propertyname="initialPoolSize"value="${c3p0.initialPoolSize}"/>

<propertyname="minPoolSize"value="${c3p0.minPoolSize}"/>

<propertyname="maxPoolSize"value="${c3p0.maxPoolSize}"/>

<propertyname="maxIdleTime"value="${c3p0.maxIdleTime}"/>

<propertyname="idleConnectionTestPeriod"value="${c3p0.idleConnectionTestPeriod}"/>

<propertyname="maxStatements"value="${c3p0.maxStatements}"/>

<propertyname="numHelperThreads"value="${c3p0.numHelperThreads}"/>

<propertyname="preferredTestQuery"value="${c3p0.preferredTestQuery}"/>

<propertyname="testConnectionOnCheckout"value="${c3p0.testConnectionOnCheckout}"/>

</bean>


<beanid="dataSourceMySQL"class="com.mchange.v2.c3p0.ComboPooledDataSource"destroy-method="close">

<propertyname="driverClass"value="com.mysql.jdbc.Driver"/>

<propertyname="jdbcUrl"value="jdbc:mysql://172.31.108.178:3306/world?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull"/>

<propertyname="user"value="root"/>

<propertyname="password"value="jp2011"/>


<propertyname="acquireIncrement"value="${c3p0.acquireIncrement}"/>

<propertyname="initialPoolSize"value="${c3p0.initialPoolSize}"/>

<propertyname="minPoolSize"value="${c3p0.minPoolSize}"/>

<propertyname="maxPoolSize"value="${c3p0.maxPoolSize}"/>

<propertyname="maxIdleTime"value="${c3p0.maxIdleTime}"/>

<propertyname="idleConnectionTestPeriod"value="${c3p0.idleConnectionTestPeriod}"/>

<propertyname="maxStatements"value="${c3p0.maxStatements}"/>

<propertyname="numHelperThreads"value="${c3p0.numHelperThreads}"/>

<propertyname="preferredTestQuery"value="${c3p0.preferredTestQuery}"/>

<propertyname="testConnectionOnCheckout"value="${c3p0.testConnectionOnCheckout}"/>

</bean>


<!--配置SqlSessionFactoryBean-->

<beanid="oracleSqlSessionFactory"class="org.mybatis.spring.SqlSessionFactoryBean">

<propertyname="dataSource"ref="dataSourceOracle"/>

<propertyname="configLocation"value="classpath:mybatis.xml"/>

<!--mapper和resultmap配置路径-->

<propertyname="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-->

<beanid="mysqlSqlSessionFactory"class="org.mybatis.spring.SqlSessionFactoryBean">

<propertyname="dataSource"ref="dataSourceMySQL"/>

<propertyname="configLocation"value="classpath:mybatis.xml"/>

<!--mapper和resultmap配置路径-->

<propertyname="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模板,注入相关配置-->

<beanid="sqlSessionTemplate"class="com.hoo.framework.mybatis.support.CustomSqlSessionTemplate">

<constructor-argref="oracleSqlSessionFactory"/>

<propertyname="targetSqlSessionFactorys">

<map>

<entryvalue-ref="oracleSqlSessionFactory"key="oracle"/>

<entryvalue-ref="mysqlSqlSessionFactory"key="mysql"/>

</map>

</property>

</bean>


<!--通过扫描的模式,扫描目录在com/hoo/任意目录下的mapper目录下,所有的mapper都需要继承SqlMapper接口的接口-->

<beanclass="org.mybatis.spring.mapper.MapperScannerConfigurer">

<propertyname="basePackage"value="com.hoo.**.mapper"/>

<!--注意注入sqlSessionTemplate-->

<propertyname="sqlSessionTemplateBeanName"value="sqlSessionTemplate"/>

<propertyname="markerInterface"value="com.hoo.framework.mybatis.SqlMapper"/>

</bean>


</beans>


上面的配置关键是在MapperScannerConfigurer中注入sqlSessionTemplate,这个要注意。当我们配置了多个SqlSessionFactoryBean的时候,就需要为MapperScannerConfigurer指定一个sqlSessionFactoryBeanName或是sqlSessionTemplateBeanName。一般情况下注入了sqlSessionTemplateBeanName对象,那sqlSessionFactory也就有值了。如果单独的注入了sqlSessionFactory那么程序会创建一个sqlSessionTemplate对象。我们可以看看代码SqlSessionFactoryDaoSupport对象的代码。如果你不喜欢使用扫描的方式,也可以注入sqlSessionTemplate或继承sqlSessionTemplate完成数据库操作。

publicabstractclassSqlSessionDaoSupportextendsDaoSupport{


privateSqlSessionsqlSession;


privatebooleanexternalSqlSession;


publicvoidsetSqlSessionFactory(SqlSessionFactorysqlSessionFactory){

if(!this.externalSqlSession){

this.sqlSession=newSqlSessionTemplate(sqlSessionFactory);

}

}


publicvoidsetSqlSessionTemplate(SqlSessionTemplatesqlSessionTemplate){

this.sqlSession=sqlSessionTemplate;

this.externalSqlSession=true;

}

......


这段代码很明显,如果注入了sqlSessionTemplate上面的注入也就不会执行了。如果没有注入sqlSessionTemplate,那么会自动new一个sqlSessionTemplate对象。

3、编写相关测试接口和实现的mapper.xml

packagecom.hoo.server.datasource.mapper;


importjava.util.List;

importjava.util.Map;

importcom.hoo.framework.mybatis.SqlMapper;


/**

*<b>function:</b>MyBatis多数据源测试查询接口

*@authorhoojo

*@createDate2013-10-10下午04:18:08

*@fileMultipleDataSourceMapper.java

*@packagecom.hoo.server.datasource.mapper

*@projectSHMB

*@blog'target='_blank'>http://blog.csdn.net/IBM_hoojo[/code]
*@emailhoojo_@126.com

*@version1.0

*/

publicinterfaceMultipleDataSourceMapperextendsSqlMapper{


publicList<Map<String,Object>>execute4MySQL()throwsException;


publicList<Map<String,Object>>execute4Oracle()throwsException;

}


multiple-datasource-mapper.xml

<?xmlversion="1.0"encoding="UTF-8"?>

<!DOCTYPEmapperPUBLIC"-//mybatis.org//DTDMapper3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mappernamespace="com.hoo.server.datasource.mapper.MultipleDataSourceMapper">


<selectid="execute4Oracle"resultType="map">

<![CDATA[

SELECT

*

FROM

deviceInfo_tabtwhererownum<10

]]>

</select>


<selectid="execute4MySQL"resultType="map">

<![CDATA[

SELECT

*

FROM

citylimit2

]]>

</select>

</mapper>


上面分别查询oracle和mysql两个数据库中的table

4、测试代码

@Autowired

@Qualifier("multipleDataSourceMapper")

privateMultipleDataSourceMappermapper;


@Test

publicvoidtestMapper(){

CustomerContextHolder.setContextType(CustomerContextHolder.SESSION_FACTORY_MYSQL);

try{

trace(mapper.execute4MySQL());

}catch(Exceptione1){

e1.printStackTrace();

}

CustomerContextHolder.setContextType(CustomerContextHolder.SESSION_FACTORY_ORACLE);

try{

trace(mapper.execute4Oracle());

}catch(Exceptione){

e.printStackTrace();

}

}


运行后发现能够顺利查询出数据。

如果你是重写SqlSessionDaoSupport,那么方法如下

packagecom.hoo.framework.mybatis.support;


importjava.util.Map;

importorg.apache.ibatis.session.SqlSession;

importorg.apache.ibatis.session.SqlSessionFactory;

importorg.mybatis.spring.SqlSessionUtils;

importorg.mybatis.spring.support.SqlSessionDaoSupport;

importorg.springframework.beans.BeansException;

importorg.springframework.context.ApplicationContext;

importorg.springframework.context.ApplicationContextAware;


/**

*<b>function:</b>MyBatis动态SqlSessionFactory

*@authorhoojo

*@createDate2013-10-14下午02:32:19

*@fileDynamicSqlSessionDaoSupport.java

*@packagecom.hoo.framework.mybatis.support

*@projectSHMB

*@blog'target='_blank'>http://blog.csdn.net/IBM_hoojo[/code]
*@emailhoojo_@126.com

*@version1.0

*/

publicclassDynamicSqlSessionDaoSupportextendsSqlSessionDaoSupportimplementsApplicationContextAware{


privateApplicationContextapplicationContext;


privateMap<Object,SqlSessionFactory>targetSqlSessionFactorys;

privateSqlSessionFactorydefaultTargetSqlSessionFactory;

privateSqlSessionsqlSession;


@Override

publicfinalSqlSessiongetSqlSession(){

SqlSessionFactorytargetSqlSessionFactory=targetSqlSessionFactorys.get(CustomerContextHolder.getContextType());

if(targetSqlSessionFactory!=null){

setSqlSessionFactory(targetSqlSessionFactory);

}elseif(defaultTargetSqlSessionFactory!=null){

setSqlSessionFactory(defaultTargetSqlSessionFactory);

targetSqlSessionFactory=defaultTargetSqlSessionFactory;

}else{

targetSqlSessionFactory=(SqlSessionFactory)applicationContext.getBean(CustomerContextHolder.getContextType());

setSqlSessionFactory(targetSqlSessionFactory);

}

this.sqlSession=SqlSessionUtils.getSqlSession(targetSqlSessionFactory);

returnthis.sqlSession;

}


@Override

protectedvoidcheckDaoConfig(){

//Assert.notNull(getSqlSession(),"Property'sqlSessionFactory'or'sqlSessionTemplate'arerequired");

}


publicvoidsetTargetSqlSessionFactorys(Map<Object,SqlSessionFactory>targetSqlSessionFactorys){

this.targetSqlSessionFactorys=targetSqlSessionFactorys;

}


publicvoidsetDefaultTargetSqlSessionFactory(SqlSessionFactorydefaultTargetSqlSessionFactory){

this.defaultTargetSqlSessionFactory=defaultTargetSqlSessionFactory;

}


@Override

publicvoidsetApplicationContext(ApplicationContextapplicationContext)throwsBeansException{

this.applicationContext=applicationContext;

}

}


主要重写getSqlSession方法,上面获取SqlSessionFactory的方法。

重写好了后就可以配置这个对象,配置代码如下

//每一个DAO由继承SqlSessionDaoSupport全部改为DynamicSqlSessionDaoSupport

publicclassUserMapperDaoImplextendsDynamicSqlSessionDaoSupportimplementsUserDao{


publicintaddUser(Useruser){

returnthis.getSqlSession().insert("com.hoo.user.dao.UserDao.addUser",user);

}

}


在上面的配置文件中加入配置

<beanid="baseDao"class="com.hoo.framework.mybatis.support.DynamicSqlSessionDaoSupport"abstract="true"lazy-init="true">

<propertyname="targetSqlSessionFactorys">

<map>

<entryvalue-ref="oracleSqlSessionFactory"key="oracle"/>

<entryvalue-ref="mysqlSqlSessionFactory"key="mysql"/>

</map>

</property>

<propertyname="defaultTargetSqlSessionFactory"ref="oracleSqlSessionFactory"/>

</bean>


<beanid="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对象的注入情况。



所以我们配置扫描的方式也就相当于我们在配置文件中给每一个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。本文最后的实现方式就是这样完成的。

publicvoidsetSqlSessionFactory(SqlSessionFactorysqlSessionFactory){

if(!this.externalSqlSession){

this.sqlSession=newSqlSessionTemplate(sqlSessionFactory);

}

}


publicvoidsetSqlSessionTemplate(SqlSessionTemplatesqlSessionTemplate){

this.sqlSession=sqlSessionTemplate;

this.externalSqlSession=true;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: