一个项目操作多个数据源的情况下,如果对事务进行管理:
分布式事务解决方案:jta + atomikos
结构目录:
只需要jta 目录即可,其余可忽略
1. 引入以下jar包:(前提:引入ssm需要的jar包)
<!-- 分布式事务 jta+ atomikos -->
<dependency>
<groupId>com.atomikos</groupId>
<artifactId>transactions-jdbc</artifactId>
<version>4.0.6</version>
</dependency>
<dependency>
<groupId>javax.transaction</groupId>
<artifactId>jta</artifactId>
<version>1.1</version>
</dependency>
2. 在resource目录中编写jta配置文件:jta.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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
">
<!-- jta事务管理器 -->
<bean class="org.springframework.transaction.jta.JtaTransactionManager" id="jtaTransactionManager">
<property name="transactionManager">
<bean class="com.atomikos.icatch.jta.UserTransactionManager" destroy-method="close" init-method="init">
<property name="forceShutdown" value="true">
</property></bean>
</property>
<property name="userTransaction">
<bean class="com.atomikos.icatch.jta.UserTransactionImp">
<property name="transactionTimeout" value="300">
</property></bean>
</property>
<!-- 允许设置隔离级别 -->
<property name="allowCustomIsolationLevels" value="true"/> <!-- 必须设置,否则程序出现异常 JtaTransactionManager does not support custom isolation levels by default -->
</bean>
<!-- 配置数据源 1 根据自己的数据库进行修改-->
<bean class="com.atomikos.jdbc.AtomikosDataSourceBean" destroy-method="close" id="jtaDataSource1" init-method="init">
<property name="uniqueResourceName" value="ds1"></property>
<property name="xaDataSourceClassName" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource"></property>
<property name="xaProperties">
<props>
<prop key="url">
jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=UTF-8
</prop>
<prop key="user">root</prop>
<prop key="password">123456</prop>
<prop key="pinGlobalTxToPhysicalConnection">true</prop>
</props>
</property>
<property name="minPoolSize" value="10"></property>
<property name="maxPoolSize" value="100"></property>
<property name="borrowConnectionTimeout" value="30"></property>
<property name="testQuery" value="select 1"></property>
<property name="maintenanceInterval" value="60"></property>
</bean>
<!-- 配置数据源 2 根据自己的数据库进行修改-->
<bean class="com.atomikos.jdbc.AtomikosDataSourceBean" destroy-method="close" id="jtaDataSource2" init-method="init">
<property name="uniqueResourceName" value="ds2"></property>
<property name="xaDataSourceClassName" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource"></property>
<property name="xaProperties">
<props>
<prop key="url">jdbc:mysql://localhost:3306/db_test?useUnicode=true&characterEncoding=UTF-8
</prop>
<prop key="user">root</prop>
<prop key="password">123456</prop>
<prop key="pinGlobalTxToPhysicalConnection">true</prop>
</props>
</property>
<property name="minPoolSize" value="10"></property>
<property name="maxPoolSize" value="100"></property>
<property name="borrowConnectionTimeout" value="30"></property>
<property name="testQuery" value="select 1"></property>
<property name="maintenanceInterval" value="60"></property>
</bean>
<!-- 让spring管理sqlsessionfactory 使用mybatis和spring整合包中的 -->
<bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sqlSessionFactory1">
<!-- 数据库连接池 -->
<property name="dataSource" ref="jtaDataSource1"></property>
<!-- 加载mybatis的全局配置文件 -->
<property name="configLocation" value="classpath:SqlMapConfig.xml"></property>
<!-- 扫描mapper文件 -->
<property name="mapperLocations" value="classpath:cn/com/admin/jta/test01/sql/*.xml"></property>
</bean>
<!-- 让spring管理sqlsessionfactory 使用mybatis和spring整合包中的 -->
<bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sqlSessionFactory2">
<!-- 数据库连接池 -->
<property name="dataSource" ref="jtaDataSource2"></property>
<!-- 加载mybatis的全局配置文件 -->
<property name="configLocation" value="classpath:SqlMapConfig.xml"></property>
<!-- 扫描mapper文件 -->
<property name="mapperLocations" value="classpath:cn/com/admin/jta/test02/sql/*.xml"></property>
</bean>
<!--指定mybatis的mapper文件的位置-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="cn.com.admin.jta.test01"></property>
<property name="sqlSessionFactory" ref="sqlSessionFactory1"></property>
</bean>
<!--指定mybatis的mapper文件的位置-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="cn.com.admin.jta.test02"></property>
<property name="sqlSessionFactory" ref="sqlSessionFactory2"></property>
</bean>
<!--2.配置事务属性 事务管理器为 jta -->
<tx:advice transaction-manager="jtaTransactionManager" id="txAdvice">
<tx:attributes>
<!-- 针对某名称的方法,配置传播行为和隔离级别属性 -->
<tx:method name="insert*" propagation="REQUIRED" isolation="READ_COMMITTED"/>
<tx:method name="query*" propagation="SUPPORTS" read-only="true" />
</tx:attributes>
</tx:advice>
<!-- 3.配置事务切点 ,以及把事务切入点及事务属性关联起来: 作用在哪些类的哪些方法上 -->
<aop:config>
<aop:pointcut expression="execution(* cn.com.admin.springlearn.jta.*.*(..))"
id="txPointCut"></aop:pointcut>
<!-- 切点和事务关联 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"></aop:advisor>
</aop:config>
<!-- 注入测试的bean -->
<bean id="jtaTestService" class="cn.com.admin.springlearn.jta.JTATestServiceImpl"></bean>
</beans>
3. 编写2个数据源对应包下的dao接口
自行建表测试
public interface BusinessTest1Dao {
/**
* 添加书本
* @param business
*/
@Insert(value={"insert into business(NAME,price,num) values(#{business.name},#{business.price},#{business.num})"})
void insertBook(@Param("business") Business business);
}
public interface BusinessTest2Dao {
/**
* 添加书本
* @param business
*/
@Insert(value={"insert into business(NAME,price,num) values(#{business.name},#{business.price},#{business.num})"})
void insertBook(@Param("business") Business business);
}
4. 测试的bean
public class JTATestServiceImpl {
@Autowired
private BusinessTest1Dao businessTest1Dao;
@Autowired
private BusinessTest2Dao businessTest2Dao;
/**
* 同时向两个数据源插入数据
*/
public void insertBusiness() {
Business business = new Business();
business.setName("张三");
business.setNum(123);
business.setPrice(123);
businessTest1Dao.insertBook(business);
businessTest2Dao.insertBook(business);
int i = 1/0;//回滚
}
5. 测试: 由于出现异常,两个数据源对应的表都插不进去数据,实现了异常同时回滚
public class JTATest {
/**
* 初始化spring容器
*/
private static ApplicationContext ap = new ClassPathXmlApplicationContext("classpath:jta.xml");
public static void main(String[] args) {
JTATestServiceImpl jta = (JTATestServiceImpl) ap.getBean("jtaTestService");
jta.insertBusiness();
//查看数据库添加情况
System.out.println("插入完成");
}
}