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

一、摘要

上两篇文章分别介绍了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

1package com.hoo.framework.mybatis.support; 2 3import static java.lang.reflect.Proxy.newProxyInstance; 4 5import static org.apache.ibatis.reflection.ExceptionUtil.unwrapThrowable; 6 7import static org.mybatis.spring.SqlSessionUtils.closeSqlSession; 8 9import static org.mybatis.spring.SqlSessionUtils.getSqlSession; 10 11import static org.mybatis.spring.SqlSessionUtils.isSqlSessionTransactional; 12 13import java.lang.reflect.InvocationHandler; 14 15import java.lang.reflect.Method; 16 17import java.sql.Connection; 18 19import java.util.List; 20 21import java.util.Map; 22 23import org.apache.ibatis.exceptions.PersistenceException; 24 25import org.apache.ibatis.executor.BatchResult; 26 27import org.apache.ibatis.session.Configuration; 28 29import org.apache.ibatis.session.ExecutorType; 30 31import org.apache.ibatis.session.ResultHandler; 32 33import org.apache.ibatis.session.RowBounds; 34 35import org.apache.ibatis.session.SqlSession; 36 37import org.apache.ibatis.session.SqlSessionFactory; 38 39import org.mybatis.spring.MyBatisExceptionTranslator; 40 41import org.mybatis.spring.SqlSessionTemplate; 42 43import org.springframework.dao.support.PersistenceExceptionTranslator; 44 45import org.springframework.util.Assert; 46 47/** 48 49 * <b>function:</b> 继承SqlSessionTemplate 重写相关方法 50 51 * @author hoojo 52 53 * @createDate 2013-10-18 下午03:07:46 54 55 * @file CustomSqlSessionTemplate.java 56 57 * @package com.hoo.framework.mybatis.support 58 59 * @project SHMB 60 61 * @blog http://blog.csdn.net/IBM_hoojo 62 63 * @email hoojo_@126.com 64 65 * @version 1.0 66 67 */ 68 69public class CustomSqlSessionTemplate extends SqlSessionTemplate { 70 71private final SqlSessionFactory sqlSessionFactory; 72 73private final ExecutorType executorType; 74 75private final SqlSession sqlSessionProxy; 76 77private final PersistenceExceptionTranslator exceptionTranslator; 78 79private Map<Object, SqlSessionFactory> targetSqlSessionFactorys; 80 81private SqlSessionFactory defaultTargetSqlSessionFactory; 82 83public void setTargetSqlSessionFactorys(Map<Object, SqlSessionFactory> targetSqlSessionFactorys) { 84 85this.targetSqlSessionFactorys = targetSqlSessionFactorys; 86 87} 88 89public void setDefaultTargetSqlSessionFactory(SqlSessionFactory defaultTargetSqlSessionFactory) { 90 91this.defaultTargetSqlSessionFactory = defaultTargetSqlSessionFactory; 92 93} 94 95public CustomSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) { 96 97this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType()); 98 99} 100 101public CustomSqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) { 102 103this(sqlSessionFactory, executorType, new MyBatisExceptionTranslator(sqlSessionFactory.getConfiguration() 104 105.getEnvironment().getDataSource(), true)); 106 107} 108 109public CustomSqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType, 110 111PersistenceExceptionTranslator exceptionTranslator) { 112 113super(sqlSessionFactory, executorType, exceptionTranslator); 114 115this.sqlSessionFactory = sqlSessionFactory; 116 117this.executorType = executorType; 118 119this.exceptionTranslator = exceptionTranslator; 120 121this.sqlSessionProxy = (SqlSession) newProxyInstance( 122 123SqlSessionFactory.class.getClassLoader(), 124 125new Class[] { SqlSession.class }, 126 127new SqlSessionInterceptor()); 128 129this.defaultTargetSqlSessionFactory = sqlSessionFactory; 130 131} 132 133@Override 134 135public SqlSessionFactory getSqlSessionFactory() { 136 137SqlSessionFactory targetSqlSessionFactory = targetSqlSessionFactorys.get(CustomerContextHolder.getContextType()); 138 139if (targetSqlSessionFactory != null) { 140 141return targetSqlSessionFactory; 142 143} else if (defaultTargetSqlSessionFactory != null) { 144 145return defaultTargetSqlSessionFactory; 146 147} else { 148 149Assert.notNull(targetSqlSessionFactorys, "Property 'targetSqlSessionFactorys' or 'defaultTargetSqlSessionFactory' are required"); 150 151Assert.notNull(defaultTargetSqlSessionFactory, "Property 'defaultTargetSqlSessionFactory' or 'targetSqlSessionFactorys' are required"); 152 153} 154 155return this.sqlSessionFactory; 156 157} 158 159@Override 160 161public Configuration getConfiguration() { 162 163return this.getSqlSessionFactory().getConfiguration(); 164 165} 166 167public ExecutorType getExecutorType() { 168 169return this.executorType; 170 171} 172 173public PersistenceExceptionTranslator getPersistenceExceptionTranslator() { 174 175return this.exceptionTranslator; 176 177} 178 179/** 180 181 * {@inheritDoc} 182 183 */ 184 185public <T> T selectOne(String statement) { 186 187return this.sqlSessionProxy.<T> selectOne(statement); 188 189} 190 191/** 192 193 * {@inheritDoc} 194 195 */ 196 197public <T> T selectOne(String statement, Object parameter) { 198 199return this.sqlSessionProxy.<T> selectOne(statement, parameter); 200 201} 202 203/** 204 205 * {@inheritDoc} 206 207 */ 208 209public <K, V> Map<K, V> selectMap(String statement, String mapKey) { 210 211return this.sqlSessionProxy.<K, V> selectMap(statement, mapKey); 212 213} 214 215/** 216 217 * {@inheritDoc} 218 219 */ 220 221public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey) { 222 223return this.sqlSessionProxy.<K, V> selectMap(statement, parameter, mapKey); 224 225} 226 227/** 228 229 * {@inheritDoc} 230 231 */ 232 233public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) { 234 235return this.sqlSessionProxy.<K, V> selectMap(statement, parameter, mapKey, rowBounds); 236 237} 238 239/** 240 241 * {@inheritDoc} 242 243 */ 244 245public <E> List<E> selectList(String statement) { 246 247return this.sqlSessionProxy.<E> selectList(statement); 248 249} 250 251/** 252 253 * {@inheritDoc} 254 255 */ 256 257public <E> List<E> selectList(String statement, Object parameter) { 258 259return this.sqlSessionProxy.<E> selectList(statement, parameter); 260 261} 262 263/** 264 265 * {@inheritDoc} 266 267 */ 268 269public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) { 270 271return this.sqlSessionProxy.<E> selectList(statement, parameter, rowBounds); 272 273} 274 275/** 276 277 * {@inheritDoc} 278 279 */ 280 281public void select(String statement, ResultHandler handler) { 282 283this.sqlSessionProxy.select(statement, handler); 284 285} 286 287/** 288 289 * {@inheritDoc} 290 291 */ 292 293public void select(String statement, Object parameter, ResultHandler handler) { 294 295this.sqlSessionProxy.select(statement, parameter, handler); 296 297} 298 299/** 300 301 * {@inheritDoc} 302 303 */ 304 305public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) { 306 307this.sqlSessionProxy.select(statement, parameter, rowBounds, handler); 308 309} 310 311/** 312 313 * {@inheritDoc} 314 315 */ 316 317public int insert(String statement) { 318 319return this.sqlSessionProxy.insert(statement); 320 321} 322 323/** 324 325 * {@inheritDoc} 326 327 */ 328 329public int insert(String statement, Object parameter) { 330 331return this.sqlSessionProxy.insert(statement, parameter); 332 333} 334 335/** 336 337 * {@inheritDoc} 338 339 */ 340 341public int update(String statement) { 342 343return this.sqlSessionProxy.update(statement); 344 345} 346 347/** 348 349 * {@inheritDoc} 350 351 */ 352 353public int update(String statement, Object parameter) { 354 355return this.sqlSessionProxy.update(statement, parameter); 356 357} 358 359/** 360 361 * {@inheritDoc} 362 363 */ 364 365public int delete(String statement) { 366 367return this.sqlSessionProxy.delete(statement); 368 369} 370 371/** 372 373 * {@inheritDoc} 374 375 */ 376 377public int delete(String statement, Object parameter) { 378 379return this.sqlSessionProxy.delete(statement, parameter); 380 381} 382 383/** 384 385 * {@inheritDoc} 386 387 */ 388 389public <T> T getMapper(Class<T> type) { 390 391return getConfiguration().getMapper(type, this); 392 393} 394 395/** 396 397 * {@inheritDoc} 398 399 */ 400 401public void commit() { 402 403throw new UnsupportedOperationException("Manual commit is not allowed over a Spring managed SqlSession"); 404 405} 406 407/** 408 409 * {@inheritDoc} 410 411 */ 412 413public void commit(boolean force) { 414 415throw new UnsupportedOperationException("Manual commit is not allowed over a Spring managed SqlSession"); 416 417} 418 419/** 420 421 * {@inheritDoc} 422 423 */ 424 425public void rollback() { 426 427throw new UnsupportedOperationException("Manual rollback is not allowed over a Spring managed SqlSession"); 428 429} 430 431/** 432 433 * {@inheritDoc} 434 435 */ 436 437public void rollback(boolean force) { 438 439throw new UnsupportedOperationException("Manual rollback is not allowed over a Spring managed SqlSession"); 440 441} 442 443/** 444 445 * {@inheritDoc} 446 447 */ 448 449public void close() { 450 451throw new UnsupportedOperationException("Manual close is not allowed over a Spring managed SqlSession"); 452 453} 454 455/** 456 457 * {@inheritDoc} 458 459 */ 460 461public void clearCache() { 462 463this.sqlSessionProxy.clearCache(); 464 465} 466 467/** 468 469 * {@inheritDoc} 470 471 */ 472 473public Connection getConnection() { 474 475return this.sqlSessionProxy.getConnection(); 476 477} 478 479/** 480 481 * {@inheritDoc} 482 483 * @since 1.0.2 484 485 */ 486 487public List<BatchResult> flushStatements() { 488 489return this.sqlSessionProxy.flushStatements(); 490 491} 492 493/** 494 495 * Proxy needed to route MyBatis method calls to the proper SqlSession got from Spring's Transaction Manager It also 496 497 * unwraps exceptions thrown by {@code Method#invoke(Object, Object...)} to pass a {@code PersistenceException} to 498 499 * the {@code PersistenceExceptionTranslator}. 500 501 */ 502 503private class SqlSessionInterceptor implements InvocationHandler { 504 505public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 506 507final SqlSession sqlSession = getSqlSession( 508 509CustomSqlSessionTemplate.this.getSqlSessionFactory(), 510 511CustomSqlSessionTemplate.this.executorType, 512 513CustomSqlSessionTemplate.this.exceptionTranslator); 514 515try { 516 517Object result = method.invoke(sqlSession, args); 518 519if (!isSqlSessionTransactional(sqlSession, CustomSqlSessionTemplate.this.getSqlSessionFactory())) { 520 521// force commit even on non-dirty sessions because some databases require 522 523// a commit/rollback before calling close() 524 525sqlSession.commit(true); 526 527} 528 529return result; 530 531} catch (Throwable t) { 532 533Throwable unwrapped = unwrapThrowable(t); 534 535if (CustomSqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) { 536 537Throwable translated = CustomSqlSessionTemplate.this.exceptionTranslator 538 539.translateExceptionIfPossible((PersistenceException) unwrapped); 540 541if (translated != null) { 542 543unwrapped = translated; 544 545} 546 547} 548 549throw unwrapped; 550 551} finally { 552 553closeSqlSession(sqlSession, CustomSqlSessionTemplate.this.getSqlSessionFactory()); 554 555} 556 557} 558 559} 560 561}

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

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

CustomerContextHolder 设置SqlSessionFactory的类型

1package com.hoo.framework.mybatis.support; 2 3/** 4 5 * <b>function:</b> 多数据源 6 7 * @author hoojo 8 9 * @createDate 2013-9-27 上午11:36:57 10 11 * @file CustomerContextHolder.java 12 13 * @package com.hoo.framework.spring.support 14 15 * @project SHMB 16 17 * @blog http://blog.csdn.net/IBM_hoojo 18 19 * @email hoojo_@126.com 20 21 * @version 1.0 22 23 */ 24 25public abstract class CustomerContextHolder { 26 27public final static String SESSION_FACTORY_MYSQL = "mysql"; 28 29public final static String SESSION_FACTORY_ORACLE = "oracle"; 30 31private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>(); 32 33public static void setContextType(String contextType) { 34 35contextHolder.set(contextType); 36 37} 38 39public static String getContextType() { 40 41return contextHolder.get(); 42 43} 44 45public static void clearContextType() { 46 47contextHolder.remove(); 48 49} 50 51}

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

1<?xml version="1.0" encoding="UTF-8"?> 2 3<beans xmlns="http://www.springframework.org/schema/beans" 4 5xmlns:aop="http://www.springframework.org/schema/aop" 6 7xmlns:tx="http://www.springframework.org/schema/tx" 8 9xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 10 11xsi:schemaLocation="http://www.springframework.org/schema/beans 12 13http://www.springframework.org/schema/beans/spring-beans-3.2.xsd 14 15http://www.springframework.org/schema/aop 16 17http://www.springframework.org/schema/aop/spring-aop-3.2.xsd 18 19http://www.springframework.org/schema/tx 20 21http://www.springframework.org/schema/tx/spring-tx-3.2.xsd "> 22 23<!-- 配置c3p0数据源 --> 24 25<bean id="dataSourceOracle" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> 26 27<property name="driverClass" value="${datasource.driver}"/> 28 29<property name="jdbcUrl" value="${datasource.url}"/> 30 31<property name="user" value="${datasource.username}"/> 32 33<property name="password" value="${datasource.password}"/> 34 35<property name="acquireIncrement" value="${c3p0.acquireIncrement}"/> 36 37<property name="initialPoolSize" value="${c3p0.initialPoolSize}"/> 38 39<property name="minPoolSize" value="${c3p0.minPoolSize}"/> 40 41<property name="maxPoolSize" value="${c3p0.maxPoolSize}"/> 42 43<property name="maxIdleTime" value="${c3p0.maxIdleTime}"/> 44 45<property name="idleConnectionTestPeriod" value="${c3p0.idleConnectionTestPeriod}"/> 46 47<property name="maxStatements" value="${c3p0.maxStatements}"/> 48 49<property name="numHelperThreads" value="${c3p0.numHelperThreads}"/> 50 51<property name="preferredTestQuery" value="${c3p0.preferredTestQuery}"/> 52 53<property name="testConnectionOnCheckout" value="${c3p0.testConnectionOnCheckout}"/> 54 55</bean> 56 57<bean id="dataSourceMySQL" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> 58 59<property name="driverClass" value="com.mysql.jdbc.Driver"/> 60 61<property name="jdbcUrl" value="jdbc:mysql://172.31.108.178:3306/world?useUnicode=true&amp;characterEncoding=UTF-8&amp;zeroDateTimeBehavior=convertToNull"/> 62 63<property name="user" value="root"/> 64 65<property name="password" value="jp2011"/> 66 67<property name="acquireIncrement" value="${c3p0.acquireIncrement}"/> 68 69<property name="initialPoolSize" value="${c3p0.initialPoolSize}"/> 70 71<property name="minPoolSize" value="${c3p0.minPoolSize}"/> 72 73<property name="maxPoolSize" value="${c3p0.maxPoolSize}"/> 74 75<property name="maxIdleTime" value="${c3p0.maxIdleTime}"/> 76 77<property name="idleConnectionTestPeriod" value="${c3p0.idleConnectionTestPeriod}"/> 78 79<property name="maxStatements" value="${c3p0.maxStatements}"/> 80 81<property name="numHelperThreads" value="${c3p0.numHelperThreads}"/> 82 83<property name="preferredTestQuery" value="${c3p0.preferredTestQuery}"/> 84 85<property name="testConnectionOnCheckout" value="${c3p0.testConnectionOnCheckout}"/> 86 87</bean> 88 89<!-- 配置SqlSessionFactoryBean --> 90 91<bean id="oracleSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> 92 93<property name="dataSource" ref="dataSourceOracle"/> 94 95<property name="configLocation" value="classpath:mybatis.xml"/> 96 97<!-- mapper和resultmap配置路径 --> 98 99<property name="mapperLocations"> 100 101<list> 102 103<!-- 表示在com.hoo目录下的任意包下的resultmap包目录中,以-resultmap.xml或-mapper.xml结尾所有文件 --> 104 105<value>classpath:com/hoo/framework/mybatis/mybatis-common.xml</value> 106 107<value>classpath:com/hoo/**/resultmap/*-resultmap.xml</value> 108 109<value>classpath:com/hoo/**/mapper/*-mapper.xml</value> 110 111<value>classpath:com/hoo/**/mapper/**/*-mapper.xml</value> 112 113</list> 114 115</property> 116 117</bean> 118 119<!-- 配置SqlSessionFactoryBean --> 120 121<bean id="mysqlSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> 122 123<property name="dataSource" ref="dataSourceMySQL"/> 124 125<property name="configLocation" value="classpath:mybatis.xml"/> 126 127<!-- mapper和resultmap配置路径 --> 128 129<property name="mapperLocations"> 130 131<list> 132 133<!-- 表示在com.hoo目录下的任意包下的resultmap包目录中,以-resultmap.xml或-mapper.xml结尾所有文件 (oracle和mysql扫描的配置和路径不一样,如果是公共的都扫描 这里要区分下,不然就报错 找不到对应的表、视图)--> 134 135<value>classpath:com/hoo/framework/mybatis/mybatis-common.xml</value> 136 137<value>classpath:com/hoo/**/resultmap/*-mysql-resultmap.xml</value> 138 139<value>classpath:com/hoo/**/mapper/*-mysql-mapper.xml</value> 140 141<value>classpath:com/hoo/**/mapper/**/*-mysql-mapper.xml</value> 142 143<value>classpath:com/hoo/**/mapper/**/multiple-datasource-mapper.xml</value> 144 145</list> 146 147</property> 148 149</bean> 150 151<!-- 配置自定义的SqlSessionTemplate模板,注入相关配置 --> 152 153<bean id="sqlSessionTemplate" class="com.hoo.framework.mybatis.support.CustomSqlSessionTemplate"> 154 155<constructor-arg ref="oracleSqlSessionFactory" /> 156 157<property name="targetSqlSessionFactorys"> 158 159<map> 160 161<entry value-ref="oracleSqlSessionFactory" key="oracle"/> 162 163<entry value-ref="mysqlSqlSessionFactory" key="mysql"/> 164 165</map> 166 167</property> 168 169</bean> 170 171<!-- 通过扫描的模式,扫描目录在com/hoo/任意目录下的mapper目录下,所有的mapper都需要继承SqlMapper接口的接口 --> 172 173<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> 174 175<property name="basePackage" value="com.hoo.**.mapper"/> 176 177<!-- 注意注入sqlSessionTemplate --> 178 179<property name="sqlSessionTemplateBeanName" value="sqlSessionTemplate"/> 180 181<property name="markerInterface" value="com.hoo.framework.mybatis.SqlMapper"/> 182 183</bean> 184 185</beans>

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

1public abstract class SqlSessionDaoSupport extends DaoSupport { 2 3private SqlSession sqlSession; 4 5private boolean externalSqlSession; 6 7public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) { 8 9if (!this.externalSqlSession) { 10 11this.sqlSession = new SqlSessionTemplate(sqlSessionFactory); 12 13} 14 15} 16 17public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) { 18 19this.sqlSession = sqlSessionTemplate; 20 21this.externalSqlSession = true; 22 23} 24 25......

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

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

1package com.hoo.server.datasource.mapper; 2 3import java.util.List; 4 5import java.util.Map; 6 7import com.hoo.framework.mybatis.SqlMapper; 8 9/** 10 11 * <b>function:</b> MyBatis 多数据源 测试查询接口 12 13 * @author hoojo 14 15 * @createDate 2013-10-10 下午04:18:08 16 17 * @file MultipleDataSourceMapper.java 18 19 * @package com.hoo.server.datasource.mapper 20 21 * @project SHMB 22 23 * @blog http://blog.csdn.net/IBM_hoojo 24 25 * @email hoojo_@126.com 26 27 * @version 1.0 28 29 */ 30 31public interface MultipleDataSourceMapper extends SqlMapper { 32 33public List<Map<String, Object>> execute4MySQL() throws Exception; 34 35public List<Map<String, Object>> execute4Oracle() throws Exception; 36 37}

multiple-datasource-mapper.xml

1<?xml version="1.0" encoding="UTF-8" ?> 2 3<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 4 5<mapper namespace="com.hoo.server.datasource.mapper.MultipleDataSourceMapper"> 6 7<select id="execute4Oracle" resultType="map"> 8 9<![CDATA[ 10 11SELECT 12 13* 14 15FROM 16 17deviceInfo_tab t where rownum < 10 18 19]]> 20 21</select> 22 23<select id="execute4MySQL" resultType="map"> 24 25<![CDATA[ 26 27SELECT 28 29* 30 31FROM 32 33city limit 2 34 35]]> 36 37</select> 38 39</mapper>

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

4、测试代码

1@Autowired 2 3@Qualifier("multipleDataSourceMapper") 4 5private MultipleDataSourceMapper mapper; 6 7@Test 8 9public void testMapper() { 10 11CustomerContextHolder.setContextType(CustomerContextHolder.SESSION_FACTORY_MYSQL); 12 13try { 14 15trace(mapper.execute4MySQL()); 16 17} catch (Exception e1) { 18 19e1.printStackTrace(); 20 21} 22 23CustomerContextHolder.setContextType(CustomerContextHolder.SESSION_FACTORY_ORACLE); 24 25try { 26 27trace(mapper.execute4Oracle()); 28 29} catch (Exception e) { 30 31e.printStackTrace(); 32 33} 34 35}

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

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

1package com.hoo.framework.mybatis.support; 2 3import java.util.Map; 4 5import org.apache.ibatis.session.SqlSession; 6 7import org.apache.ibatis.session.SqlSessionFactory; 8 9import org.mybatis.spring.SqlSessionUtils; 10 11import org.mybatis.spring.support.SqlSessionDaoSupport; 12 13import org.springframework.beans.BeansException; 14 15import org.springframework.context.ApplicationContext; 16 17import org.springframework.context.ApplicationContextAware; 18 19/** 20 21 * <b>function:</b> MyBatis 动态SqlSessionFactory 22 23 * @author hoojo 24 25 * @createDate 2013-10-14 下午02:32:19 26 27 * @file DynamicSqlSessionDaoSupport.java 28 29 * @package com.hoo.framework.mybatis.support 30 31 * @project SHMB 32 33 * @blog http://blog.csdn.net/IBM_hoojo 34 35 * @email hoojo_@126.com 36 37 * @version 1.0 38 39 */ 40 41public class DynamicSqlSessionDaoSupport extends SqlSessionDaoSupport implements ApplicationContextAware { 42 43private ApplicationContext applicationContext; 44 45private Map<Object, SqlSessionFactory> targetSqlSessionFactorys; 46 47private SqlSessionFactory defaultTargetSqlSessionFactory; 48 49private SqlSession sqlSession; 50 51@Override 52 53public final SqlSession getSqlSession() { 54 55SqlSessionFactory targetSqlSessionFactory = targetSqlSessionFactorys.get(CustomerContextHolder.getContextType()); 56 57if (targetSqlSessionFactory != null) { 58 59setSqlSessionFactory(targetSqlSessionFactory); 60 61} else if (defaultTargetSqlSessionFactory != null) { 62 63setSqlSessionFactory(defaultTargetSqlSessionFactory); 64 65targetSqlSessionFactory = defaultTargetSqlSessionFactory; 66 67} else { 68 69targetSqlSessionFactory = (SqlSessionFactory) applicationContext.getBean(CustomerContextHolder.getContextType()); 70 71setSqlSessionFactory(targetSqlSessionFactory); 72 73} 74 75this.sqlSession = SqlSessionUtils.getSqlSession(targetSqlSessionFactory); 76 77return this.sqlSession; 78 79} 80 81@Override 82 83protected void checkDaoConfig() { 84 85//Assert.notNull(getSqlSession(), "Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required"); 86 87} 88 89public void setTargetSqlSessionFactorys(Map<Object, SqlSessionFactory> targetSqlSessionFactorys) { 90 91this.targetSqlSessionFactorys = targetSqlSessionFactorys; 92 93} 94 95public void setDefaultTargetSqlSessionFactory(SqlSessionFactory defaultTargetSqlSessionFactory) { 96 97this.defaultTargetSqlSessionFactory = defaultTargetSqlSessionFactory; 98 99} 100 101@Override 102 103public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 104 105this.applicationContext = applicationContext; 106 107} 108 109}

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

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

1//每一个DAO由继承SqlSessionDaoSupport全部改为DynamicSqlSessionDaoSupport 2 3public class UserMapperDaoImpl extends DynamicSqlSessionDaoSupport implements UserDao { 4 5public int addUser(User user) { 6 7return this.getSqlSession().insert("com.hoo.user.dao.UserDao.addUser", user); 8 9} 10 11} 12 13在上面的配置文件中加入配置 14 15<bean id="baseDao" class="com.hoo.framework.mybatis.support.DynamicSqlSessionDaoSupport" abstract="true" lazy-init="true"> 16 17<property name="targetSqlSessionFactorys"> 18 19<map> 20 21<entry value-ref="oracleSqlSessionFactory" key="oracle"/> 22 23<entry value-ref="mysqlSqlSessionFactory" key="mysql"/> 24 25</map> 26 27</property> 28 29<property name="defaultTargetSqlSessionFactory" ref="oracleSqlSessionFactory"/> 30 31</bean> 32 33<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。本文最后的实现方式就是这样完成的。

1public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) { 2 3if (!this.externalSqlSession) { 4 5this.sqlSession = new SqlSessionTemplate(sqlSessionFactory); 6 7} 8 9} 10 11public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) { 12 13this.sqlSession = sqlSessionTemplate; 14 15this.externalSqlSession = true; 16 17}
点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )

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