Spring 整合mybatis

首先必须得说spring的强大,主流的框架几乎都可以进行整合,那么今天我们就先看一下spring整合mybatis的简单配置,首先我们创建在我们spring中配置文件中配置我们spring的入口信息,如下:

1.数据源信息

<bean id="mysql" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
        <property name="driverClass">
            <value>${jdbc.driverClassName}</value>
        </property>
        <property name="jdbcUrl">
            <value>${jdbc.url}</value>
        </property>
        <property name="user">
            <value>${jdbc.username}</value>
        </property>
        <property name="password">
            <value>${jdbc.password}</value>
        </property>  
        <property name="minPoolSize" value="10" />
        <property name="maxPoolSize" value="100" />  
        <property name="maxIdleTime" value="1800" />   
        <property name="acquireIncrement" value="3" />
        <property name="maxStatements" value="1000" />
        <property name="initialPoolSize" value="10" />
        <property name="idleConnectionTestPeriod" value="60" /> 
        <property name="acquireRetryAttempts" value="30" />
        <property name="breakAfterAcquireFailure" value="false" />
        <property name="testConnectionOnCheckout" value="false" />
        <property name="acquireRetryDelay">
            <value>100</value>
        </property>  
    </bean>
    
    <bean id="oracle" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
        <property name="driverClass">
            <value>${jdbc.driverClassName}</value>
        </property>
        <property name="jdbcUrl">
            <value>${jdbc.url}</value>
        </property>
        <property name="user">
            <value>${jdbc.username}</value>
        </property>
        <property name="password">
            <value>${jdbc.password}</value>
        </property>  
        <property name="minPoolSize" value="10" />
        <property name="maxPoolSize" value="100" />  
        <property name="maxIdleTime" value="1800" />   
        <property name="acquireIncrement" value="3" />
        <property name="maxStatements" value="1000" />
        <property name="initialPoolSize" value="10" />
        <property name="idleConnectionTestPeriod" value="60" /> 
        <property name="acquireRetryAttempts" value="30" />
        <property name="breakAfterAcquireFailure" value="false" />
        <property name="testConnectionOnCheckout" value="false" />
        <property name="acquireRetryDelay">
            <value>100</value>
        </property>  
    </bean>

