代码已上传到Github,有兴趣的同学可以下载来看看:https://github.com/ylw-github/SpringBoot-Transaction-Demo
1. SpringBoot事务管理
SpringBoot默认集成事务,只主要在方法上加上@Transactional
即可
操作比较简单,此处不再详述,可以参考:https://blog.csdn.net/justry_deng/article/details/80828180
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:
@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; //getter setter... //DBConfig2/// @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; //getter setter...
2.4 配置数据源
配置的时候注意,每个扫描mapper的包位置不一致。
数据源1:
package com.ylw.datasource; @Configuration // basePackages 最好分开配置 如果放在同一个文件夹可能会报错 @MapperScan(basePackages = "com.ylw.mapper.test01", sqlSessionTemplateRef = "testSqlSessionTemplate") public class MyBatisConfig1 { // 配置数据源 @Bean(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; } @Bean(name = "testSqlSessionFactory") public SqlSessionFactory testSqlSessionFactory(@Qualifier("testDataSource") DataSource dataSource) throws Exception { SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); bean.setDataSource(dataSource); return bean.getObject(); } @Bean(name = "testSqlSessionTemplate") public SqlSessionTemplate testSqlSessionTemplate( @Qualifier("testSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception { return new SqlSessionTemplate(sqlSessionFactory); } }
数据源2:
@Configuration @MapperScan(basePackages = "com.ylw.mapper.test02", sqlSessionTemplateRef = "test2SqlSessionTemplate") public class MyBatisConfig2 { // 配置数据源 @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); } }
2.5 配置Mapper
注意包结构不一样。
UserMapperTest01:
package com.ylw.mapper.test01; public interface UserMapperTest01 { // 查询语句 @Select("SELECT * FROM t_user WHERE name = #{name}") User findByName(@Param("name") String name); // 添加 @Insert("INSERT INTO t_user(uuid,name, age) VALUES(#{uuid},#{name}, #{age})") int insert(@Param("uuid") String uuid,@Param("name") String name, @Param("age") Integer age); }
UserMapperTest02:
package com.ylw.mapper.test02; public interface UserMapperTest02 { // 查询语句 @Select("SELECT * FROM t_user WHERE name = #{name}") User findByName(@Param("name") String name); // 添加 @Insert("INSERT INTO t_user(uuid,name, age) VALUES(#{uuid},#{name}, #{age})") int insert(@Param("uuid") String uuid,@Param("name") String name, @Param("age") Integer age); }
2.6 业务类
UserServiceTest01:
package com.ylw.service.test01; @Service public class UserServiceTest01 { @Autowired private UserMapperTest01 userMapperTest01; @Transactional 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; @Service public class UserServiceTest02 { @Autowired private UserMapperTest02 userMapperTest02; @Autowired private UserMapperTest01 userMapperTest01; @Transactional 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; } @Transactional() 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:
// 开启读取配置文件 @EnableConfigurationProperties(value = { DBConfig1.class, DBConfig2.class }) @SpringBootApplication public class MybatisApp03 { public static void main(String[] args) { SpringApplication.run(MybatisApp03.class, args); } }
controller:
@RestController public class MybatisMultilDataSourceController { @Autowired private UserServiceTest01 userServiceTest01; @Autowired private UserServiceTest02 userServiceTest02; @RequestMapping(value = "/insertUserTest1" ,method = RequestMethod.GET) public Integer insertUserTest1(String name, Integer age) { return userServiceTest01.insertUser(name, age); } @RequestMapping(value = "/insertUserTest2",method = RequestMethod.GET) public Integer insertUserTest2(String name, Integer age) { return userServiceTest02.insertUser(name, age); } @RequestMapping(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
运行后,会发现两个数据库均添加了数据:
总结