首先必须得说spring的强大,主流的框架几乎都可以进行整合,那么今天我们就先看一下spring整合mybatis的简单配置,首先我们创建在我们spring中配置文件中配置我们spring的入口信息,如下:
1.数据源信息
<bean id="mysql" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass">
<value>${jdbc.driverClassName}</value>
</property>
<property name="jdbcUrl">
<value>${jdbc.url}</value>
</property>
<property name="user">
<value>${jdbc.username}</value>
</property>
<property name="password">
<value>${jdbc.password}</value>
</property>
<property name="minPoolSize" value="10" />
<property name="maxPoolSize" value="100" />
<property name="maxIdleTime" value="1800" />
<property name="acquireIncrement" value="3" />
<property name="maxStatements" value="1000" />
<property name="initialPoolSize" value="10" />
<property name="idleConnectionTestPeriod" value="60" />
<property name="acquireRetryAttempts" value="30" />
<property name="breakAfterAcquireFailure" value="false" />
<property name="testConnectionOnCheckout" value="false" />
<property name="acquireRetryDelay">
<value>100</value>
</property>
</bean>
<bean id="oracle" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass">
<value>${jdbc.driverClassName}</value>
</property>
<property name="jdbcUrl">
<value>${jdbc.url}</value>
</property>
<property name="user">
<value>${jdbc.username}</value>
</property>
<property name="password">
<value>${jdbc.password}</value>
</property>
<property name="minPoolSize" value="10" />
<property name="maxPoolSize" value="100" />
<property name="maxIdleTime" value="1800" />
<property name="acquireIncrement" value="3" />
<property name="maxStatements" value="1000" />
<property name="initialPoolSize" value="10" />
<property name="idleConnectionTestPeriod" value="60" />
<property name="acquireRetryAttempts" value="30" />
<property name="breakAfterAcquireFailure" value="false" />
<property name="testConnectionOnCheckout" value="false" />
<property name="acquireRetryDelay">
<value>100</value>
</property>
</bean>
2.mybatis 的入口类也就是我们sqlsession工厂类
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="${dataSource}" />
<property name="mapperLocations">
<value>classpath*:com/dongnao/jack/xml/*Mapper.xml</value>
</property>
<property name="databaseIdProvider" ref="databaseIdProvider" />
<property name="plugins">
<array>
<bean class="com.dongnao.jack.mybatisInterceptor.ExectorInterceptor"></bean>
<bean class="com.dongnao.jack.mybatisInterceptor.PageInterceptor"></bean>
<bean class="com.dongnao.jack.mybatisInterceptor.ResultSetCacheInterceptor"></bean>
</array>
</property>
</bean>
3.创建数据库切换类配置
<!-- mybatis多产商数据库支持 -->
<bean id="databaseIdProvider" class="org.apache.ibatis.mapping.VendorDatabaseIdProvider">
<property name="properties" ref="vendorProperties" />
</bean>
4.创建spring 属性文件获取的类,可以根据需要切换我们的数据库
<bean id="vendorProperties"
class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="properties">
<props>
<prop key="Oracle">oracle</prop>
<prop key="MySQL">mysql</prop>
</props>
</property>
</bean>
注意:我们在mapper中配置我们,如果不同数据库中执行的sql有差异,这时我们上述配置就起到了作用,可以根据我们拼写的具体sql来链接我们不同的数据库,具体操作使用mysql mapper配置标签databaseId="mysql"(根据我们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 [email protected] -
* @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; -
/* -
*多数据源 -
*/ -
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; -
/* -
*MyBatis 多数据源 测试查询接口 -
*/ -
public interface MultipleDataSourceMapper extends SqlMapper { -
public List<Map<String, Object>> execute4MySQL() throws Exception; -
public List<Map<String, Object>> execute4Oracle() throws Exception; -
}
[xml] view plain copy print?
- 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>
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; -
/* -
*MyBatis 动态SqlSessionFactory -
*/ -
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); -
} -
}
在上面的配置文件中加入配置
[xml] view plain copy print?
- <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"/>
<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对象的注入情况。
所以我们配置扫描的方式也就相当于我们在配置文件中给每一个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; -
}