一、摘要
白云鄂網(wǎng)站制作公司哪家好,找創(chuàng)新互聯(lián)!從網(wǎng)頁設(shè)計、網(wǎng)站建設(shè)、微信開發(fā)、APP開發(fā)、響應(yīng)式網(wǎng)站建設(shè)等網(wǎng)站項目制作,到程序開發(fā),運營維護。創(chuàng)新互聯(lián)于2013年開始到現(xiàn)在10年的時間,我們擁有了豐富的建站經(jīng)驗和運維經(jīng)驗,來保證我們的工作的順利進行。專注于網(wǎng)站建設(shè)就選創(chuàng)新互聯(lián)。
這篇文章將介紹Spring整合Mybatis 如何完成SqlSessionFactory的動態(tài)切換的。并且會簡單的介紹下MyBatis整合Spring中的官方的相關(guān)代碼。
Spring整合MyBatis切換SqlSessionFactory有兩種方法
第一、 繼承SqlSessionDaoSupport,重寫獲取SqlSessionFactory的方法。
第二、繼承SqlSessionTemplate 重寫getSqlSessionFactory、getConfiguration和SqlSessionInterceptor這個攔截器。其中最為關(guān)鍵還是繼承SqlSessionTemplate 并重寫里面的方法。
而Spring整合MyBatis也有兩種方式,一種是配置MapperFactoryBean,另一種則是利用MapperScannerConfigurer進行掃描接口或包完成對象的自動創(chuàng)建。相對來說后者更方便些。
MapperFactoryBean繼承了SqlSessionDaoSupport也就是動態(tài)切換SqlSessionFactory的第一種方法,我們需要重寫和實現(xiàn)SqlSessionDaoSupport方法,或者是繼承MapperFactoryBean來重寫覆蓋相關(guān)方法。如果利用MapperScannerConfigurer的配置整合來切換SqlSessionFactory,那么我們就需要繼承SqlSessionTemplate,重寫上面提到的方法。在整合的配置中很多地方都是可以注入SqlSessionTemplate代替SqlSessionFactory的注入的。因為SqlSessionTemplate的創(chuàng)建也是需要注入SqlSessionFactory的。
二、實現(xiàn)代碼
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; /** * function: 繼承SqlSessionTemplate 重寫相關(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
重寫后的getSqlSessionFactory方法會從我們配置的SqlSessionFactory集合targetSqlSessionFactorys或默認的defaultTargetSqlSessionFactory中獲取Session對象。而改寫的SqlSessionInterceptor 是這個MyBatis整合Spring的關(guān)鍵,所有的SqlSessionFactory對象的session都將在這里完成創(chuàng)建、提交、關(guān)閉等操作。所以我們改寫這里的代碼,在這里獲取getSqlSessionFactory的時候,從多個SqlSessionFactory中獲取我們設(shè)置的那個即可。
上面添加了targetSqlSessionFactorys、defaultTargetSqlSessionFactory兩個屬性來配置多個SqlSessionFactory對象和默認的SqlSessionFactory對象。
CustomerContextHolder 設(shè)置SqlSessionFactory的類型
package com.hoo.framework.mybatis.support; /** * function: 多數(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 ThreadLocalcontextHolder = new ThreadLocal (); 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"?>
classpath:com/hoo/framework/mybatis/mybatis-common.xml classpath:com/hoo/**/resultmap/*-resultmap.xml classpath:com/hoo/**/mapper/*-mapper.xml classpath:com/hoo/**/mapper/**/*-mapper.xml
classpath:com/hoo/framework/mybatis/mybatis-common.xml classpath:com/hoo/**/resultmap/*-mysql-resultmap.xml classpath:com/hoo/**/mapper/*-mysql-mapper.xml classpath:com/hoo/**/mapper/**/*-mysql-mapper.xml classpath:com/hoo/**/mapper/**/multiple-datasource-mapper.xml
上面的配置關(guān)鍵是在MapperScannerConfigurer中注入sqlSessionTemplate,這個要注意。當我們配置了多個SqlSessionFactoryBean的時候,就需要為MapperScannerConfigurer指定一個sqlSessionFactoryBeanName或是sqlSessionTemplateBeanName。一般情況下注入了sqlSessionTemplateBeanName對象,那sqlSessionFactory也就有值了。如果單獨的注入了sqlSessionFactory那么程序會創(chuàng)建一個sqlSessionTemplate對象。我們可以看看代碼SqlSessionFactoryDaoSupport對象的代碼。如果你不喜歡使用掃描的方式,也可以注入sqlSessionTemplate或繼承sqlSessionTemplate完成數(shù)據(jù)庫操作。
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上面的注入也就不會執(zhí)行了。如果沒有注入sqlSessionTemplate,那么會自動new一個sqlSessionTemplate對象。
3、編寫相關(guān)測試接口和實現(xiàn)的mapper.xml
package com.hoo.server.datasource.mapper; import java.util.List; import java.util.Map; import com.hoo.framework.mybatis.SqlMapper; /** * function: MyBatis 多數(shù)據(jù)源 測試查詢接口 * @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
上面分別查詢oracle和mysql兩個數(shù)據(jù)庫中的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(); } }
運行后發(fā)現(xiàn)能夠順利查詢出數(shù)據(jù)。
如果你是重寫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; /** * function: MyBatis 動態(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 MaptargetSqlSessionFactorys; 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 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); } }
在上面的配置文件中加入配置
就這樣也可以利用DynamicSqlSessionDaoSupport來完成動態(tài)切換sqlSessionFactory對象,只需用在注入userMapperDao調(diào)用方法的時候設(shè)置下CustomerContextHolder的contextType即可。
三、總結(jié)
為了實現(xiàn)這個功能看了mybatis-spring-1.2.0.jar這個包的部分源代碼,代碼內(nèi)容不是很多。所以看了下主要的代碼,下面做些簡單的介紹。
MapperScannerConfigurer這個類就是我們要掃描的Mapper接口的類,也就是basePackage中繼承markerInterface配置的接口。可以看看ClassPathBeanDefinitionScanner、ClassPathMapperScanner中的doScan這個方法。它會掃描basePackage這個包下所有接口,在ClassPathScanningCandidateComponentProvider中有這個方法findCandidateComponents,它會找到所有的BeanDefinition。
最重要的一點是ClassPathMapperScanner中的doScan這個方法它會給這些接口創(chuàng)建一個MapperFactoryBean。并且會檢查sqlSessionFactory和sqlSessionTemplate對象的注入情況。
image 所以我們配置掃描的方式也就相當于我們在配置文件中給每一個Mapper配置一個MapperFactoryBean一樣。而這個MapperFactoryBean又繼承SqlSessionDaoSupport。所以當初我想重寫MapperScannerConfigurer中的postProcessBeanDefinitionRegistry方法,然后重寫方法中的ClassPathMapperScanner中的doScan方法,將definition.setBeanClass(MapperFactoryBean.class);改成自己定義的MapperFactoryBean。最后以失敗告終,因為這里是Spring裝載掃描對象的時候都已經(jīng)為這些對象創(chuàng)建好了代理、設(shè)置好了mapperInterface和注入需要的類。所以在調(diào)用相關(guān)操作數(shù)據(jù)庫的API方法的時候,設(shè)置對應(yīng)的SqlSessionFactory也是無效的。
輾轉(zhuǎn)反側(cè)我看到了SqlSessionTemplate這個類,它的功能相當于SqlSessionDaoSupport的實現(xiàn)類MapperFactoryBean。最為關(guān)鍵的是SqlSessionTemplate有一個攔截器SqlSessionInterceptor,它復(fù)制所有SqlSession的創(chuàng)建、提交、關(guān)閉,而且是在每個方法之前。這點在上面也提到過了!所以我們只需要在SqlSessionInterceptor方法中獲取SqlSessionFactory的時候,在這之前調(diào)用下CustomerContextHolder.setContextType方法即可完成數(shù)據(jù)庫的SqlSessionFactory的切換。而在MapperScannerConfigurer提供了注入SqlSessionFactory和sqlSessionTemplate的方法,如果注入了SqlSessionFactory系統(tǒng)將會new一個sqlSessionTemplate,而注入了sqlSessionTemplate就不會創(chuàng)建其他對象(見下面代碼)。所以我們配置一個sqlSessionTemplate并注入到MapperScannerConfigurer中,程序?qū)褂眠@個sqlSessionTemplate。本文最后的實現(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ù)源動態(tài)選擇SqlSessionFactory詳細教程,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對創(chuàng)新互聯(lián)網(wǎng)站的支持!