2.mybatis 的入口类也就是我们sqlsession工厂类

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="${dataSource}" />
        <property name="mapperLocations">
            <value>classpath*:com/dongnao/jack/xml/*Mapper.xml</value>
        </property>
        <property name="databaseIdProvider" ref="databaseIdProvider" />  
        <property name="plugins">
            <array>
                <bean class="com.dongnao.jack.mybatisInterceptor.ExectorInterceptor"></bean>
                <bean class="com.dongnao.jack.mybatisInterceptor.PageInterceptor"></bean>
                <bean class="com.dongnao.jack.mybatisInterceptor.ResultSetCacheInterceptor"></bean>
            </array>
        </property>
    </bean>

3.创建数据库切换类配置

<!-- mybatis多产商数据库支持 -->
    <bean id="databaseIdProvider" class="org.apache.ibatis.mapping.VendorDatabaseIdProvider">  
        <property name="properties" ref="vendorProperties" />  
    </bean>  

4.创建spring 属性文件获取的类,可以根据需要切换我们的数据库

<bean id="vendorProperties"  
        class="org.springframework.beans.factory.config.PropertiesFactoryBean">  
        <property name="properties">  
            <props>  
                <prop key="Oracle">oracle</prop>  
                <prop key="MySQL">mysql</prop>  
            </props>  
        </property>  
    </bean> 

注意:我们在mapper中配置我们,如果不同数据库中执行的sql有差异,这时我们上述配置就起到了作用,可以根据我们拼写的具体sql来链接我们不同的数据库,具体操作使用mysql mapper配置标签databaseId="mysql"(根据我们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

 
  1. package com.hoo.framework.mybatis.support;

  2.  
  3. import static java.lang.reflect.Proxy.newProxyInstance;

  4. import static org.apache.ibatis.reflection.ExceptionUtil.unwrapThrowable;

  5. import static org.mybatis.spring.SqlSessionUtils.closeSqlSession;

  6. import static org.mybatis.spring.SqlSessionUtils.getSqlSession;

  7. import static org.mybatis.spring.SqlSessionUtils.isSqlSessionTransactional;

  8.  
  9. import java.lang.reflect.InvocationHandler;

  10. import java.lang.reflect.Method;

  11. import java.sql.Connection;

  12. import java.util.List;

  13. import java.util.Map;

  14.  
  15. import org.apache.ibatis.exceptions.PersistenceException;

  16. import org.apache.ibatis.executor.BatchResult;

  17. import org.apache.ibatis.session.Configuration;

  18. import org.apache.ibatis.session.ExecutorType;

  19. import org.apache.ibatis.session.ResultHandler;

  20. import org.apache.ibatis.session.RowBounds;

  21. import org.apache.ibatis.session.SqlSession;

  22. import org.apache.ibatis.session.SqlSessionFactory;

  23. import org.mybatis.spring.MyBatisExceptionTranslator;

  24. import org.mybatis.spring.SqlSessionTemplate;

  25. import org.springframework.dao.support.PersistenceExceptionTranslator;

  26. import org.springframework.util.Assert;

  27.  
  28. /**

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

  30. * @author hoojo

  31. * @createDate 2013-10-18 下午03:07:46

  32. * @file CustomSqlSessionTemplate.java

  33. * @package com.hoo.framework.mybatis.support

  34. * @project SHMB

  35. * @blog http://blog.****.net/IBM_hoojo

  36. * @email [email protected]

  37. * @version 1.0

  38. */

  39. public class CustomSqlSessionTemplate extends SqlSessionTemplate {

  40.  
  41. private final SqlSessionFactory sqlSessionFactory;

  42. private final ExecutorType executorType;

  43. private final SqlSession sqlSessionProxy;

  44. private final PersistenceExceptionTranslator exceptionTranslator;

  45.  
  46. private Map<Object, SqlSessionFactory> targetSqlSessionFactorys;

  47. private SqlSessionFactory defaultTargetSqlSessionFactory;

  48.  
  49. public void setTargetSqlSessionFactorys(Map<Object, SqlSessionFactory> targetSqlSessionFactorys) {

  50. this.targetSqlSessionFactorys = targetSqlSessionFactorys;

  51. }

  52.  
  53. public void setDefaultTargetSqlSessionFactory(SqlSessionFactory defaultTargetSqlSessionFactory) {

  54. this.defaultTargetSqlSessionFactory = defaultTargetSqlSessionFactory;

  55. }

  56.  
  57. public CustomSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {

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

  59. }

  60.  
  61. public CustomSqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) {

  62. this(sqlSessionFactory, executorType, new MyBatisExceptionTranslator(sqlSessionFactory.getConfiguration()

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

  64. }

  65.  
  66. public CustomSqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,

  67. PersistenceExceptionTranslator exceptionTranslator) {

  68.  
  69. super(sqlSessionFactory, executorType, exceptionTranslator);

  70.  
  71. this.sqlSessionFactory = sqlSessionFactory;

  72. this.executorType = executorType;

  73. this.exceptionTranslator = exceptionTranslator;

  74.  
  75. this.sqlSessionProxy = (SqlSession) newProxyInstance(

  76. SqlSessionFactory.class.getClassLoader(),

  77. new Class[] { SqlSession.class },

  78. new SqlSessionInterceptor());

  79.  
  80. this.defaultTargetSqlSessionFactory = sqlSessionFactory;

  81. }

  82.  
  83. @Override

  84. public SqlSessionFactory getSqlSessionFactory() {

  85.  
  86. SqlSessionFactory targetSqlSessionFactory = targetSqlSessionFactorys.get(CustomerContextHolder.getContextType());

  87. if (targetSqlSessionFactory != null) {

  88. return targetSqlSessionFactory;

  89. } else if (defaultTargetSqlSessionFactory != null) {

  90. return defaultTargetSqlSessionFactory;

  91. } else {

  92. Assert.notNull(targetSqlSessionFactorys, "Property 'targetSqlSessionFactorys' or 'defaultTargetSqlSessionFactory' are required");

  93. Assert.notNull(defaultTargetSqlSessionFactory, "Property 'defaultTargetSqlSessionFactory' or 'targetSqlSessionFactorys' are required");

  94. }

  95. return this.sqlSessionFactory;

  96. }

  97.  
  98. @Override

  99. public Configuration getConfiguration() {

  100. return this.getSqlSessionFactory().getConfiguration();

  101. }

  102.  
  103. public ExecutorType getExecutorType() {

  104. return this.executorType;

  105. }

  106.  
  107. public PersistenceExceptionTranslator getPersistenceExceptionTranslator() {

  108. return this.exceptionTranslator;

  109. }

  110.  
  111. /**

  112. * {@inheritDoc}

  113. */

  114. public <T> T selectOne(String statement) {

  115. return this.sqlSessionProxy.<T> selectOne(statement);

  116. }

  117.  
  118. /**

  119. * {@inheritDoc}

  120. */

  121. public <T> T selectOne(String statement, Object parameter) {

  122. return this.sqlSessionProxy.<T> selectOne(statement, parameter);

  123. }

  124.  
  125. /**

  126. * {@inheritDoc}

  127. */

  128. public <K, V> Map<K, V> selectMap(String statement, String mapKey) {

  129. return this.sqlSessionProxy.<K, V> selectMap(statement, mapKey);

  130. }

  131.  
  132. /**

  133. * {@inheritDoc}

  134. */

  135. public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey) {

  136. return this.sqlSessionProxy.<K, V> selectMap(statement, parameter, mapKey);

  137. }

  138.  
  139. /**

  140. * {@inheritDoc}

  141. */

  142. public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) {

  143. return this.sqlSessionProxy.<K, V> selectMap(statement, parameter, mapKey, rowBounds);

  144. }

  145.  
  146. /**

  147. * {@inheritDoc}

  148. */

  149. public <E> List<E> selectList(String statement) {

  150. return this.sqlSessionProxy.<E> selectList(statement);

  151. }

  152.  
  153. /**

  154. * {@inheritDoc}

  155. */

  156. public <E> List<E> selectList(String statement, Object parameter) {

  157. return this.sqlSessionProxy.<E> selectList(statement, parameter);

  158. }

  159.  
  160. /**

  161. * {@inheritDoc}

  162. */

  163. public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {

  164. return this.sqlSessionProxy.<E> selectList(statement, parameter, rowBounds);

  165. }

  166.  
  167. /**

  168. * {@inheritDoc}

  169. */

  170. public void select(String statement, ResultHandler handler) {

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

  172. }

  173.  
  174. /**

  175. * {@inheritDoc}

  176. */

  177. public void select(String statement, Object parameter, ResultHandler handler) {

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

  179. }

  180.  
  181. /**

  182. * {@inheritDoc}

  183. */

  184. public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {

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

  186. }

  187.  
  188. /**

  189. * {@inheritDoc}

  190. */

  191. public int insert(String statement) {

  192. return this.sqlSessionProxy.insert(statement);

  193. }

  194.  
  195. /**

  196. * {@inheritDoc}

  197. */

  198. public int insert(String statement, Object parameter) {

  199. return this.sqlSessionProxy.insert(statement, parameter);

  200. }

  201.  
  202. /**

  203. * {@inheritDoc}

  204. */

  205. public int update(String statement) {

  206. return this.sqlSessionProxy.update(statement);

  207. }

  208.  
  209. /**

  210. * {@inheritDoc}

  211. */

  212. public int update(String statement, Object parameter) {

  213. return this.sqlSessionProxy.update(statement, parameter);

  214. }

  215.  
  216. /**

  217. * {@inheritDoc}

  218. */

  219. public int delete(String statement) {

  220. return this.sqlSessionProxy.delete(statement);

  221. }

  222.  
  223. /**

  224. * {@inheritDoc}

  225. */

  226. public int delete(String statement, Object parameter) {

  227. return this.sqlSessionProxy.delete(statement, parameter);

  228. }

  229.  
  230. /**

  231. * {@inheritDoc}

  232. */

  233. public <T> T getMapper(Class<T> type) {

  234. return getConfiguration().getMapper(type, this);

  235. }

  236.  
  237. /**

  238. * {@inheritDoc}

  239. */

  240. public void commit() {

  241. throw new UnsupportedOperationException("Manual commit is not allowed over a Spring managed SqlSession");

  242. }

  243.  
  244. /**

  245. * {@inheritDoc}

  246. */

  247. public void commit(boolean force) {

  248. throw new UnsupportedOperationException("Manual commit is not allowed over a Spring managed SqlSession");

  249. }

  250.  
  251. /**

  252. * {@inheritDoc}

  253. */

  254. public void rollback() {

  255. throw new UnsupportedOperationException("Manual rollback is not allowed over a Spring managed SqlSession");

  256. }

  257.  
  258. /**

  259. * {@inheritDoc}

  260. */

  261. public void rollback(boolean force) {

  262. throw new UnsupportedOperationException("Manual rollback is not allowed over a Spring managed SqlSession");

  263. }

  264.  
  265. /**

  266. * {@inheritDoc}

  267. */

  268. public void close() {

  269. throw new UnsupportedOperationException("Manual close is not allowed over a Spring managed SqlSession");

  270. }

  271.  
  272. /**

  273. * {@inheritDoc}

  274. */

  275. public void clearCache() {

  276. this.sqlSessionProxy.clearCache();

  277. }

  278.  
  279. /**

  280. * {@inheritDoc}

  281. */

  282. public Connection getConnection() {

  283. return this.sqlSessionProxy.getConnection();

  284. }

  285.  
  286. /**

  287. * {@inheritDoc}

  288. * @since 1.0.2

  289. */

  290. public List<BatchResult> flushStatements() {

  291. return this.sqlSessionProxy.flushStatements();

  292. }

  293.  
  294. /**

  295. * Proxy needed to route MyBatis method calls to the proper SqlSession got from Spring's Transaction Manager It also

  296. * unwraps exceptions thrown by {@code Method#invoke(Object, Object...)} to pass a {@code PersistenceException} to

  297. * the {@code PersistenceExceptionTranslator}.

  298. */

  299. private class SqlSessionInterceptor implements InvocationHandler {

  300. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

  301. final SqlSession sqlSession = getSqlSession(

  302. CustomSqlSessionTemplate.this.getSqlSessionFactory(),

  303. CustomSqlSessionTemplate.this.executorType,

  304. CustomSqlSessionTemplate.this.exceptionTranslator);

  305. try {

  306. Object result = method.invoke(sqlSession, args);

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

  308. // force commit even on non-dirty sessions because some databases require

  309. // a commit/rollback before calling close()

  310. sqlSession.commit(true);

  311. }

  312. return result;

  313. } catch (Throwable t) {

  314. Throwable unwrapped = unwrapThrowable(t);

  315. if (CustomSqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {

  316. Throwable translated = CustomSqlSessionTemplate.this.exceptionTranslator

  317. .translateExceptionIfPossible((PersistenceException) unwrapped);

  318. if (translated != null) {

  319. unwrapped = translated;

  320. }

  321. }

  322. throw unwrapped;

  323. } finally {

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

  325. }

  326. }

  327. }

  328.  
  329. }

 

 

 

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

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

