Spring整合Mybatis实现动态数据源切换教程配置

Spring整合Mybatis实现动态数据源切换教程配置

一.摘要

这篇文章将介绍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

001package com.hoo.framework.mybatis.support;
002  
003import static java.lang.reflect.Proxy.newProxyInstance;
004import static org.apache.ibatis.reflection.ExceptionUtil.unwrapThrowable;
005import static org.mybatis.spring.SqlSessionUtils.closeSqlSession;
006import static org.mybatis.spring.SqlSessionUtils.getSqlSession;
007import static org.mybatis.spring.SqlSessionUtils.isSqlSessionTransactional;
008  
009import java.lang.reflect.InvocationHandler;
010import java.lang.reflect.Method;
011import java.sql.Connection;
012import java.util.List;
013import java.util.Map;
014  
015import org.apache.ibatis.exceptions.PersistenceException;
016import org.apache.ibatis.executor.BatchResult;
017import org.apache.ibatis.session.Configuration;
018import org.apache.ibatis.session.ExecutorType;
019import org.apache.ibatis.session.ResultHandler;
020import org.apache.ibatis.session.RowBounds;
021import org.apache.ibatis.session.SqlSession;
022import org.apache.ibatis.session.SqlSessionFactory;
023import org.mybatis.spring.MyBatisExceptionTranslator;
024import org.mybatis.spring.SqlSessionTemplate;
025import org.springframework.dao.support.PersistenceExceptionTranslator;
026import org.springframework.util.Assert;
027  
028/**
029 * <b>function:</b> 继承SqlSessionTemplate 重写相关方法
030 * @author hoojo
031 * @createDate 2013-10-18 下午03:07:46
032 * @file CustomSqlSessionTemplate.java
033 * @package com.hoo.framework.mybatis.support
034 * @project SHMB
036 * @email hoojo_@126.com
037 * @version 1.0
038 */
039public class CustomSqlSessionTemplate extends SqlSessionTemplate {
040  
041    private final SqlSessionFactory sqlSessionFactory;
042    private final ExecutorType executorType;
043    private final SqlSession sqlSessionProxy;
044    private final PersistenceExceptionTranslator exceptionTranslator;
045  
046    private Map<Object, SqlSessionFactory> targetSqlSessionFactorys;
047    private SqlSessionFactory defaultTargetSqlSessionFactory;
048  
049    public void setTargetSqlSessionFactorys(Map<Object, SqlSessionFactory> targetSqlSessionFactorys) {
050        this.targetSqlSessionFactorys = targetSqlSessionFactorys;
051    }
052  
053    public void setDefaultTargetSqlSessionFactory(SqlSessionFactory defaultTargetSqlSessionFactory) {
054        this.defaultTargetSqlSessionFactory = defaultTargetSqlSessionFactory;
055    }
056  
057    public CustomSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
058        this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType());
059    }
060  
061    public CustomSqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) {
062        this(sqlSessionFactory, executorType, new MyBatisExceptionTranslator(sqlSessionFactory.getConfiguration()
063                .getEnvironment().getDataSource(), true));
064    }
065  
066    public CustomSqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
067            PersistenceExceptionTranslator exceptionTranslator) {
068  
069        super(sqlSessionFactory, executorType, exceptionTranslator);
070  
071        this.sqlSessionFactory = sqlSessionFactory;
072        this.executorType = executorType;
073        this.exceptionTranslator = exceptionTranslator;
074         
075        this.sqlSessionProxy = (SqlSession) newProxyInstance(
076                SqlSessionFactory.class.getClassLoader(),
077                new Class[] { SqlSession.class },
078                new SqlSessionInterceptor());
079  
080        this.defaultTargetSqlSessionFactory = sqlSessionFactory;
081    }
082  
083    @Override
084    public SqlSessionFactory getSqlSessionFactory() {
085  
086        SqlSessionFactory targetSqlSessionFactory = targetSqlSessionFactorys.get(CustomerContextHolder.getContextType());
087        if (targetSqlSessionFactory != null) {
088            return targetSqlSessionFactory;
089        } else if (defaultTargetSqlSessionFactory != null) {
090            return defaultTargetSqlSessionFactory;
091        } else {
092            Assert.notNull(targetSqlSessionFactorys, "Property 'targetSqlSessionFactorys' or 'defaultTargetSqlSessionFactory' are required");
093            Assert.notNull(defaultTargetSqlSessionFactory, "Property 'defaultTargetSqlSessionFactory' or 'targetSqlSessionFactorys' are required");
094        }
095        return this.sqlSessionFactory;
096    }
097  
098    @Override
099    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的类型

