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
上两篇文章分别介绍了
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;}@OverridepublicSqlSessionFactorygetSqlSessionFactory(){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;}@OverridepublicConfigurationgetConfiguration(){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/beanshttp://www.springframework.org/schema/beans/spring-beans-3.2.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-3.2.xsdhttp://www.springframework.org/schema/txhttp://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.xmlpackagecom.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*FROMdeviceInfo_tabtwhererownum<10]]></select><selectid="execute4MySQL"resultType="map"><![CDATA[SELECT*FROMcitylimit2]]></select></mapper>
上面分别查询oracle和mysql两个数据库中的table
4、测试代码@Autowired@Qualifier("multipleDataSourceMapper")privateMultipleDataSourceMappermapper;@TestpublicvoidtestMapper(){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;@OverridepublicfinalSqlSessiongetSqlSession(){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;}@OverrideprotectedvoidcheckDaoConfig(){//Assert.notNull(getSqlSession(),"Property'sqlSessionFactory'or'sqlSessionTemplate'arerequired");}publicvoidsetTargetSqlSessionFactorys(Map<Object,SqlSessionFactory>targetSqlSessionFactorys){this.targetSqlSessionFactorys=targetSqlSessionFactorys;}publicvoidsetDefaultTargetSqlSessionFactory(SqlSessionFactorydefaultTargetSqlSessionFactory){this.defaultTargetSqlSessionFactory=defaultTargetSqlSessionFactory;}@OverridepublicvoidsetApplicationContext(ApplicationContextapplicationContext)throwsBeansException{this.applicationContext=applicationContext;}}
主要重写getSqlSession方法,上面获取SqlSessionFactory的方法。
重写好了后就可以配置这个对象,配置代码如下//每一个DAO由继承SqlSessionDaoSupport全部改为DynamicSqlSessionDaoSupportpublicclassUserMapperDaoImplextendsDynamicSqlSessionDaoSupportimplementsUserDao{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;}
相关文章推荐
- Cucumber java + Webdriver (4) 使用自动化测试的Page对象模式
- C3P0+Spring集成核心配置
- Java加密技术(七)——数字证书
- [转] Java多线程发展简史
- Java 多线程 并发编程
- 深入详解Struts2——struts.xml配置之Action配置详解
- Ubuntu下HBase安装过程
- n人站圈报数,3 出列。 最后留下的人原来的位置 3种方式
- java学习笔记
- 防止表单重复提交
- 浅谈spring的bean管理
- Java加密技术(六)——非对称加密算法DSA
- java类加载机制
- 转 jdk1.5新特性 ConcurrentHashMap
- 如何优化 Java 性能?
- 如何优化 Java 性能?
- 在Eclipse中使用Axis2插件自动生成WSDL文件
- 在Eclipse中使用Axis2插件生成Web Service服务端/客户端
- C/S和B/S 《JavaWeb开发王者归来》学习笔记
- java观察者(Observer)模式