Spring Boot 整合事物管理
Spring Boot 默认集成事物,只主要在方法上加上@Transactional即可
我的上一篇博文:Spring Boot 整合多数据源,里面讲解了Spring Boot整合多数据源,我们根据里面的例子来讲解Spring Boot多数据源的事务管理。
首先我们来改造之前我们的代码:

我们在User1Service中引入了数据源2的user2Mapper。然后我们写了一个方法,先执行数据源1的插入操作,然后在执行数据源2的插入操作,但是在数据源2操作之前我们让其报错,然后我们给我们的方法加入事务,看看在这种情况下会出现什么问题?
然后改造我们的IndexController:

然后执行看看数据库的情况。



发现数据库都没有生成新的数据,这样我们的事务的确起作用了,但是这样我们为什么还要提到多数据源的事务或者是分布式事务管理呢?
我们现在稍微改改我们的代码,就能发现有很大的不同。

这次我们只是把操作的前后顺序换个位置,先操作数据源2然后在操作数据源1


结果发现事务居然不起作用了,所以这才是我们为什么要提到多数据源的事务管理。
为什么我们的事务不起作用了,只不过是调了个位置而已。。
下面是我的个人理解,如果有不对的地方欢迎指正,谢谢!
这是因为这个项目使用了多个数据源,使用了@Transactional的事务注解只对第一个起作用,就是我们配置了@Primary注解的数据源,即Spring Boot默认的那个dataSource
因为我们的@Primary配置在了数据源1,所以默认事务注解只对数据源1起作用,所以我们先操作数据源1,后操作数据源2的时候,会开启数据源1的事务,方法结束之前出现了错误,所以事务会回滚。而我们后面操作数据源2再操作数据源1的时候,在数据源2中并不能启动数据源1的事务,所以事务没有启动。
spring2.5以前,对分布式多数据源不支持,但是在spring3.0之后针对这个问题的做出了解决方案,具体你们可以看看这篇博文:被忽略的Spring3小改进—支持多数据源的@Transactional事务注解
由于本文使用的不是spring3.0,所以我们这里给出了另一种解决方案:使用springboot+jta+atomikos 分布式事物管理案
Spring Boot+jta+atomikos 分布式事物管理
新增配置文件信息
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jta-atomikos</artifactId>
</dependency>
新增配置文件信息
# Mysql 1
mysql.datasource.test.url = jdbc:mysql://localhost:3306/test01?useUnicode=true&characterEncoding=utf-8
mysql.datasource.test1.username = root
mysql.datasource.test1.password = root
mysql.datasource.test1.minPoolSize = 3
mysql.datasource.test1.maxPoolSize = 25
mysql.datasource.test1.maxLifetime = 20000
mysql.datasource.test1.borrowConnectionTimeout = 30
mysql.datasource.test1.loginTimeout = 30
mysql.datasource.test1.maintenanceInterval = 60
mysql.datasource.test1.maxIdleTime = 60
mysql.datasource.test1.testQuery = select 1
# Mysql 2
mysql.datasource.test2.url =jdbc:mysql://localhost:3306/test02?useUnicode=true&characterEncoding=utf-8
mysql.datasource.test2.username =root
mysql.datasource.test2.password =root
mysql.datasource.test2.minPoolSize = 3
mysql.datasource.test2.maxPoolSize = 25
mysql.datasource.test2.maxLifetime = 20000
mysql.datasource.test2.borrowConnectionTimeout = 30
mysql.datasource.test2.loginTimeout = 30
mysql.datasource.test2.maintenanceInterval = 60
mysql.datasource.test2.maxIdleTime = 60
mysql.datasource.test2.testQuery = select 1
读取配置文件信息
创建两个DBConfig类

