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详细教程,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • Spring Boot 整合 Mybatis Annotation 注解的完整 Web 案例

    前言 距离第一篇 Spring Boot 系列的博文 3 个月了.虽然 XML 形式是我比较推荐的,但是注解形式也是方便的.尤其一些小系统,快速的 CRUD 轻量级的系统. 这里感谢晓春 http://xchunzhao.tk/ 的 Pull Request,提供了 springboot-mybatis-annotation 的实现. 一.运行 springboot-mybatis-annotation 工程 然后Application 应用启动类的 main 函数,然后在浏览器访问: http

  • 关于Spring3 + Mybatis3整合时多数据源动态切换的问题

    以前的项目经历中,基本上都是spring + hibernate + Spring JDBC这种组合用的多.至于MyBatis,也就这个项目才开始试用,闲话不多说,进入正题. 以前的这种框架组合中,动态数据源切换可谓已经非常成熟了,网上也有非常多的博客介绍,都是继承AbstractRoutingDataSource,重写determineCurrentLookupKey()方法.具体做法就不在此废话了. 所以当项目中碰到这个问题,我几乎想都没有想,就采用了这种做法,但是一测试,一点反应都没有.当

  • Spring Boot整合MyBatis操作过程

    1.加入mybatis-spring-boot-stater的Maven依赖 <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.1.1</version> </dependency> 2.配置数据源 在src/main/re

  • spring与mybatis整合配置文件

    最近因为项目要求整合了spring+mybatis架构进行项目开发,现将相关整合配置文件整理如下: 基本架构:spring+springmvc+mybatis 分布式框架:dubbo+zookeeper 数据库:mysql 数据库连接池:Druid 1 数据库连接配置信息jdbc.properties #mysql version database druid # setting validationQuery=SELECT 1 jdbc.driverClassName=com.mysql.jd

  • 详解spring+springmvc+mybatis整合注解

    每天记录一点点,慢慢的成长,今天我们学习了ssm,这是我自己总结的笔记,大神勿喷!谢谢,主要代码!! ! spring&springmvc&mybatis整合(注解) 1.jar包 2.引入web.xml文件 <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param

  • 详解Spring Boot整合Mybatis实现 Druid多数据源配置

    一.多数据源的应用场景 目前,业界流行的数据操作框架是 Mybatis,那 Druid 是什么呢? Druid 是 Java 的数据库连接池组件.Druid 能够提供强大的监控和扩展功能.比如可以监控 SQL ,在监控业务可以查询慢查询 SQL 列表等.Druid 核心主要包括三部分: 1. DruidDriver 代理 Driver,能够提供基于 Filter-Chain 模式的插件体系. 2. DruidDataSource 高效可管理的数据库连接池 3. SQLParser 当业务数据量达

  • Spring3 整合MyBatis3 配置多数据源动态选择SqlSessionFactory详细教程

    一.摘要 这篇文章将介绍Spring整合Mybatis 如何完成SqlSessionFactory的动态切换的.并且会简单的介绍下MyBatis整合Spring中的官方的相关代码. Spring整合MyBatis切换SqlSessionFactory有两种方法 第一. 继承SqlSessionDaoSupport,重写获取SqlSessionFactory的方法. 第二.继承SqlSessionTemplate 重写getSqlSessionFactory.getConfiguration和Sq

  • 详解Springboot之整合JDBCTemplate配置多数据源

    一.前言 现在在我们的项目中,使用多数据源已经是很常见的,下面,这里总结一下springboot整合jdbcTemplate配置多数据源的代码示例,以方便以后直接使用. 二.配置文件 spring: datasource: datasourceone: driverClassName: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/eesy?serverTimezone=UTC&characterEncoding=utf8&u

  • Springboot整合JPA配置多数据源流程详解

    目录 1. Maven 2. 基本配置 DataSource 3. 多数据源配置 3.1 JpaConfigOracle 3.2 JpaConfigMysql 4. Dao层接口 1. Maven <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>

  • CentOS 7下安装配置proftpd搭建ftp服务器的详细教程

    proftpd全称:Professional FTP daemon,是针对Wu-FTP的弱项而开发的,除了改进的安全性,还具备许多Wu-FTP没有的特点,能以Stand-alone.xinetd模式运行等.ProFTP已经成为继Wu-FTP之后最为流行的FTP服务器软件,越来越多的站点选用它构筑安全高效的FTP站点,ProFTP配置方便,并有MySQL和Quota模块可供选择,利用它们的完美结合可以实现非系统账号的管理和用户磁盘的限制.<摘抄百度百科> 本章通过下载源码的方式安装,可以到官网下

  • VS Code配置Go语言开发环境的详细教程

    VS Code是微软开源的一款编辑器,插件系统十分的丰富.本文就介绍了如何使用VS Code搭建Go语言开发环境. VS Code配置Go语言开发环境 说在前面的话,Go语言是采用UTF8编码的,理论上使用任何文本编辑器都能做Go语言开发.大家可以根据自己的喜好自行选择.编辑器/IDE没有最好只有最适合. 下载与安装 VS Code官方下载地址:https://code.visualstudio.com/Download 三大主流平台都支持,请根据自己的电脑平台选择对应的安装包. 双击下载好的安

  • VSCode配置C/C++环境的最新详细教程

    目录 VSCode简介 一.下载 二.安装 三.设置中文环境 四.完全卸载 五.配置C/C++环境 1.安装MinGW编译器 2.安装C/C++扩展 3.配置C/C++环境 六.结语 补充:vscode设置成中文 VSCode简介 VSCode是一款微软出的轻量级编辑器,它本身只是一款文本编辑器而已,所有的功能都是以插件扩展的形式所存在,想用什么功能就安装对应的扩展即可,非常方便,同时也支持非常多的主题和图标,外观比较好看,重要的是VSCode支持各大主流操作系统,包括Windows.Linux

  • springboot下配置多数据源的方法

    一.springboot 简介 SpringBoot使开发独立的,产品级别的基于Spring的应用变得非常简单,你只需"just run". 我们为Spring平台及第三方库提 供开箱即用的设置,这样你就可以有条不紊地开始.多数Spring Boot应用需要很少的Spring配置. 你可以使用SpringBoot创建Java应用,并使用 java -jar 启动它或采用传统的war部署方式.我们也提供了一个运行"spring 脚本"的命令行工具. 二.传统的Dat

  • SpringBoot整合MyBatisPlus配置动态数据源的方法

    MybatisPlus特性 •无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑 •损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作 •强大的 CRUD 操作:内置通用 Mapper.通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求 •支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错 •支持多种数据库:支持 MySQL.MariaDB.Ora

  • SpringBoot整合Mybatis实现多数据源配置与跨数据源事务实例

    目录 Pom依赖 application.yml 多数据源配置 MasterDataSourceConfig ClusterDataSourceConfig 启动类 使用 实现跨数据源事务 Pom依赖 <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.

随机推荐