摘要:这个技术本来应该是过时的技术,但是最近的项目比较棘手,时间又比较赶,临时使用jta-atomikos
多数据源事务,下面给大家分享如何快速接入jta-atomikos
多数据源的分布式事务。不推荐生产环境长时间使用,临时解决问题可以使用。
项目简介
项目基于
SpringBoot + mybatis-plus + jta-atomikos + mysql
,使用传统的MVC
风格代码做案例,写入user
用户库和order
订单库。
项目开始
创建数据库和表
创建jta-user
库
sql
CREATE TABLE `userinfo` (
`id` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`username` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
创建jta-order
库
sql
CREATE TABLE `order_info` (
`id` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`description` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
代码
pom.xml
xml
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.11</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jta-atomikos</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
启动类JtaApplication
arduino
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class JtaApplication {
public static void main(String[] args) {
SpringApplication.run(JtaApplication.class, args);
}
}
多数据源相关配置
user
库
实体和
mapper
接口我就不写出来了很简单
java
@Configuration
@MapperScan(basePackages = UserDataSourceConfig.PACKAGE, sqlSessionFactoryRef = "userSqlSessionFactory")
@Slf4j
public class UserDataSourceConfig {
protected static final String PACKAGE = "com.huzhihui.jta.infrastructure.user.mapper";
@Bean(name = "userDataSourceProperties")
@ConfigurationProperties(prefix = "spring.datasource.jta.user")
public DataSourceProperties userDataSourceProperties() {
return new DataSourceProperties();
}
@Primary
@Bean(name = "userDataSource")
public DataSource userDataSource(@Qualifier("userDataSourceProperties") DataSourceProperties dataSourceProperties) {
DruidXADataSource druidXADataSource = new DruidXADataSource();
druidXADataSource.setDriverClassName(dataSourceProperties.getDriverClassName());
druidXADataSource.setUrl(dataSourceProperties.getUrl());
druidXADataSource.setUsername(dataSourceProperties.getUsername());
druidXADataSource.setPassword(dataSourceProperties.getPassword());
AtomikosDataSourceBean atomikosDataSourceBean = new AtomikosDataSourceBean();
atomikosDataSourceBean.setXaDataSource(druidXADataSource);
atomikosDataSourceBean.setUniqueResourceName("userDataSource");
atomikosDataSourceBean.setPoolSize(5);
return atomikosDataSourceBean;
}
@Primary
@Bean(name = "userSqlSessionFactory")
public SqlSessionFactory userSqlSessionFactory(@Qualifier("userDataSource") DataSource dataSource) {
MybatisSqlSessionFactoryBean sessionFactory = new MybatisSqlSessionFactoryBean();
try{
sessionFactory.setDataSource(dataSource);
return sessionFactory.getObject();
} catch (Exception e) {
log.error("",e);
}
return null;
}
@Primary
@Bean(name = "userSqlSessionTemplate")
public SqlSessionTemplate organizationSqlSessionTemplate(@Qualifier("userSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
/**
* 事务配置,只需要配置一个即可
* @return
* @throws Exception
*/
@Bean(name = "transactionManager")
@Primary
public JtaTransactionManager regTransactionManager() throws Exception{
UserTransactionManager userTransactionManager = new UserTransactionManager();
userTransactionManager.setTransactionTimeout(3600);
UserTransaction userTransaction = new UserTransactionImp();
userTransaction.setTransactionTimeout(3600);
JtaTransactionManager jtaTransactionManager = new JtaTransactionManager(userTransaction, userTransactionManager);
jtaTransactionManager.setDefaultTimeout(3600);
return jtaTransactionManager;
}
}
order
库
实体和
mapper
接口我就不写出来了很简单
less
@Configuration
@MapperScan(basePackages = OrderDataSourceConfig.PACKAGE, sqlSessionFactoryRef = "orderSqlSessionFactory")
@Slf4j
public class OrderDataSourceConfig {
protected static final String PACKAGE = "com.huzhihui.jta.infrastructure.order.mapper";
@Bean(name = "orderDataSourceProperties")
@ConfigurationProperties(prefix = "spring.datasource.jta.order")
public DataSourceProperties orderDataSourceProperties() {
return new DataSourceProperties();
}
@Bean(name = "orderDataSource")
public DataSource orderDataSource(@Qualifier("orderDataSourceProperties") DataSourceProperties dataSourceProperties) {
DruidXADataSource druidXADataSource = new DruidXADataSource();
druidXADataSource.setDriverClassName(dataSourceProperties.getDriverClassName());
druidXADataSource.setUrl(dataSourceProperties.getUrl());
druidXADataSource.setUsername(dataSourceProperties.getUsername());
druidXADataSource.setPassword(dataSourceProperties.getPassword());
AtomikosDataSourceBean atomikosDataSourceBean = new AtomikosDataSourceBean();
atomikosDataSourceBean.setXaDataSource(druidXADataSource);
atomikosDataSourceBean.setUniqueResourceName("orderDataSource");
atomikosDataSourceBean.setPoolSize(5);
return atomikosDataSourceBean;
}
@Bean(name = "orderSqlSessionFactory")
public SqlSessionFactory orderSqlSessionFactory(@Qualifier("orderDataSource") DataSource dataSource) {
MybatisSqlSessionFactoryBean sessionFactory = new MybatisSqlSessionFactoryBean();
try{
sessionFactory.setDataSource(dataSource);
return sessionFactory.getObject();
} catch (Exception e) {
log.error("",e);
}
return null;
}
@Bean(name = "orderSqlSessionTemplate")
public SqlSessionTemplate orderSqlSessionTemplate(@Qualifier("orderSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
测试接口编写
InitService
接口
csharp
@Service
public class InitService {
@Autowired
private UserinfoMapper userinfoMapper;
@Autowired
private OrderMapper orderMapper;
/**
* 先写入user再写入order,再抛出异常看数据是否都回滚
*/
@Transactional(rollbackFor = Exception.class)
public void test01(){
userinfoMapper.insert(new UserinfoEntity("多库ID-userinfo","先写入user再写入order,再抛出异常看数据是否都回滚"));
orderMapper.insert(new OrderEntity("多库ID-order","先写入user再写入order,再抛出异常看数据是否都回滚"));
int a = 1/0;
}
/**
* user表的事务回滚
*/
@Transactional(rollbackFor = Exception.class)
public void test02(){
userinfoMapper.insert(new UserinfoEntity("单库ID-userinfo","user表的事务回滚"));
int a = 1/0;
}
/**
* order表的事务回滚
*/
@Transactional(rollbackFor = Exception.class)
public void test03(){
orderMapper.insert(new OrderEntity("单库ID-order","order表的事务回滚"));
int a = 1/0;
}
/**
* 数据正常写入2个库
*/
@Transactional(rollbackFor = Exception.class)
public void test04(){
userinfoMapper.insert(new UserinfoEntity("多库ID-userinfo-s","先写入user再写入order,都成功"));
orderMapper.insert(new OrderEntity("多库ID-order-s","先写入user再写入order,都成功"));
}
}
JtaTestController
typescript
@RestController
@RequestMapping(value = "jta-test")
public class JtaTestController {
@Autowired
private InitService initService;
@GetMapping(value = "test01")
public String test01(){
initService.test01();
return "success";
}
@GetMapping(value = "test02")
public String test02(){
initService.test02();
return "success";
}
@GetMapping(value = "test03")
public String test03(){
initService.test03();
return "success";
}
@GetMapping(value = "test04")
public String test04(){
initService.test04();
return "success";
}
}
实际测试结果
案例一
先写入user再写入order,再抛出异常看数据是否都回滚
bash
curl http://localhost:8080/jta-test/test01
结果
案例二
user表的事务回滚
bash
curl http://localhost:8080/jta-test/test02
结果
案例三
order表的事务回滚
bash
curl http://localhost:8080/jta-test/test03
结果
案例四
先写入user再写入order,都成功
bash
curl http://localhost:8080/jta-test/test04
结果
注意点
事务超时时间配置
单位是秒,最长300秒,超出无效
ini
@Bean(name = "transactionManager")
@Primary
public JtaTransactionManager regTransactionManager() throws Exception{
UserTransactionManager userTransactionManager = new UserTransactionManager();
userTransactionManager.setTransactionTimeout(300);
UserTransaction userTransaction = new UserTransactionImp();
userTransaction.setTransactionTimeout(300);
JtaTransactionManager jtaTransactionManager = new JtaTransactionManager(userTransaction, userTransactionManager);
jtaTransactionManager.setDefaultTimeout(300);
return jtaTransactionManager;
}