溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

Spring3 整合MyBatis3 配置多數(shù)據(jù)源動(dòng)態(tài)選擇SqlSessionFactory詳細(xì)教程

發(fā)布時(shí)間:2020-08-19 15:04:05 來(lái)源:腳本之家 閱讀:157 作者:hoojo 欄目:編程語(yǔ)言

一、摘要

這篇文章將介紹Spring整合Mybatis 如何完成SqlSessionFactory的動(dòng)態(tài)切換的。并且會(huì)簡(jiǎn)單的介紹下MyBatis整合Spring中的官方的相關(guān)代碼。

Spring整合MyBatis切換SqlSessionFactory有兩種方法

第一、 繼承SqlSessionDaoSupport,重寫(xiě)獲取SqlSessionFactory的方法。

第二、繼承SqlSessionTemplate 重寫(xiě)getSqlSessionFactory、getConfiguration和SqlSessionInterceptor這個(gè)攔截器。其中最為關(guān)鍵還是繼承SqlSessionTemplate 并重寫(xiě)里面的方法。

而Spring整合MyBatis也有兩種方式,一種是配置MapperFactoryBean,另一種則是利用MapperScannerConfigurer進(jìn)行掃描接口或包完成對(duì)象的自動(dòng)創(chuàng)建。相對(duì)來(lái)說(shuō)后者更方便些。

MapperFactoryBean繼承了SqlSessionDaoSupport也就是動(dòng)態(tài)切換SqlSessionFactory的第一種方法,我們需要重寫(xiě)和實(shí)現(xiàn)SqlSessionDaoSupport方法,或者是繼承MapperFactoryBean來(lái)重寫(xiě)覆蓋相關(guān)方法。如果利用MapperScannerConfigurer的配置整合來(lái)切換SqlSessionFactory,那么我們就需要繼承SqlSessionTemplate,重寫(xiě)上面提到的方法。在整合的配置中很多地方都是可以注入SqlSessionTemplate代替SqlSessionFactory的注入的。因?yàn)镾qlSessionTemplate的創(chuàng)建也是需要注入SqlSessionFactory的。

二、實(shí)現(xiàn)代碼

1、繼承SqlSessionTemplate 重寫(xiě)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 重寫(xiě)相關(guān)方法
 * @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());
  }
 }
 }
}

重寫(xiě)后的getSqlSessionFactory方法會(huì)從我們配置的SqlSessionFactory集合targetSqlSessionFactorys或默認(rèn)的defaultTargetSqlSessionFactory中獲取Session對(duì)象。而改寫(xiě)的SqlSessionInterceptor 是這個(gè)MyBatis整合Spring的關(guān)鍵,所有的SqlSessionFactory對(duì)象的session都將在這里完成創(chuàng)建、提交、關(guān)閉等操作。所以我們改寫(xiě)這里的代碼,在這里獲取getSqlSessionFactory的時(shí)候,從多個(gè)SqlSessionFactory中獲取我們?cè)O(shè)置的那個(gè)即可。

上面添加了targetSqlSessionFactorys、defaultTargetSqlSessionFactory兩個(gè)屬性來(lái)配置多個(gè)SqlSessionFactory對(duì)象和默認(rèn)的SqlSessionFactory對(duì)象。

CustomerContextHolder 設(shè)置SqlSessionFactory的類(lèi)型