01package com.hoo.framework.mybatis.support;
02/*
03 *多数据源
04 */
05 
06public abstract class CustomerContextHolder {
07  
08    public final static String SESSION_FACTORY_MYSQL = "mysql";
09    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}

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

001<?xml version="1.0" encoding="UTF-8"?>
006    xsi:schemaLocation="http://www.springframework.org/schema/beans
007    http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
008    http://www.springframework.org/schema/aop
009    http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
010    http://www.springframework.org/schema/tx 
011    http://www.springframework.org/schema/tx/spring-tx-3.2.xsd ">
012     
013    <!-- 配置c3p0数据源 -->
014    <bean id="dataSourceOracle" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
015        <property name="driverClass" value="${datasource.driver}"/>
016        <property name="jdbcUrl" value="${datasource.url}"/>
017        <property name="user" value="${datasource.username}"/>
018        <property name="password" value="${datasource.password}"/>
019                 
020        <property name="acquireIncrement" value="${c3p0.acquireIncrement}"/>
021        <property name="initialPoolSize" value="${c3p0.initialPoolSize}"/>
022        <property name="minPoolSize" value="${c3p0.minPoolSize}"/>
023        <property name="maxPoolSize" value="${c3p0.maxPoolSize}"/>
024        <property name="maxIdleTime" value="${c3p0.maxIdleTime}"/>
025        <property name="idleConnectionTestPeriod" value="${c3p0.idleConnectionTestPeriod}"/>
026        <property name="maxStatements" value="${c3p0.maxStatements}"/>
027        <property name="numHelperThreads" value="${c3p0.numHelperThreads}"/>
028        <property name="preferredTestQuery" value="${c3p0.preferredTestQuery}"/>
029        <property name="testConnectionOnCheckout" value="${c3p0.testConnectionOnCheckout}"/>
030    </bean>
031     
032    <bean id="dataSourceMySQL" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
033        <property name="driverClass" value="com.mysql.jdbc.Driver"/>
035        <property name="user" value="root"/>
036        <property name="password" value="jp2011"/>
037                 
038        <property name="acquireIncrement" value="${c3p0.acquireIncrement}"/>
039        <property name="initialPoolSize" value="${c3p0.initialPoolSize}"/>
040        <property name="minPoolSize" value="${c3p0.minPoolSize}"/>
041        <property name="maxPoolSize" value="${c3p0.maxPoolSize}"/>
042        <property name="maxIdleTime" value="${c3p0.maxIdleTime}"/>
043        <property name="idleConnectionTestPeriod" value="${c3p0.idleConnectionTestPeriod}"/>
044        <property name="maxStatements" value="${c3p0.maxStatements}"/>
045        <property name="numHelperThreads" value="${c3p0.numHelperThreads}"/>
046        <property name="preferredTestQuery" value="${c3p0.preferredTestQuery}"/>
047        <property name="testConnectionOnCheckout" value="${c3p0.testConnectionOnCheckout}"/>
048    </bean>
049     
050    <!-- 配置SqlSessionFactoryBean -->
051    <bean id="oracleSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
052        <property name="dataSource" ref="dataSourceOracle"/>
053        <property name="configLocation" value="classpath:mybatis.xml"/>
054        <!-- mapper和resultmap配置路径 -->
055        <property name="mapperLocations">
056            <list>
057                <!-- 表示在com.hoo目录下的任意包下的resultmap包目录中,以-resultmap.xml或-mapper.xml结尾所有文件 -->
058                <value>classpath:com/hoo/framework/mybatis/mybatis-common.xml</value>
059                <value>classpath:com/hoo/**/resultmap/*-resultmap.xml</value>
060                <value>classpath:com/hoo/**/mapper/*-mapper.xml</value>
061                <value>classpath:com/hoo/**/mapper/**/*-mapper.xml</value>
062            </list>
063        </property>
064    </bean>
065     
066    <!-- 配置SqlSessionFactoryBean -->
067    <bean id="mysqlSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
068        <property name="dataSource" ref="dataSourceMySQL"/>
069        <property name="configLocation" value="classpath:mybatis.xml"/>
070        <!-- mapper和resultmap配置路径 --> 
071        <property name="mapperLocations">
072            <list>
073                <!-- 表示在com.hoo目录下的任意包下的resultmap包目录中,以-resultmap.xml或-mapper.xml结尾所有文件 (oracle和mysql扫描的配置和路径不一样,如果是公共的都扫描 这里要区分下,不然就报错 找不到对应的表、视图)-->
074                <value>classpath:com/hoo/framework/mybatis/mybatis-common.xml</value>
075                <value>classpath:com/hoo/**/resultmap/*-mysql-resultmap.xml</value>
076                <value>classpath:com/hoo/**/mapper/*-mysql-mapper.xml</value>
077                <value>classpath:com/hoo/**/mapper/**/*-mysql-mapper.xml</value>
078                <value>classpath:com/hoo/**/mapper/**/multiple-datasource-mapper.xml</value>
079            </list>
080        </property>
081    </bean>   
082     
083    <!-- 配置自定义的SqlSessionTemplate模板,注入相关配置 -->
084    <bean id="sqlSessionTemplate" class="com.hoo.framework.mybatis.support.CustomSqlSessionTemplate">
085        <constructor-arg ref="oracleSqlSessionFactory" />
086        <property name="targetSqlSessionFactorys">
087            <map>    
088                <entry value-ref="oracleSqlSessionFactory" key="oracle"/>
089                <entry value-ref="mysqlSqlSessionFactory" key="mysql"/>
090            </map>
091        </property>
092    </bean>
093     
094    <!-- 通过扫描的模式,扫描目录在com/hoo/任意目录下的mapper目录下,所有的mapper都需要继承SqlMapper接口的接口 -->
095    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
096        <property name="basePackage" value="com.hoo.**.mapper"/>
097        <!-- 注意注入sqlSessionTemplate -->
098        <property name="sqlSessionTemplateBeanName" value="sqlSessionTemplate"/>
099        <property name="markerInterface" value="com.hoo.framework.mybatis.SqlMapper"/>
100    </bean>
101     
102</beans>

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

