多库数据源深入分析(Mybatis+ Spring + JTA)(1)
多库数据源深入分析(Mybatis+ Spring + JTA)(一)
最近搭建架构,碰到JTA和事务Transaction的问题,在此做个总结:
架构:Mybatis+ Spring
技术:spring的AbstractRoutingDataSource和JTA
老规矩,先贴代码,在讲原理,刚开始的时候不使用JTA,代码如下:
/** * DataSource上下文句柄,通过此类设置需要访问的对应数据源 * */ public class DataSourceContextHolder { /** * DataSource上下文,每个线程对应相应的数据源key */ public static final ThreadLocal contextHolder = new ThreadLocal(); public static void setDataSourceType(String dataSourceType) { contextHolder.set(dataSourceType); } public static String getDataSourceType() { return contextHolder.get(); } public static void clearDataSourceType() { contextHolder.remove(); } }
/** * 动态数据源 * */ public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DataSourceContextHolder.getDataSourceType(); } }
spring中配置如下:
<!-- 配置数据源 -->
<bean id="ds1" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"
lazy-init="false">
<property name="driverClassName" value="${jdbc.ds1.driverClassName}" />
<property name="url" value="${jdbc.ds1.url}" />
<property name="username" value="${jdbc.ds1.username}" />
<property name="password" value="${jdbc.ds1.password}" />
<property name="initialSize" value="5" />
<property name="maxActive" value="10" />
<property name="maxWait" value="60000" />
<property name="poolPreparedStatements" value="true" />
</bean>
<bean id="ds2" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"
lazy-init="false">
<property name="driverClassName" value="${jdbc.ds2.driverClassName}" />
<property name="url" value="${jdbc.ds2.url}" />
<property name="username" value="${jdbc.ds2.username}" />
<property name="password" value="${jdbc.ds2.password}" />
<property name="initialSize" value="5" />
<property name="maxActive" value="10" />
<property name="maxWait" value="60000" />
<property name="poolPreparedStatements" value="true" />
</bean>
<!-- 动态数据源 -->
<bean id="dataSource" class="xxx.DynamicDataSource">
<property name="targetDataSources">
<map>
<entry key="ds1" value-ref="ds1" />
<entry key="ds2" value-ref="ds2" />
</map>
</property>
<property name="defaultTargetDataSource" ref="ds1" />
</bean>
<!-- 事务管理 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<tx:annotation-driven/>
<!-- myBatis配置 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="configLocation" value="classpath:mybatis-config.xml" />
<property name="dataSource" ref="dataSource" />
</bean>
<!-- DAO层由 MapperScannerConfigurer自动生成mapper bean -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="xxx.mapper" />
</bean>
因为每个Service目前只可能访问一个DataSource,所以在调用Service的时候,调用DataSourceContextHolder.setDataSourceType(key)(key可以为ds1,ds2),
就可以动态切换数据源了(当然最好用AOP思想,技术上spring + AspectJ,在每个Service需要的方法切上一刀),
而且对于spring的@Transactional事务管理是起作用的
OK,按照这种模式,如果Service可能访问多个库,就将DataSourceTransactionManager换成JtaTransactionManager
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager" /> <tx:annotation-driven transaction-manager="transactionManager" />
当然,Datasource换成JNDI获取
<!-- 创建数据源。 --> <bean id="ds1" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiName"> <value>ds1</value> </property> <property name="resourceRef"> <value>true</value> </property> </bean> <bean id="ds2" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiName"> <value>ds2</value> </property> <property name="resourceRef"> <value>true</value> </property> </bean>
在spring的@Transactional事务管理中,那是死活无法切换数据源
由于内容有点多,这个技术总结分为两部分。