package com.cc.springboot.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "mysql.datasource.test1")
public class DBConfig1 {
private String url;
private String username;
private String password;
private int minPoolSize;
private int maxPoolSize;
private int maxLifetime;
private int borrowConnectionTimeout;
private int loginTimeout;
private int maintenanceInterval;
private int maxIdleTime;
private String testQuery;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getMinPoolSize() {
return minPoolSize;
}
public void setMinPoolSize(int minPoolSize) {
this.minPoolSize = minPoolSize;
}
public int getMaxPoolSize() {
return maxPoolSize;
}
public void setMaxPoolSize(int maxPoolSize) {
this.maxPoolSize = maxPoolSize;
}
public int getMaxLifetime() {
return maxLifetime;
}
public void setMaxLifetime(int maxLifetime) {
this.maxLifetime = maxLifetime;
}
public int getBorrowConnectionTimeout() {
return borrowConnectionTimeout;
}
public void setBorrowConnectionTimeout(int borrowConnectionTimeout) {
this.borrowConnectionTimeout = borrowConnectionTimeout;
}
public int getLoginTimeout() {
return loginTimeout;
}
public void setLoginTimeout(int loginTimeout) {
this.loginTimeout = loginTimeout;
}
public int getMaintenanceInterval() {
return maintenanceInterval;
}
public void setMaintenanceInterval(int maintenanceInterval) {
this.maintenanceInterval = maintenanceInterval;
}
public int getMaxIdleTime() {
return maxIdleTime;
}
public void setMaxIdleTime(int maxIdleTime) {
this.maxIdleTime = maxIdleTime;
}
public String getTestQuery() {
return testQuery;
}
public void setTestQuery(String testQuery) {
this.testQuery = testQuery;
}
}
package com.cc.springboot.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "mysql.datasource.test2")
public class DBConfig2 {
private String url;
private String username;
private String password;
private int minPoolSize;
private int maxPoolSize;
private int maxLifetime;
private int borrowConnectionTimeout;
private int loginTimeout;
private int maintenanceInterval;
private int maxIdleTime;
private String testQuery;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getMinPoolSize() {
return minPoolSize;
}
public void setMinPoolSize(int minPoolSize) {
this.minPoolSize = minPoolSize;
}
public int getMaxPoolSize() {
return maxPoolSize;
}
public void setMaxPoolSize(int maxPoolSize) {
this.maxPoolSize = maxPoolSize;
}
public int getMaxLifetime() {
return maxLifetime;
}
public void setMaxLifetime(int maxLifetime) {
this.maxLifetime = maxLifetime;
}
public int getBorrowConnectionTimeout() {
return borrowConnectionTimeout;
}
public void setBorrowConnectionTimeout(int borrowConnectionTimeout) {
this.borrowConnectionTimeout = borrowConnectionTimeout;
}
public int getLoginTimeout() {
return loginTimeout;
}
public void setLoginTimeout(int loginTimeout) {
this.loginTimeout = loginTimeout;
}
public int getMaintenanceInterval() {
return maintenanceInterval;
}
public void setMaintenanceInterval(int maintenanceInterval) {
this.maintenanceInterval = maintenanceInterval;
}
public int getMaxIdleTime() {
return maxIdleTime;
}
public void setMaxIdleTime(int maxIdleTime) {
this.maxIdleTime = maxIdleTime;
}
public String getTestQuery() {
return testQuery;
}
public void setTestQuery(String testQuery) {
this.testQuery = testQuery;
}
}


创建多数据源