CustomerContextHolder 设置SqlSessionFactory的类型

 
  1. package com.hoo.framework.mybatis.support;

  2. /*

  3. *多数据源

  4. */

  5.  
  6. public abstract class CustomerContextHolder {

  7.  
  8. public final static String SESSION_FACTORY_MYSQL = "mysql";

  9. public final static String SESSION_FACTORY_ORACLE = "oracle";

  10.  
  11. private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();

  12.  
  13. public static void setContextType(String contextType) {

  14. contextHolder.set(contextType);

  15. }

  16.  
  17. public static String getContextType() {

  18. return contextHolder.get();

  19. }

  20.  
  21. public static void clearContextType() {

  22. contextHolder.remove();

  23. }

  24. }

  25.  
  26.  
  27.  

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

 
  1. <?xml version="1.0" encoding="UTF-8"?>

  2. <beans xmlns="http://www.springframework.org/schema/beans"

  3. xmlns:aop="http://www.springframework.org/schema/aop"

  4. xmlns:tx="http://www.springframework.org/schema/tx"

  5. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

  6. xsi:schemaLocation="http://www.springframework.org/schema/beans

  7. http://www.springframework.org/schema/beans/spring-beans-3.2.xsd

  8. http://www.springframework.org/schema/aop

  9. http://www.springframework.org/schema/aop/spring-aop-3.2.xsd

  10. http://www.springframework.org/schema/tx

  11. http://www.springframework.org/schema/tx/spring-tx-3.2.xsd ">

  12.  
  13. <!-- 配置c3p0数据源 -->

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

  15. <property name="driverClass" value="${datasource.driver}"/>

  16. <property name="jdbcUrl" value="${datasource.url}"/>

  17. <property name="user" value="${datasource.username}"/>

  18. <property name="password" value="${datasource.password}"/>

  19.  
  20. <property name="acquireIncrement" value="${c3p0.acquireIncrement}"/>

  21. <property name="initialPoolSize" value="${c3p0.initialPoolSize}"/>

  22. <property name="minPoolSize" value="${c3p0.minPoolSize}"/>

  23. <property name="maxPoolSize" value="${c3p0.maxPoolSize}"/>

  24. <property name="maxIdleTime" value="${c3p0.maxIdleTime}"/>

  25. <property name="idleConnectionTestPeriod" value="${c3p0.idleConnectionTestPeriod}"/>

  26. <property name="maxStatements" value="${c3p0.maxStatements}"/>

  27. <property name="numHelperThreads" value="${c3p0.numHelperThreads}"/>

  28. <property name="preferredTestQuery" value="${c3p0.preferredTestQuery}"/>

  29. <property name="testConnectionOnCheckout" value="${c3p0.testConnectionOnCheckout}"/>

  30. </bean>

  31.  
  32. <bean id="dataSourceMySQL" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">

  33. <property name="driverClass" value="com.mysql.jdbc.Driver"/>

  34. <property name="jdbcUrl" value="jdbc:mysql://172.31.108.178:3306/world?useUnicode=true&amp;characterEncoding=UTF-8&amp;zeroDateTimeBehavior=convertToNull"/>

  35. <property name="user" value="root"/>

  36. <property name="password" value="jp2011"/>

  37.  
  38. <property name="acquireIncrement" value="${c3p0.acquireIncrement}"/>

  39. <property name="initialPoolSize" value="${c3p0.initialPoolSize}"/>

  40. <property name="minPoolSize" value="${c3p0.minPoolSize}"/>

  41. <property name="maxPoolSize" value="${c3p0.maxPoolSize}"/>

  42. <property name="maxIdleTime" value="${c3p0.maxIdleTime}"/>

  43. <property name="idleConnectionTestPeriod" value="${c3p0.idleConnectionTestPeriod}"/>

  44. <property name="maxStatements" value="${c3p0.maxStatements}"/>

  45. <property name="numHelperThreads" value="${c3p0.numHelperThreads}"/>

  46. <property name="preferredTestQuery" value="${c3p0.preferredTestQuery}"/>

  47. <property name="testConnectionOnCheckout" value="${c3p0.testConnectionOnCheckout}"/>

  48. </bean>

  49.  
  50. <!-- 配置SqlSessionFactoryBean -->

  51. <bean id="oracleSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">

  52. <property name="dataSource" ref="dataSourceOracle"/>

  53. <property name="configLocation" value="classpath:mybatis.xml"/>

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

  55. <property name="mapperLocations">

  56. <list>

  57. <!-- 表示在com.hoo目录下的任意包下的resultmap包目录中,以-resultmap.xml或-mapper.xml结尾所有文件 -->

  58. <value>classpath:com/hoo/framework/mybatis/mybatis-common.xml</value>

  59. <value>classpath:com/hoo/**/resultmap/*-resultmap.xml</value>

  60. <value>classpath:com/hoo/**/mapper/*-mapper.xml</value>

  61. <value>classpath:com/hoo/**/mapper/**/*-mapper.xml</value>

  62. </list>

  63. </property>

  64. </bean>

  65.  
  66. <!-- 配置SqlSessionFactoryBean -->

  67. <bean id="mysqlSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">

  68. <property name="dataSource" ref="dataSourceMySQL"/>

  69. <property name="configLocation" value="classpath:mybatis.xml"/>

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

  71. <property name="mapperLocations">

  72. <list>

  73. <!-- 表示在com.hoo目录下的任意包下的resultmap包目录中,以-resultmap.xml或-mapper.xml结尾所有文件 (oracle和mysql扫描的配置和路径不一样,如果是公共的都扫描 这里要区分下,不然就报错 找不到对应的表、视图)-->

  74. <value>classpath:com/hoo/framework/mybatis/mybatis-common.xml</value>

  75. <value>classpath:com/hoo/**/resultmap/*-mysql-resultmap.xml</value>

  76. <value>classpath:com/hoo/**/mapper/*-mysql-mapper.xml</value>

  77. <value>classpath:com/hoo/**/mapper/**/*-mysql-mapper.xml</value>

  78. <value>classpath:com/hoo/**/mapper/**/multiple-datasource-mapper.xml</value>

  79. </list>

  80. </property>

  81. </bean>

  82.  
  83. <!-- 配置自定义的SqlSessionTemplate模板,注入相关配置 -->

  84. <bean id="sqlSessionTemplate" class="com.hoo.framework.mybatis.support.CustomSqlSessionTemplate">

  85. <constructor-arg ref="oracleSqlSessionFactory" />

  86. <property name="targetSqlSessionFactorys">

  87. <map>

  88. <entry value-ref="oracleSqlSessionFactory" key="oracle"/>

  89. <entry value-ref="mysqlSqlSessionFactory" key="mysql"/>

  90. </map>

  91. </property>

  92. </bean>

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

  95. <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">

  96. <property name="basePackage" value="com.hoo.**.mapper"/>

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

  98. <property name="sqlSessionTemplateBeanName" value="sqlSessionTemplate"/>

  99. <property name="markerInterface" value="com.hoo.framework.mybatis.SqlMapper"/>

  100. </bean>

  101.  
  102. </beans>

  103.  

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

 
  1. public abstract class SqlSessionDaoSupport extends DaoSupport {

  2.  
  3. private SqlSession sqlSession;

  4.  
  5. private boolean externalSqlSession;

  6.  
  7. public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {

  8. if (!this.externalSqlSession) {

  9. this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);

  10. }

  11. }

  12.  
  13. public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {

  14. this.sqlSession = sqlSessionTemplate;

  15. this.externalSqlSession = true;

  16. }

  17. ......

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

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

 
  1. package com.hoo.server.datasource.mapper;

  2.  
  3. import java.util.List;

  4. import java.util.Map;

  5. import com.hoo.framework.mybatis.SqlMapper;

  6. /*

  7. *MyBatis 多数据源 测试查询接口

  8. */

  9. public interface MultipleDataSourceMapper extends SqlMapper {

  10.  
  11. public List<Map<String, Object>> execute4MySQL() throws Exception;

  12.  
  13. public List<Map<String, Object>> execute4Oracle() throws Exception;

  14. }

[xml] view plain copy print?

  1. multiple-datasource-mapper.xml  
  2.   
  3.   
  4. <?xml version="1.0" encoding="UTF-8" ?>  
  5. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">  
  6. <mapper namespace="com.hoo.server.datasource.mapper.MultipleDataSourceMapper">  
  7.       
  8.     <select id="execute4Oracle" resultType="map">  
  9.         <![CDATA[ 
  10.             SELECT 
  11.                 * 
  12.             FROM 
  13.                 deviceInfo_tab t where rownum < 10 
  14.         ]]>  
  15.     </select>  
  16.       
  17.     <select id="execute4MySQL" resultType="map">  
  18.         <![CDATA[ 
  19.             SELECT 
  20.                 * 
  21.             FROM 
  22.                 city limit 2 
  23.         ]]>  
  24.     </select>  
  25. </mapper>  
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、测试代码

 
  1. @Autowired

  2. @Qualifier("multipleDataSourceMapper")

  3. private MultipleDataSourceMapper mapper;

  4.  
  5. @Test

  6. public void testMapper() {

  7. CustomerContextHolder.setContextType(CustomerContextHolder.SESSION_FACTORY_MYSQL);

  8. try {

  9. trace(mapper.execute4MySQL());

  10. } catch (Exception e1) {

  11. e1.printStackTrace();

  12. }

  13. CustomerContextHolder.setContextType(CustomerContextHolder.SESSION_FACTORY_ORACLE);

  14. try {

  15. trace(mapper.execute4Oracle());

  16. } catch (Exception e) {

  17. e.printStackTrace();

  18. }

  19. }

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

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

 
  1. package com.hoo.framework.mybatis.support;

  2.  
  3. import java.util.Map;

  4. import org.apache.ibatis.session.SqlSession;

  5. import org.apache.ibatis.session.SqlSessionFactory;

  6. import org.mybatis.spring.SqlSessionUtils;

  7. import org.mybatis.spring.support.SqlSessionDaoSupport;

  8. import org.springframework.beans.BeansException;

  9. import org.springframework.context.ApplicationContext;

  10. import org.springframework.context.ApplicationContextAware;

  11. /*

  12. *MyBatis 动态SqlSessionFactory

  13. */

  14. public class DynamicSqlSessionDaoSupport extends SqlSessionDaoSupport implements ApplicationContextAware {

  15.  
  16. private ApplicationContext applicationContext;

  17.  
  18. private Map<Object, SqlSessionFactory> targetSqlSessionFactorys;

  19. private SqlSessionFactory defaultTargetSqlSessionFactory;

  20. private SqlSession sqlSession;

  21.  
  22. @Override

  23. public final SqlSession getSqlSession() {

  24. SqlSessionFactory targetSqlSessionFactory = targetSqlSessionFactorys.get(CustomerContextHolder.getContextType());

  25. if (targetSqlSessionFactory != null) {

  26. setSqlSessionFactory(targetSqlSessionFactory);

  27. } else if (defaultTargetSqlSessionFactory != null) {

  28. setSqlSessionFactory(defaultTargetSqlSessionFactory);

  29. targetSqlSessionFactory = defaultTargetSqlSessionFactory;

  30. } else {

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

  32. setSqlSessionFactory(targetSqlSessionFactory);

  33. }

  34. this.sqlSession = SqlSessionUtils.getSqlSession(targetSqlSessionFactory);

  35. return this.sqlSession;

  36. }

  37.  
  38. @Override

  39. protected void checkDaoConfig() {

  40. //Assert.notNull(getSqlSession(), "Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required");

  41. }

  42.  
  43. public void setTargetSqlSessionFactorys(Map<Object, SqlSessionFactory> targetSqlSessionFactorys) {

  44. this.targetSqlSessionFactorys = targetSqlSessionFactorys;

  45. }

  46.  
  47. public void setDefaultTargetSqlSessionFactory(SqlSessionFactory defaultTargetSqlSessionFactory) {

  48. this.defaultTargetSqlSessionFactory = defaultTargetSqlSessionFactory;

  49. }

  50.  
  51. @Override

  52. public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {

  53. this.applicationContext = applicationContext;

  54. }

  55. }

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

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

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

 
  1. public class UserMapperDaoImpl extends DynamicSqlSessionDaoSupport implements UserDao {

  2.  
  3. public int addUser(User user) {

  4. return this.getSqlSession().insert("com.hoo.user.dao.UserDao.addUser", user);

  5. }

  6. }

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

[xml] view plain copy print?

  1. <bean id="baseDao" class="com.hoo.framework.mybatis.support.DynamicSqlSessionDaoSupport" abstract="true" lazy-init="true">  
  2.     <property name="targetSqlSessionFactorys">  
  3.         <map>       
  4.             <entry value-ref="oracleSqlSessionFactory" key="oracle"/>  
  5.             <entry value-ref="mysqlSqlSessionFactory" key="mysql"/>  
  6.         </map>   
  7.     </property>  
  8.     <property name="defaultTargetSqlSessionFactory" ref="oracleSqlSessionFactory"/>  
  9.    </bean>  
  10.    
  11. <bean id="userMapperDao" class="com.hoo.user.dao.impl.UserMapperDaoImpl" parent="baseDao"/>  
<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对象的注入情况。

Spring 整合mybatis

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

 
  1. public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {

  2. if (!this.externalSqlSession) {

  3. this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);

  4. }

  5. }

  6.  
  7. public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {

  8. this.sqlSession = sqlSessionTemplate;

  9. this.externalSqlSession = true;

  10. }