01public abstract class SqlSessionDaoSupport extends DaoSupport {
02  
03  private SqlSession sqlSession;
04  
05  private boolean externalSqlSession;
06  
07  public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
08    if (!this.externalSqlSession) {
09      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

01package com.hoo.server.datasource.mapper;
02  
03import java.util.List;
04import java.util.Map;
05import com.hoo.framework.mybatis.SqlMapper;
06/*
07 *MyBatis 多数据源 测试查询接口
08 */
09public 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}
01multiple-datasource-mapper.xml
02 
03 
04<?xml version="1.0" encoding="UTF-8" ?>
05<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
06<mapper namespace="com.hoo.server.datasource.mapper.MultipleDataSourceMapper">
07     
08    <select id="execute4Oracle" resultType="map">
09        <![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>

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

4、测试代码

01@Autowired
02@Qualifier("multipleDataSourceMapper")
03private MultipleDataSourceMapper mapper;
04  
05@Test
06public void testMapper() {
07    CustomerContextHolder.setContextType(CustomerContextHolder.SESSION_FACTORY_MYSQL);
08    try {
09        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,那么方法如下

01package com.hoo.framework.mybatis.support;
02  
03import java.util.Map;
04import org.apache.ibatis.session.SqlSession;
05import org.apache.ibatis.session.SqlSessionFactory;
06import org.mybatis.spring.SqlSessionUtils;
07import org.mybatis.spring.support.SqlSessionDaoSupport;
08import org.springframework.beans.BeansException;
09import org.springframework.context.ApplicationContext;
10import org.springframework.context.ApplicationContextAware;
11/*
12 *MyBatis 动态SqlSessionFactory
13 */
14public 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

1public 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}

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

01<bean id="baseDao" class="com.hoo.framework.mybatis.support.DynamicSqlSessionDaoSupport" abstract="true" lazy-init="true">
02    <property name="targetSqlSessionFactorys">
03        <map>    
04            <entry value-ref="oracleSqlSessionFactory" key="oracle"/>
05            <entry value-ref="mysqlSqlSessionFactory" key="mysql"/>
06        </map>
07    </property>
08    <property name="defaultTargetSqlSessionFactory" ref="oracleSqlSessionFactory"/>
09   </bean>
10  
11<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。本文最后的实现方式就是这样完成的。

01public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
02  if (!this.externalSqlSession) {
03    this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);
04  }
05}
06  
07public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
08  this.sqlSession = sqlSessionTemplate;
09  this.externalSqlSession = true;
10}

原文地址:http://www.blogjava.net/hoojo/archive/2013/10/22/405488.html

欢迎拜访!


最代码官方编辑于2016-7-9 18:10:58
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值