package com.cc.springboot.datasource
import java.sql.SQLException
import javax.sql.DataSource
import org.apache.ibatis.session.SqlSessionFactory
import org.mybatis.spring.SqlSessionFactoryBean
import org.mybatis.spring.SqlSessionTemplate
import org.mybatis.spring.annotation.MapperScan
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Primary
import com.atomikos.jdbc.AtomikosDataSourceBean
import com.cc.springboot.config.DBConfig1
import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource
@Configuration
//basePackages 最好分开配置 如果放在同一个文件夹可能会报错
@MapperScan(basePackages = "com.cc.springboot.test01", sqlSessionTemplateRef = "test1SqlSessionTemplate")
public class TestMyBatisConfig1 {
// 配置数据源
@Primary
@Bean(name = "test1DataSource")
public DataSource testDataSource(DBConfig1 testConfig) throws SQLException {
MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource()
mysqlXaDataSource.setUrl(testConfig.getUrl())
mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true)
mysqlXaDataSource.setPassword(testConfig.getPassword())
mysqlXaDataSource.setUser(testConfig.getUsername())
mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true)
AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean()
xaDataSource.setXaDataSource(mysqlXaDataSource)
xaDataSource.setUniqueResourceName("test1DataSource")
xaDataSource.setMinPoolSize(testConfig.getMinPoolSize())
xaDataSource.setMaxPoolSize(testConfig.getMaxPoolSize())
xaDataSource.setMaxLifetime(testConfig.getMaxLifetime())
xaDataSource.setBorrowConnectionTimeout(testConfig.getBorrowConnectionTimeout())
xaDataSource.setLoginTimeout(testConfig.getLoginTimeout())
xaDataSource.setMaintenanceInterval(testConfig.getMaintenanceInterval())
xaDataSource.setMaxIdleTime(testConfig.getMaxIdleTime())
xaDataSource.setTestQuery(testConfig.getTestQuery())
return xaDataSource
}
@Bean(name = "test1SqlSessionFactory")
public SqlSessionFactory testSqlSessionFactory(@Qualifier("test1DataSource") DataSource dataSource)
throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean()
bean.setDataSource(dataSource)
return bean.getObject()
}
@Bean(name = "test1SqlSessionTemplate")
public SqlSessionTemplate testSqlSessionTemplate(
@Qualifier("test1SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory)
}
}
package com.cc.springboot.datasource
import java.sql.SQLException
import javax.sql.DataSource
import org.apache.ibatis.session.SqlSessionFactory
import org.mybatis.spring.SqlSessionFactoryBean
import org.mybatis.spring.SqlSessionTemplate
import org.mybatis.spring.annotation.MapperScan
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Primary
import com.atomikos.jdbc.AtomikosDataSourceBean
import com.cc.springboot.config.DBConfig2
import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource
@Configuration
//basePackages 最好分开配置 如果放在同一个文件夹可能会报错
@MapperScan(basePackages = "com.cc.springboot.test02", sqlSessionTemplateRef = "test2SqlSessionTemplate")
public class TestMyBatisConfig2 {
// 配置数据源
@Bean(name = "test2DataSource")
public DataSource testDataSource(DBConfig2 testConfig) throws SQLException {
MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource()
mysqlXaDataSource.setUrl(testConfig.getUrl())
mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true)
mysqlXaDataSource.setPassword(testConfig.getPassword())
mysqlXaDataSource.setUser(testConfig.getUsername())
mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true)
AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean()
xaDataSource.setXaDataSource(mysqlXaDataSource)
xaDataSource.setUniqueResourceName("test2DataSource")
xaDataSource.setMinPoolSize(testConfig.getMinPoolSize())
xaDataSource.setMaxPoolSize(testConfig.getMaxPoolSize())
xaDataSource.setMaxLifetime(testConfig.getMaxLifetime())
xaDataSource.setBorrowConnectionTimeout(testConfig.getBorrowConnectionTimeout())
xaDataSource.setLoginTimeout(testConfig.getLoginTimeout())
xaDataSource.setMaintenanceInterval(testConfig.getMaintenanceInterval())
xaDataSource.setMaxIdleTime(testConfig.getMaxIdleTime())
xaDataSource.setTestQuery(testConfig.getTestQuery())
return xaDataSource
}
@Bean(name = "test2SqlSessionFactory")
public SqlSessionFactory testSqlSessionFactory(@Qualifier("test2DataSource") DataSource dataSource)
throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean()
bean.setDataSource(dataSource)
return bean.getObject()
}
@Bean(name = "test2SqlSessionTemplate")
public SqlSessionTemplate testSqlSessionTemplate(
@Qualifier("test2SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory)
}
}
把DataSource1和DataSource2都交给Atomikos管理
启动加载配置

测试



发现结果事务成功启动了。 |