package com.hoo.framework.mybatis.support;
/**
 * <b>function:</b> 多數(shù)據(jù)源
 * @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、配置相關(guān)的文件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數(shù)據(jù)源 -->
 <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&amp;characterEncoding=UTF-8&amp;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結(jié)尾所有文件 --> 
  <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結(jié)尾所有文件 (oracle和mysql掃描的配置和路徑不一樣,如果是公共的都掃描 這里要區(qū)分下,不然就報(bào)錯(cuò) 找不到對(duì)應(yīng)的表、視圖)--> 
  <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模板,注入相關(guān)配置 -->
 <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>
 <!-- 通過(guò)掃描的模式,掃描目錄在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>

上面的配置關(guān)鍵是在MapperScannerConfigurer中注入sqlSessionTemplate,這個(gè)要注意。當(dāng)我們配置了多個(gè)SqlSessionFactoryBean的時(shí)候,就需要為MapperScannerConfigurer指定一個(gè)sqlSessionFactoryBeanName或是sqlSessionTemplateBeanName。一般情況下注入了sqlSessionTemplateBeanName對(duì)象,那sqlSessionFactory也就有值了。如果單獨(dú)的注入了sqlSessionFactory那么程序會(huì)創(chuàng)建一個(gè)sqlSessionTemplate對(duì)象。我們可以看看代碼SqlSessionFactoryDaoSupport對(duì)象的代碼。如果你不喜歡使用掃描的方式,也可以注入sqlSessionTemplate或繼承sqlSessionTemplate完成數(shù)據(jù)庫(kù)操作。

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上面的注入也就不會(huì)執(zhí)行了。如果沒(méi)有注入sqlSessionTemplate,那么會(huì)自動(dòng)new一個(gè)sqlSessionTemplate對(duì)象。

3、編寫(xiě)相關(guān)測(cè)試接口和實(shí)現(xiàn)的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 多數(shù)據(jù)源 測(cè)試查詢(xún)接口
 * @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>

上面分別查詢(xún)oracle和mysql兩個(gè)數(shù)據(jù)庫(kù)中的table

4、測(cè)試代碼

@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();
 }
}

運(yùn)行后發(fā)現(xiàn)能夠順利查詢(xún)出數(shù)據(jù)。

如果你是重寫(xiě)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 動(dòng)態(tài)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;
 }
}

主要重寫(xiě)getSqlSession方法,上面獲取SqlSessionFactory的方法。

重寫(xiě)好了后就可以配置這個(gè)對(duì)象,配置代碼如下

//每一個(gè)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來(lái)完成動(dòng)態(tài)切換sqlSessionFactory對(duì)象,只需用在注入userMapperDao調(diào)用方法的時(shí)候設(shè)置下CustomerContextHolder的contextType即可。

三、總結(jié)

為了實(shí)現(xiàn)這個(gè)功能看了mybatis-spring-1.2.0.jar這個(gè)包的部分源代碼,代碼內(nèi)容不是很多。所以看了下主要的代碼,下面做些簡(jiǎn)單的介紹。

MapperScannerConfigurer這個(gè)類(lèi)就是我們要掃描的Mapper接口的類(lèi),也就是basePackage中繼承markerInterface配置的接口??梢钥纯碈lassPathBeanDefinitionScanner、ClassPathMapperScanner中的doScan這個(gè)方法。它會(huì)掃描basePackage這個(gè)包下所有接口,在ClassPathScanningCandidateComponentProvider中有這個(gè)方法findCandidateComponents,它會(huì)找到所有的BeanDefinition。

最重要的一點(diǎn)是ClassPathMapperScanner中的doScan這個(gè)方法它會(huì)給這些接口創(chuàng)建一個(gè)MapperFactoryBean。并且會(huì)檢查sqlSessionFactory和sqlSessionTemplate對(duì)象的注入情況。

Spring3 整合MyBatis3 配置多數(shù)據(jù)源動(dòng)態(tài)選擇SqlSessionFactory詳細(xì)教程

image 所以我們配置掃描的方式也就相當(dāng)于我們?cè)谂渲梦募薪o每一個(gè)Mapper配置一個(gè)MapperFactoryBean一樣。而這個(gè)MapperFactoryBean又繼承SqlSessionDaoSupport。所以當(dāng)初我想重寫(xiě)MapperScannerConfigurer中的postProcessBeanDefinitionRegistry方法,然后重寫(xiě)方法中的ClassPathMapperScanner中的doScan方法,將definition.setBeanClass(MapperFactoryBean.class);改成自己定義的MapperFactoryBean。最后以失敗告終,因?yàn)檫@里是Spring裝載掃描對(duì)象的時(shí)候都已經(jīng)為這些對(duì)象創(chuàng)建好了代理、設(shè)置好了mapperInterface和注入需要的類(lèi)。所以在調(diào)用相關(guān)操作數(shù)據(jù)庫(kù)的API方法的時(shí)候,設(shè)置對(duì)應(yīng)的SqlSessionFactory也是無(wú)效的。

輾轉(zhuǎn)反側(cè)我看到了SqlSessionTemplate這個(gè)類(lèi),它的功能相當(dāng)于SqlSessionDaoSupport的實(shí)現(xiàn)類(lèi)MapperFactoryBean。最為關(guān)鍵的是SqlSessionTemplate有一個(gè)攔截器SqlSessionInterceptor,它復(fù)制所有SqlSession的創(chuàng)建、提交、關(guān)閉,而且是在每個(gè)方法之前。這點(diǎn)在上面也提到過(guò)了!所以我們只需要在SqlSessionInterceptor方法中獲取SqlSessionFactory的時(shí)候,在這之前調(diào)用下CustomerContextHolder.setContextType方法即可完成數(shù)據(jù)庫(kù)的SqlSessionFactory的切換。而在MapperScannerConfigurer提供了注入SqlSessionFactory和sqlSessionTemplate的方法,如果注入了SqlSessionFactory系統(tǒng)將會(huì)new一個(gè)sqlSessionTemplate,而注入了sqlSessionTemplate就不會(huì)創(chuàng)建其他對(duì)象(見(jiàn)下面代碼)。所以我們配置一個(gè)sqlSessionTemplate并注入到MapperScannerConfigurer中,程序?qū)?huì)使用這個(gè)sqlSessionTemplate。本文最后的實(shí)現(xiàn)方式就是這樣完成的。

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 配置多數(shù)據(jù)源動(dòng)態(tài)選擇SqlSessionFactory詳細(xì)教程,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)億速云網(wǎng)站的支持!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI