SpringBoot使用Atomikos实现分布式事务管理

1.分布式事务管理

如果在一个项目中,配置了两个数据库,那么两个数据库如何配置,以及事务怎么控制?
这里我们使用jta-atomikos 来实现分布式事物管理。
引入依赖

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-jta-atomikos</artifactId>
</dependency>

针对使用多数据源,我们可以提供两种解决思路来实现。
1、AOP实现:在调用数据库的方法上,添加注解,执行此方法的时候,进行AOP拦截,然后根据注解信息选择用不同的数据源。
2、分包实现:不同的数据源,扫描不同的包。(本文章以Mybatis的MapperScan进行扫描示例)。
以下采用分包思路实现配置多数据源。

1.1.application.properties配置

在单数据源中,我们只需要在配置文件中配置如下数据源即可,本文以application.properties配置为例

spring.datasource.url = jdbc:mysql://xxxxxxxx
spring.datasource.username = xxxxxx
spring.datasource.password = xxxxxx
#注意,新版本SpringBoot采用的横线代替驼峰
#spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.driverClassName = com.mysql.cj.jdbc.Driver

如果存在多个数据源,那么此时,如上配置则应变成

#mysql1配置
spring.datasource.mysql1.url = jdbc:mysql://xxxxxxxx
spring.datasource.mysql1.username = xxxxxx
spring.datasource.mysql1.password = xxxxxx
#mysql2配置
spring.datasource.mysql2.url = jdbc:mysql://xxxxxxxx
spring.datasource.mysql2.username = xxxxxx
spring.datasource.mysql2.password = xxxxxx

1.2.读取properties多数据源配置信息

读取mysql1信息,mysql1信息和mysql2写法保持一直,mysql2配置就是将1换成2即可。

/**
 * Description:映射application.properties中的mysql1信息到MysqlConfig1这个类中
 * @author hutao
 * @mail [email protected]
 * @date 2020年8月8日 
 */
@ConfigurationProperties(prefix = "mysql.datasource.mysql1")
public class MysqlConfig1 {
	private String url;
	private String username;
	private String password;
	//省略其他配置,及GET/SET
}
/**
 * Description:映射application.properties中的mysql2信息到MysqlConfig2这个类中
 * @author hutao
 * @mail [email protected]
 * @date 2020年8月8日 
 */
@ConfigurationProperties(prefix = "mysql.datasource.mysql2")
public class MysqlConfig2 {
	private String url;
	private String username;
	private String password;
	//省略其他配置,及GET/SET
}

1.3.创建mysql1数据源

因此这里不是单数据源,不能使用系统自带的数据源配置,因此需要我们将配置的多数据源配置注入到Spring上下文。核心包含:DataSource 、SqlSessionFactory 、SqlSessionTemplate。

@SpringBootConfiguration
/**
 * Description:多数据源分包扫描
 * @author hutao
 * @mail [email protected]
 * @date 2020年8月8日 
 */
@MapperScan(basePackages = "com.hutao.springboot.module.mysql1", sqlSessionTemplateRef = "sqlSessionTemplate1")
public class MyBatisConfig1 {
	//@Primary:自动装配时当出现多个Bean候选者时,被注解为@Primary的Bean将作为首选者,否则将抛出异常 
	/**
 	* Description:配置数据源dataSource1,低版本的SpringBoot需要添加注解@Primary
	* @author hutao
	* @mail [email protected]
 	* @date 2020年8月8日 
 	*/
	@Bean(name = "dataSource1")
	public DataSource dataSource1(MysqlConfig1 mysqlConfig) throws SQLException {
		MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
		mysqlXaDataSource.setUrl(mysqlConfig.getUrl());
		mysqlXaDataSource.setPassword(mysqlConfig.getPassword());
		mysqlXaDataSource.setUser(mysqlConfig.getUsername());
		//省略其他mysql1配置信息
		
		//jta-atomikos统一管理事务
		AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
		xaDataSource.setXaDataSource(mysqlXaDataSource);
		xaDataSource.setUniqueResourceName("dataSource1");
		//省略其他jta-atomikos配置
		return xaDataSource;
	}
	/**
 	* Description:配置SQL会话工厂,低版本的SpringBoot需要添加注解@Primary
	* @author hutao
	* @mail [email protected]
 	* @date 2020年8月8日 
 	*/
	@Bean(name = "sqlSessionFactory1")
	public SqlSessionFactory sqlSessionFactory1(@Qualifier("dataSource1") DataSource dataSource)
			throws Exception {
		SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
		bean.setDataSource(dataSource);
		return bean.getObject();
	}

	/**
 	* Description:配置SQL会话模板,低版本的SpringBoot需要添加注解@Primary
	* @author hutao
	* @mail [email protected]
 	* @date 2020年8月8日 
 	*/
	@Bean(name = "sqlSessionTemplate1")
	public SqlSessionTemplate testSqlSessionTemplate(
			@Qualifier("sqlSessionTemplate1") SqlSessionFactory sqlSessionFactory) throws Exception {
		return new SqlSessionTemplate(sqlSessionFactory);
	}
}

1.4.创建mysql2数据源

@SpringBootConfiguration
/**
 * Description:多数据源分包扫描
 * @author hutao
 * @mail [email protected]
 * @date 2020年8月8日 
 */
@MapperScan(basePackages = "com.hutao.springboot.module.mysql2", sqlSessionTemplateRef = "sqlSessionTemplate2")
public class MyBatisConfig2 {
	//@Primary:自动装配时当出现多个Bean候选者时,被注解为@Primary的Bean将作为首选者,否则将抛出异常 
	/**
 	* Description:配置数据源dataSource2,低版本的SpringBoot需要添加注解@Primary
	* @author hutao
	* @mail [email protected]
 	* @date 2020年8月8日 
 	*/
	@Bean(name = "dataSource2")
	public DataSource dataSource2(MysqlConfig2 mysqlConfig) throws SQLException {
		MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
		mysqlXaDataSource.setUrl(mysqlConfig.getUrl());
		mysqlXaDataSource.setPassword(mysqlConfig.getPassword());
		mysqlXaDataSource.setUser(mysqlConfig.getUsername());
		//省略其他mysql1配置信息
		
		//jta-atomikos统一管理事务
		AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
		xaDataSource.setXaDataSource(mysqlXaDataSource);
		xaDataSource.setUniqueResourceName("dataSource2");
		//省略其他jta-atomikos配置
		return xaDataSource;
	}
	/**
 	* Description:配置SQL会话工厂,低版本的SpringBoot需要添加注解@Primary
	* @author hutao
	* @mail [email protected]
 	* @date 2020年8月8日 
 	*/
	@Bean(name = "sqlSessionFactory2")
	public SqlSessionFactory sqlSessionFactory1(@Qualifier("dataSource2") DataSource dataSource)
			throws Exception {
		SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
		bean.setDataSource(dataSource);
		return bean.getObject();
	}

	/**
 	* Description:配置SQL会话模板,低版本的SpringBoot需要添加注解@Primary
	* @author hutao
	* @mail [email protected]
 	* @date 2020年8月8日 
 	*/
	@Bean(name = "sqlSessionTemplate2")
	public SqlSessionTemplate testSqlSessionTemplate(
			@Qualifier("sqlSessionTemplate2") SqlSessionFactory sqlSessionFactory) throws Exception {
		return new SqlSessionTemplate(sqlSessionFactory);
	}
}

1.5.启动类启动加载数据库配置资源

//启动加载数据库资源
@EnableConfigurationProperties(value = { MysqlConfig1 .class, MysqlConfig1 .class })
//声明Application这个类是这个项目的启动类
@SpringBootApplication
public class Application {
	public static void main(String[] args) {
		SpringApplication.run(Application.class, args);
	}
}

1.6.Atomikos实现分布式事务使用示例

下述代码,当除0错误后,两个数据库都会回滚。

	/**
    * Description:多数据源分包扫描
    * @author hutao
    * @mail [email protected]
    * @date 2020年8月8日 
    */
	@Transactional()
	public int insertUserMysql1AndMysql2(String name, Integer age) {
		// 传统分布式事务解决方案 jta+atomikos 注册同一个全局事务中
		// 第一个数据源
		int mysql1= userMapperMysql1.insert(name, age);
		// 第二个数据源
		int mysql1= userMapperMysql2.insert(name, age);
		int i = 1 / 0;
		return result;
	}

你可能感兴趣的:(Spring,分布式,spring,boot)