
正文
springboot+atomikos+多数据源管理事务(mysql 8.0)
提示:扫一扫查出行【扫一扫了解最新限行尾号】
复制提示
jta:Java Transaction API,即是java中对事务处理的api 即 api即是接口的意思
atomikos:Atomikos TransactionsEssentials 是一个为Java平台提供增值服务的并且开源类事务管理器
1.结构
2.pom依赖
我这里使用本地数据库是mysql8,
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<!--<version>2.0.0.RELEASE</version>-->
<version>2.1.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties> <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.0</version><!-- 1.3.0以上的版本没有@MapperScan以及@Select注解 -->
</dependency>
<!-- automatic+jta的分布式事务管理 -->
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-jta-atomikos -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jta-atomikos</artifactId>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<!--boot 2.1默认 mysql8的版本; boot 2.0默认mysql5版本-->
<version>8.0.13</version>
<!--<version>5.1.46</version>-->
<!--<scope>runtime</scope>-->
</dependency> <dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
<version>1.18.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency> </dependencies>
3.创建本地数据库+表
4.application.yml
server:
port: 8080
servlet:
# # 项目contextPath
context-path: /manyDatasource spring:
application:
name: manyDatasource
datasource:
# spring.datasource.test1
# druid:
test1:
# jdbc-url,url,jdbcurl哪个合适用哪个
jdbcurl: jdbc:mysql://localhost:3306/test1?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8
username: root
password: 123456
initial-size: 1
min-idle: 1
max-active: 20
test-on-borrow: true
# driver-class-name: com.mysql.jdbc.Driver
# 下面是最新的mysql8版本推荐的驱动
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
# 下面是另外加的配置数据源的参数
minPoolSize: 3
maxPoolSize: 25
maxLifetime: 20000
borrowConnectionTimeout: 30
loginTimeout: 30
maintenanceInterval: 60
maxIdleTime: 60 test2:
jdbcurl: jdbc:mysql://localhost:3306/test2?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
minPoolSize: 3
maxPoolSize: 25
maxLifetime: 20000
borrowConnectionTimeout: 30
loginTimeout: 30
maintenanceInterval: 60
maxIdleTime: 60 mybatis:
mapper-locations: classpath:mapper/*.xml #设置静态资源路径,多个以逗号分隔
spring.resources.static-locations: classpath:static/,file:static/ # 日志配置
logging:
level:
czs: debug
org.springframework: WARN
org.spring.springboot.dao: debug
5.实体类
ps.使用lombok插件挺方便的~ id数据库主键自增
@Data
public class User {
private Integer id;
private String name;
private long age;
}
6.mapper接口
UserMapper1:
public interface UserMapper1 {
// 查询语句
@Select("SELECT * FROM users WHERE NAME = #{name}")
User findByName(@Param("name") String name);
// 添加
@Insert("INSERT INTO users(NAME, AGE) VALUES(#{name}, #{age})")
int insert(@Param("name") String name, @Param("age") Integer age);
}
UserMapper2:
public interface UserMapper2 {
// 查询语句
@Select("SELECT * FROM users WHERE NAME = #{name}")
User findByName(@Param("name") String name);
// 添加
@Insert("INSERT INTO users(NAME, AGE) VALUES(#{name}, #{age})")
int insert(@Param("name") String name, @Param("age") Integer age);
}
7.service
ManyService1:
@Service
public class ManyService1 { @Autowired
private UserMapper1 userMapper1;
@Autowired
private UserMapper2 userMapper2; /* @Transactional(transactionManager = "test1TransactionManager",rollbackFor = Exception.class)
public int insert(String name, Integer age) {
int i = userMapper1.insert(name, age);
System.out.println("userMapper1.insert结束~ :" + i);
// int a = 1 / 0;//手动异常
return i;
}*/ // 开启事务,由于使用jta+atomikos解决分布式事务,所以此处不必再指定事务
@Transactional
public int insert(String name, Integer age) {
int insert = userMapper1.insert(name, age);
//int i = 1 / age;// 赋值age为0故意引发事务
return insert;
} //http://localhost:8080/manyDatasource/insertDb1AndDb2?name=tom3&age=2
// 开启事务,由于使用jta+atomikos解决分布式事务,所以此处不必再指定事务
@Transactional
public int insertDb1AndDb2(String name, Integer age) {
int insert = userMapper1.insert(name, age);
int insert2 = userMapper2.insert(name, age);
int i = 1 / age;// 赋值age为0故意引发事务
return insert + insert2;
} }
ManyService2:
@Service
public class ManyService2 { @Autowired
private UserMapper2 userMapper2; @Transactional(transactionManager = "test2TransactionManager",rollbackFor = Exception.class)
public int insert(String name, Integer age) {
int i = userMapper2.insert(name, age);
System.out.println("userMapper2.insert结束~ :" + null);
int a = 1 / 0;//手动异常
return i;
} }
8.Controller
@RestController
public class ManyController { @Autowired
private ManyService1 manyService1; @Resource
private ManyService2 manyService2; @RequestMapping(value = "datasource1")
public int datasource1(String name, Integer age) {
return manyService1.insert(name, age);
} @RequestMapping(value = "datasource2")
public int datasource2(String name, Integer age) {
return manyService2.insert(name, age);
} /**
* @Param:
* @Description: 这里测试两个service两个数据源的事务(不加上atomikos插件的情况下测试,
*使用DataSource1Config和DataSource2Config 两个配置类, 关闭DBConfig1, DBConfig2和MyBatisConfig1, MyBatisConfig1两个类)
* @Author: zyf 2019/5/10
*/
//http://localhost:8080/manyDatasource/testManyTrans?name=tom4&age=2
@RequestMapping(value = "testManyTrans")
public int testManyTrans(String name, Integer age) {
int i = 0;
int i1 = manyService1.insert(name, age);
System.out.println("manyService1.insert :" + i1); /*
第二个事务中会手动造成一个异常~,
但是第一个事务执行完毕了,保存到了数据库
*/
int i2 = manyService2.insert(name, age);
System.out.println("manyService2.insert :" + i2);
return i;
} /**
* @Param:
* @Description: 这里测试使用atomikos插件测试多数据源事务
* @Author: zyf 2019/5/10
*/
//http://localhost:8080/manyDatasource/insertDb1AndDb2?name=tom5&age=2
//http://localhost:8080/manyDatasource/insertDb1AndDb2?name=tom6&age=0 //测试除数为0后的事务管理
@RequestMapping(value = "insertDb1AndDb2")
public int insertDb1AndDb2(String name, Integer age) {
return manyService1.insertDb1AndDb2(name, age);
} }
9.配置数据源(*******重点总是在最后********)
DBConfig1:
@Data
@ConfigurationProperties(prefix = "spring.datasource.test1") // 注意这个前缀要和application.yml文件的前缀一样
public class DBConfig1 {
// @Value("${mysql.datasource.test1.jdbcurl}")
//@Value("${jdbcurl}")
private String jdbcurl;
//private String url;
// 比如这个url在properties中是这样子的mysql.datasource.test1.username = root
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;
}
DBConfig2:
@Data
@ConfigurationProperties(prefix = "spring.datasource.test2")// 注意这个前缀要和application.yml文件的前缀一样
public class DBConfig2 {
//@Value("${spring.datasource.test2.jdbcurl}")
//@Value("${jdbcurl}")
//private String url;
private String jdbcurl;
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; }
上面两个配置类作用: 将application.yml配置文件中配置自动封装到实体类字段中,然后赋值给atomikos类型的数据源.(下面两个具体配置数据源)
MyBatisConfig1:
// 配置数据源
//@Bean(name = "testDataSource") //test1DataSource
@Bean(name = "test1DataSource") //test1DataSource
public DataSource testDataSource(DBConfig1 testConfig) throws SQLException {
MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
//mysqlXaDataSource.setUrl(testConfig.getUrl());
mysqlXaDataSource.setUrl(testConfig.getJdbcurl());
mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
mysqlXaDataSource.setPassword(testConfig.getPassword());
mysqlXaDataSource.setUser(testConfig.getUsername());
mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true); // 将本地事务注册到创 Atomikos全局事务
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);
}
MyBatisConfig2 :
@Configuration
@MapperScan(basePackages = "czs.mapper2", sqlSessionTemplateRef = "test2SqlSessionTemplate")
public class MyBatisConfig2 { // 配置数据源
@Bean(name = "test2DataSource")
public DataSource testDataSource(DBConfig2 testConfig) throws SQLException {
MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
//mysqlXaDataSource.setUrl(testConfig.getUrl());
mysqlXaDataSource.setUrl(testConfig.getJdbcurl());
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);
}
}
10.测试
http://localhost:8080/manyDatasource/insertDb1AndDb2?name=tom5&age=2
结果: test1和test2数据库都插入数据~
http://localhost:8080/manyDatasource/insertDb1AndDb2?name=tom6&age=0 (两个insert操作后,手动异常)
结果: test1和test2数据库都未插入数据~
GitHub传送门: https://github.com/ColoZhu/springbootmanyDatasource
参考出处: https://blog.csdn.net/qq_36138324/article/details/81612890







