spring 多資料來源配置
spring 多資料來源配置一般有兩種方案:
1、在spring專案啟動的時候直接配置兩個不同的資料來源,不同的sessionFactory。在dao 層根據不同業務自行選擇使用哪個資料來源的session來操作。
2、配置多個不同的資料來源,使用一個sessionFactory,在業務邏輯使用的時候自動切換到不同的資料來源,有一個種是在攔截器裡面根據不同的業務現切換到不同的datasource;有的會在業務層根據業務來自動切換。但這種方案在多執行緒併發的時候會出現一些問題,需要使用threadlocal等技術來實現多執行緒競爭切換資料來源的問題。
【本文暫時只討論第一種方案】
spring多事務配置主要體現在db配置這塊,配置不同的資料來源和不同的session
1、一下貼出 spring-db.xml配置
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd"> <bean id="test1DataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" > <property name="driverClassName" value="${database.test1.driverClassName}" /> <property name="url" value="${database.test1.url}" /> <property name="username" value="${database.test1.username}" /> <property name="password" value="${database.test1.password}" /> </bean> <bean id="test2DataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" > <property name="driverClassName" value="${database.test2.driverClassName}" /> <property name="url" value="${database.test2.url}" /> <property name="username" value="${database.test2.username}" /> <property name="password" value="${database.test2.password}" /> </bean> <bean id="test1SqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="test1DataSource" /> <property name="configLocation" value="classpath:mybatis/mybatis-config.xml"></property> <property name="mapperLocations" value="classpath*:mybatis/mapper/*.xml" /> </bean> <bean id="test2SqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="test2DataSource" /> <property name="configLocation" value="classpath:mybatis/mybatis-config.xml"></property> <property name="mapperLocations" value="classpath*:mybatis/mapper/*.xml" /> </bean> <bean id="test1TxManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="test1DataSource"></property> </bean> <bean id="test2TxManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="test2DataSource"></property> </bean> <tx:annotation-driven transaction-manager="test2TxManager" /> <tx:annotation-driven transaction-manager="test1TxManager" /> </beans>
2、dao層做了一個小的封裝,將不同的SqlSessionFactory 注入到 SessionFactory,通過BaseDao來做簡單的封裝,封裝不同庫的基本增刪改。dao實現層都整合於Basedao 這樣的話,實現可以根據自己需要來選擇不同的庫來操作不同的內容。
session工廠
package com.neo.dao;
import com.neo.entity.Entity;
public class BaseDao extends SessionFactory{
public void test1Update(Entity entity) {
this.getTest1Session().update(entity.getClass().getSimpleName()+".update", entity);
}
public void test2Update(Entity entity) {
this.getTest2Session().update(entity.getClass().getSimpleName()+".update", entity);
}
}
BaseDao
package com.neo.dao;
import com.neo.entity.Entity;
public class BaseDao extends SessionFactory{
public void test1Update(Entity entity) {
this.getTest1Session().update(entity.getClass().getSimpleName()+".update", entity);
}
public void test2Update(Entity entity) {
this.getTest2Session().update(entity.getClass().getSimpleName()+".update", entity);
}
}
以上的配置在多資料來源連線,正常的增刪改都是沒有問題的,但是遇到分散式的事務是就出問題:
測試程式碼:
package com.neo.service.impl;
import javax.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.neo.dao.UserDao;
import com.neo.dao.UserInformationsDao;
import com.neo.entity.UserEntity;
import com.neo.entity.UserInformationsEntity;
import com.neo.service.UserService;
@Service
public class UserServiceImpl implements UserService {
@Resource UserDao userDao;
@Resource UserInformationsDao userInformationsDao;
@Override
@Transactional
public void updateUserinfo() {
UserEntity user=new UserEntity();
user.setId(1);
user.setUserName("李四4");
UserInformationsEntity userInfo=new UserInformationsEntity();
userInfo.setUserId(1);
userInfo.setAddress("陝西4");
userDao.updateUser(user);
userInformationsDao.updateUserInformations(userInfo);
if(true){
throw new RuntimeException("test tx ");
}
}
}
在service新增事務後,更新完畢丟擲異常,test2更新進行了回滾,test1 資料更新沒有回滾。
解決方案新增分散式的事務,Atomikos和spring結合來處理。
Atomikos多資料來源的配置
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd"> <bean id="test1DataSource" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" destroy-method="close"> <property name="uniqueResourceName" value="test1"/> <property name="xaDataSourceClassName" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource"/> <property name="xaProperties"> <props> <prop key="url">${database.test1.url}</prop> <prop key="user">${database.test1.username}</prop> <prop key="password">${database.test1.password}</prop> </props> </property> <property name="minPoolSize" value="10" /> <property name="maxPoolSize" value="100" /> <property name="borrowConnectionTimeout" value="30" /> <property name="testQuery" value="select 1" /> <property name="maintenanceInterval" value="60" /> </bean> <bean id="test2DataSource" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" destroy-method="close"> <property name="uniqueResourceName" value="test2"/> <property name="xaDataSourceClassName" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource"/> <property name="xaProperties"> <props> <prop key="url">${database.test2.url}</prop> <prop key="user">${database.test2.username}</prop> <prop key="password">${database.test2.password}</prop> </props> </property> <property name="minPoolSize" value="10" /> <property name="maxPoolSize" value="100" /> <property name="borrowConnectionTimeout" value="30" /> <property name="testQuery" value="select 1" /> <property name="maintenanceInterval" value="60" /> </bean> <bean id="test1SqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="test1DataSource" /> <property name="configLocation" value="classpath:mybatis/mybatis-config.xml"></property> <property name="mapperLocations" value="classpath*:mybatis/mapper/*.xml" /> </bean> <bean id="test2SqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="test2DataSource" /> <property name="configLocation" value="classpath:mybatis/mybatis-config.xml"></property> <property name="mapperLocations" value="classpath*:mybatis/mapper/*.xml" /> </bean> <!-- 分散式事務 --> <bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager" init-method="init" destroy-method="close"> <property name="forceShutdown" value="true"/> </bean> <bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp"> <property name="transactionTimeout" value="300"/> </bean> <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"> <property name="transactionManager" ref="atomikosTransactionManager"/> <property name="userTransaction" ref="atomikosUserTransaction"/> </bean> <tx:annotation-driven/> </beans>
所有程式碼請參考這裡:
https://github.com/ityouknow/spring-examples