代码已上传到Github,有兴趣的同学可以下载来看看:https://github.com/ylw-github/SpringBoot-Transaction-Demo
1. SpringBoot事务管理
SpringBoot默认集成事务,只主要在方法上加上@Transactional
即可
操作比较简单,此处不再详述
2. SpringBoot分布式事务管理
可以使用springboot+jta+atomikos 进行分布式事务管理,下面来详细介绍集成的步骤:
2.1 添加mave依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jta-atomikos</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- 测试 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.1.1</version>
</dependency>
<!-- mysql 依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- springboot-web组件 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
2.2 配置application.properties
# Mysql 1
mysql.datasource.test1.url = jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8
mysql.datasource.test1.username = root
mysql.datasource.test1.password = 123456
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 2
mysql.datasource.test2.url =jdbc:mysql://localhost:3306/test02?useUnicode=true&characterEncoding=utf-8
mysql.datasource.test2.username =root
mysql.datasource.test2.password =123456
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
2.3 ConfigurationProperties
总共要写两个配置类DBConfig1和DBConfig2:
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;
//getter setter...
//DBConfig2///
(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;
//getter setter...
2.4 配置数据源
配置的时候注意,每个扫描mapper的包位置不一致。
数据源1:
package com.ylw.datasource;
// basePackages 最好分开配置 如果放在同一个文件夹可能会报错
(basePackages = "com.ylw.mapper.test01", sqlSessionTemplateRef = "testSqlSessionTemplate")
public class MyBatisConfig1 {
// 配置数据源
(name = "testDataSource")
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);
// 将本地事务注册到创 Atomikos全局事务
AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
xaDataSource.setXaDataSource(mysqlXaDataSource);
xaDataSource.setUniqueResourceName("testDataSource");
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;
}
(name = "testSqlSessionFactory")
public SqlSessionFactory testSqlSessionFactory( ("testDataSource") DataSource dataSource)
throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
return bean.getObject();
}
(name = "testSqlSessionTemplate")
public SqlSessionTemplate testSqlSessionTemplate(
("testSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
数据源2:
(basePackages = "com.ylw.mapper.test02", sqlSessionTemplateRef = "test2SqlSessionTemplate")
public class MyBatisConfig2 {
// 配置数据源
(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;
}
(name = "test2SqlSessionFactory")
public SqlSessionFactory testSqlSessionFactory( ("test2DataSource") DataSource dataSource)
throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
return bean.getObject();
}
(name = "test2SqlSessionTemplate")
public SqlSessionTemplate testSqlSessionTemplate(
("test2SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
2.5 配置Mapper
注意包结构不一样。
UserMapperTest01:
package com.ylw.mapper.test01;
public interface UserMapperTest01 {
// 查询语句
("SELECT * FROM t_user WHERE name = #{name}")
User findByName( ("name") String name);
// 添加
("INSERT INTO t_user(uuid,name, age) VALUES(#{uuid},#{name}, #{age})")
int insert( ("uuid") String uuid, ("name") String name, ("age") Integer age);
}
UserMapperTest02:
package com.ylw.mapper.test02;
public interface UserMapperTest02 {
// 查询语句
("SELECT * FROM t_user WHERE name = #{name}")
User findByName( ("name") String name);
// 添加
("INSERT INTO t_user(uuid,name, age) VALUES(#{uuid},#{name}, #{age})")
int insert( ("uuid") String uuid, ("name") String name, ("age") Integer age);
}
2.6 业务类
UserServiceTest01:
package com.ylw.service.test01;
public class UserServiceTest01 {
private UserMapperTest01 userMapperTest01;
public int insertUser(String name, Integer age) {
int insertUserResult = userMapperTest01.insert(UUID.randomUUID().toString(), name, age);
System.out.println("######insertUserResult:{}##########-> " + insertUserResult);
//int i = 1 / age;
// 验证事务开启
return insertUserResult;
}
}
UserServiceTest02:
package com.ylw.service.test02;
public class UserServiceTest02 {
private UserMapperTest02 userMapperTest02;
private UserMapperTest01 userMapperTest01;
public int insertUser(String name, Integer age) {
int insertUserResult = userMapperTest02.insert(UUID.randomUUID().toString(), name, age);
System.out.println("######insertUserResult:{}########## -> " + insertUserResult);
// 怎么样验证事务开启成功!~
//int i = 1 / age;
return insertUserResult;
}
()
public int insertUserTest01AndTest02(String name, Integer age) {
// 传统分布式事务解决方案 jta+atomikos 注册同一个全局事务中
// 第一个数据源
int insertUserResult01 = userMapperTest01.insert(UUID.randomUUID().toString(), name, age);
// 第二个数据源
int insertUserResult02 = userMapperTest02.insert(UUID.randomUUID().toString(), name, age);
//int i = 1 / 0;
int result = insertUserResult01 + insertUserResult02;
// test01入库 test02回滚
return result;
}
}
2.7 启动类与Controller
启动类,要配置EnableConfigurationProperties:
// 开启读取配置文件
(value = { DBConfig1.class, DBConfig2.class })
public class MybatisApp03 {
public static void main(String[] args) {
SpringApplication.run(MybatisApp03.class, args);
}
}
controller:
public class MybatisMultilDataSourceController {
private UserServiceTest01 userServiceTest01;
private UserServiceTest02 userServiceTest02;
(value = "/insertUserTest1" ,method = RequestMethod.GET)
public Integer insertUserTest1(String name, Integer age) {
return userServiceTest01.insertUser(name, age);
}
(value = "/insertUserTest2",method = RequestMethod.GET)
public Integer insertUserTest2(String name, Integer age) {
return userServiceTest02.insertUser(name, age);
}
(value ="/insertUserTest01AndTest02",method = RequestMethod.GET)
public int insertUserTest01AndTest02(String name, Integer age) {
return userServiceTest02.insertUserTest01AndTest02(name, age);
}
}
2.8 测试
在浏览器打开三个页面,地址分别是:
- http://localhost:8080/insertUserTest1?name=name1&age=10
- http://localhost:8080/insertUserTest2?name=name2&age=18
- http://localhost:8080/insertUserTest01AndTest02?name=name1AndName2&age=19
运行后,会发现两个数据库均添加了数据: