最近项目用到多数据源, 在网上找了资料解决了,稍微描述下.
假如数据源用到两个,一个正式数据库, 一个历史数据库.
在applicationContext.xml中的相关配置如下:
<bean id="dataSource_now" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${database.driver}"></property>
<property name="jdbcUrl" value="${database.url}"></property>
<property name="user" value="${database.username}"></property>
<property name="password" value="${database.password}"></property>
<property name="minPoolSize" value="${database.minimumConnectionCount}"></property>
<property name="maxPoolSize" value="${database.maximumConnectionCount}"></property>
<property name="maxIdleTime" value="${database.houseKeepingSleepTime}"></property>
</bean>
<!-- 历史库,历史的数据的查询到另外的表进行查询 -->
<bean id="dataSource_his" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${database_his.driver}"></property>
<property name="jdbcUrl" value="${database_his.url}"></property>
<property name="user" value="${database_his.username}"></property>
<property name="password" value="${database_his.password}"></property>
<property name="minPoolSize" value="${database_his.minimumConnectionCount}"></property>
<property name="maxPoolSize" value="${database_his.maximumConnectionCount}"></property>
<property name="maxIdleTime" value="${database_his.houseKeepingSleepTime}"></property>
</bean>
这里相关的配置放在properties文件中加载,就不详述了.数据源的管理和设置默认值.
<bean id="dataSource" class="com.chris.common.multidataSource.DynamicDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry key="ds_now" value-ref="dataSource_now" ></entry>
<entry key="ds_his" value-ref="dataSource_his" ></entry>
</map>
</property>
<property name="defaultTargetDataSource" ref="dataSource_now"></property>
</bean>
数据源的管理需要重写AbstractRoutingDataSource类的determineCurrentLookupKey()方法, 这个是最关键的, 他决定了当前线程的绑定事务的上下文.
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
* @author wuxing
*
* 2015年2月6日
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSourceType();
}
}
其中DataSourceContextHolder用于获取当前线程的事务上下文.
/**
* @author wuxing
*
* 2015年2月6日
*/
@SuppressWarnings("unchecked")
public class DataSourceContextHolder {
/**
* DataSource上下文,每个线程对应相应的数据源key
*/
@SuppressWarnings("rawtypes")
public static final ThreadLocal contextHolder = new ThreadLocal();
public static void setDataSourceType(String dataSourceType)
{
contextHolder.set(dataSourceType);
}
public static String getDataSourceType()
{
return (String)contextHolder.get();
}
public static void clearDataSourceType()
{
contextHolder.remove();
}
}
setter和getter的参数,就是切换数据源时在配置文件中配置的key..本例中为ds_now和ds_his
这边有个需要注意的地方..当没有调用数据源的set的方法时..不能在之前调用他的get方法.
在ThreadLocal中有段代码, 当get被调用时,若数据源没有被set, 则会调用他的setInitialValue()方法, 而他的initialValue的值为null.
这样的话,系统就会报一行错误--->SqlSession was not registered for synchronization because synchronization is no active.
数据源的和mybatis的配置如下, 指定了mybatis的sql文件的扫描路径.
<bean id="sqlSessionFactory" name="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations">
<list>
<value>classpath:com/chris/dao/impl/mapper/*Mapper.xml</value>
</list>
</property>
</bean>
<bean id="sqlSessionFactoryTemplate" name="sqlSessionFactoryTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory" />
</bean>
mybatis-config.xml的配置如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="multipleResultSetsEnabled" value="true"/>
<setting name="useColumnLabel" value="true"/>
<setting name="useGeneratedKeys" value="true"/>
<setting name="defaultExecutorType" value="SIMPLE"/>
<setting name="defaultStatementTimeout" value="25000"/>
</settings>
</configuration>
接下来就是事务的配置
<bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource">
<ref bean="dataSource" />
</property>
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value=" com.chris.dao.mapper" />
<!-- spring 3.1.1 和mybatis 要用名字指定连接工厂 因为跟property-placeholder冲突 -->
<!-- <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" /> -->
<!-- <property name="sqlSessionFactory" ref="sqlSessionFactory" /> -->
<property name="sqlSessionTemplateBeanName" value="sqlSessionFactoryTemplate" />
</bean>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="add*" propagation="REQUIRED" rollback-for="Exception" />
<tx:method name="del*" propagation="REQUIRED" rollback-for="Exception" />
<tx:method name="modify*" propagation="REQUIRED" rollback-for="Exception" />
<tx:method name="update*" propagation="REQUIRED" rollback-for="Exception" />
<tx:method name="insert*" propagation="REQUIRED" rollback-for="Exception" />
<tx:method name="txn_*" propagation="REQUIRED" rollback-for="java.lang.Exception" no-rollback-for=""/> <!-- 需要加事务的函数开头 -->
<tx:method name="create*" propagation="REQUIRED" rollback-for="java.lang.Exception" no-rollback-for=""/><!-- 定义不需要回滚的异常 -->
<tx:method name="*" read-only="true" />
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="allManagerMethod" expression="execution(* com.chris.service..*.*(..))" />
<aop:advisor pointcut-ref="allManagerMethod" advice-ref="txAdvice" order="10" />
</aop:config>
通过指定事务内的方法名, 以及事务扫描的包路径,回滚事宜, 将事务配置好.