Spring3 整合MyBatis3 配置多数据源动态选择SqlSessionFactory详细教程
一、摘要
这篇文章将介绍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; }
以上所述是小编给大家介绍的spring3 整合mybatis3 配置多数据源动态选择sqlsessionfactory详细教程,希望对大家有所帮助