XA协议:是一种标准协议,允许事务管理器协调多个资源管理器,确保在分布式事务中的一致性和原子性。
JTA:是JavaEE规范中的一种,用于管理分布式事务的 API,提供了事务的控制和协调机制
Atomikos理解成JTA的实现
XA是JTA的基础(JTA实现了XA协议) Atomikos实现了JTA
Atomikos这种方式限定与单应用多数据源使用场景.
关于理论的一些:
当业务操作需要涉及到多个数据库时,这就可称为分布式事务处理.实现分布式事务处理的关键是采用一种手段保证事务涉及的所有数据库所做的全部动作要么全部生效,要买么全部回滚.
目前处理分布式事务的规范就是XA规范.
X/Open组织为分布式事务制定了的事务中间件与数据库之间的接口规范.这种规范就是XA规范.事务中间件用它来通知数据库事务开始,提交,回滚等
X/Open组织仅仅制定了分布式事务处理的XA规范,但具体的实现则由不同的数据库厂商自行提供.
对于大部分主流商业及开源数据库都提供了XA规范的驱动
分布式事务处理方法很多
2PC 两阶段提交:
成功示例:
失败示例:
`id` int NOT NULL AUTO_INCREMENT,
`title` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`content` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
`user_id` int NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`password` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`age` int DEFAULT NULL,
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
@Configuration
@MapperScan(basePackages = {"com.example.demo5.mapper"},sqlSessionFactoryRef = "primarySqlSessionFactory")
public class DataSourceConfig1 {
@Primary
@Bean(name="primaryDataSource")
@ConfigurationProperties(prefix = "spring.datasource.primary")
public DataSource primaryDataSource(){
//return DataSourceBuilder.create().build();
//return new HikariDataSource();
return new DruidDataSource();
}
@Primary
@Bean(name="primarySqlSessionFactory")
public SqlSessionFactory primarySqlSessionFactory(@Qualifier("primaryDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean=new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setTypeAliasesPackage("com.example.demo5.pojo");
bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mappers/*Mapper.xml"));
SqlSessionFactory sqlSessionFactory = bean.getObject();
sqlSessionFactory.getConfiguration().setMapUnderscoreToCamelCase(true);
//sqlSessionFactory.getConfiguration().setJdbcTypeForNull(JdbcType.NULL);
return sqlSessionFactory;
}
@Primary
@Bean(name="primarySqlSessionTemplate")
public SqlSessionTemplate primarySqlSessionTemplate(@Qualifier("primarySqlSessionFactory")SqlSessionFactory sqlSessionFactory){
return new SqlSessionTemplate(sqlSessionFactory);
}
@Primary
@Bean(name="primaryJdbcTemplate")
public JdbcTemplate primaryJdbcTemplate(@Qualifier("primaryDataSource")DataSource dataSource){
return new JdbcTemplate(dataSource);
}
@Primary
@Bean(name="primaryTransactionManager")
public DataSourceTransactionManager primaryTransactionManager(@Qualifier("primaryDataSource")DataSource dataSource){
return new DataSourceTransactionManager(dataSource);
}
}
@Configuration
@MapperScan(basePackages = {"com.example.demo5.mapper2"},sqlSessionFactoryRef = "secondSqlSessionFactory")
public class DataSourceConfig2 {
@Bean(name="secondDataSource")
@ConfigurationProperties(prefix = "spring.datasource.second")
public DataSource secondDataSource(){
return DataSourceBuilder.create().build();
}
@Bean(name="secondSqlSessionFactory")
public SqlSessionFactory secondSqlSessionFactory(@Qualifier("secondDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean=new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setTypeAliasesPackage("com.example.demo5.pojo2");
bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mappers2/*Mapper.xml"));
SqlSessionFactory sqlSessionFactory = bean.getObject();
sqlSessionFactory.getConfiguration().setMapUnderscoreToCamelCase(true);
//sqlSessionFactory.getConfiguration().setJdbcTypeForNull(JdbcType.NULL);
return sqlSessionFactory;
}
@Bean(name="secondSqlSessionTemplate")
public SqlSessionTemplate secondSqlSessionTemplate(@Qualifier("secondSqlSessionFactory")SqlSessionFactory sqlSessionFactory){
return new SqlSessionTemplate(sqlSessionFactory);
}
@Bean(name="secondJdbcTemplate")
public JdbcTemplate secondJdbcTemplate(@Qualifier("secondDataSource")DataSource dataSource){
return new JdbcTemplate(dataSource);
}
@Bean(name="secondTransactionManager")
public DataSourceTransactionManager secondTransactionManager(@Qualifier("secondDataSource")DataSource dataSource){
return new DataSourceTransactionManager(dataSource);
}
}
实体类
service
serviceImpl
事务测试
但是这两个事务管理器无法满足分布式事务
就是说我要将A数据库和B数据库的操作放在一个事务中,无法满足
Atomikos的数据源必须是AtomikosDataSourceBean,因此配置的数据源必须是XADatasource
然后再使用AtomikosDataSourceBean包装它们
使用Atomikos
Atomikos的数据源必须是AtomikosDataSourceBean,因此配置数据源必须先使用XADatasource,
然后再使用AtomikosDataSourceBean包装他们.
将不同数据源对应的AtomikosDataSourceBean注入对应sqlSessionFactory
application.properties
两个配置文件
@Configuration
@MapperScan(basePackages = {"com.example.demo5.mapper"},sqlSessionFactoryRef = "primarySqlSessionFactory")
public class DataSourceConfig1 {
// @Primary
@Bean(name="primary")
@ConfigurationProperties(prefix = "spring.datasource.primary")
public XADataSource primary(){
//MysqlXADataSource mysqlXADataSource = new MysqlXADataSource();//application.properties中的要与他里面的属性对应
//return mysqlXADataSource;
DruidXADataSource druidXADataSource=new DruidXADataSource();
return druidXADataSource;
}
@Bean(name="primaryDataSource")
@Primary
public DataSource primaryDataSource(){
AtomikosDataSourceBean atomikosDataSourceBean=new AtomikosDataSourceBean();
atomikosDataSourceBean.setXaDataSource(primary());
atomikosDataSourceBean.setMaxPoolSize(20);
atomikosDataSourceBean.setUniqueResourceName("mysqlDs1");//取个唯一名字
return atomikosDataSourceBean;
}
@Primary
@Bean(name="primarySqlSessionFactory")
public SqlSessionFactory primarySqlSessionFactory(@Qualifier("primaryDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean=new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setTypeAliasesPackage("com.example.demo5.pojo");
bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mappers/*Mapper.xml"));
SqlSessionFactory sqlSessionFactory = bean.getObject();
sqlSessionFactory.getConfiguration().setMapUnderscoreToCamelCase(true);
//sqlSessionFactory.getConfiguration().setJdbcTypeForNull(JdbcType.NULL);
return sqlSessionFactory;
}
@Primary
@Bean(name="primarySqlSessionTemplate")
public SqlSessionTemplate primarySqlSessionTemplate(@Qualifier("primarySqlSessionFactory")SqlSessionFactory sqlSessionFactory){
return new SqlSessionTemplate(sqlSessionFactory);
}
@Primary
@Bean(name="primaryJdbcTemplate")
public JdbcTemplate primaryJdbcTemplate(@Qualifier("primaryDataSource")DataSource dataSource){
return new JdbcTemplate(dataSource);
}
//使用JTA全局事务管理器后,不要再自定义事务管理器
// @Primary
// @Bean(name="primaryTransactionManager")
// public DataSourceTransactionManager primaryTransactionManager(@Qualifier("primaryDataSource")DataSource dataSource){
// return new DataSourceTransactionManager(dataSource);
// }
}
@Configuration
@MapperScan(basePackages = {"com.example.demo5.mapper2"},sqlSessionFactoryRef = "secondSqlSessionFactory")
public class DataSourceConfig2 {
// @Primary
@Bean(name="second")
@ConfigurationProperties(prefix = "spring.datasource.second")
public XADataSource second(){
// MysqlXADataSource mysqlXADataSource = new MysqlXADataSource();//application.properties中的要与他里面的属性对应
//
// return mysqlXADataSource;
DruidXADataSource druidXADataSource=new DruidXADataSource();
return druidXADataSource;
}
@Bean(name="secondDataSource")
public DataSource secondDataSource(){
AtomikosDataSourceBean atomikosDataSourceBean=new AtomikosDataSourceBean();
atomikosDataSourceBean.setXaDataSource(second());
atomikosDataSourceBean.setMaxPoolSize(20);
atomikosDataSourceBean.setUniqueResourceName("mysqlDs2");//取个唯一名字
return atomikosDataSourceBean;
}
@Bean(name="secondSqlSessionFactory")
public SqlSessionFactory secondSqlSessionFactory(@Qualifier("secondDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean=new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setTypeAliasesPackage("com.example.demo5.pojo2");
bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mappers2/*Mapper.xml"));
SqlSessionFactory sqlSessionFactory = bean.getObject();
sqlSessionFactory.getConfiguration().setMapUnderscoreToCamelCase(true);
//sqlSessionFactory.getConfiguration().setJdbcTypeForNull(JdbcType.NULL);
return sqlSessionFactory;
}
@Bean(name="secondSqlSessionTemplate")
public SqlSessionTemplate secondSqlSessionTemplate(@Qualifier("secondSqlSessionFactory")SqlSessionFactory sqlSessionFactory){
return new SqlSessionTemplate(sqlSessionFactory);
}
@Bean(name="secondJdbcTemplate")
public JdbcTemplate secondJdbcTemplate(@Qualifier("secondDataSource")DataSource dataSource){
return new JdbcTemplate(dataSource);
}
// @Bean(name="secondTransactionManager")
// public DataSourceTransactionManager secondTransactionManager(@Qualifier("secondDataSource")DataSource dataSource){
// return new DataSourceTransactionManager(dataSource);
// }
}
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.3.0</version> </dependency> <!--stomikos--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jta-atomikos</artifactId> </dependency> <!--德鲁伊--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.2.15</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <!-- <scope>runtime</scope>使用MysqlXADataSource的话把运行时去掉,不然找不到 不用可以恢复运行时--> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </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-test</artifactId> <version>3.0.2</version> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build>