【SpringBoot2.0】基于Atomikos的多数据源分布式事务(XA)解决方案

本文围绕Spring Boot项目中多数据源的XA分布式事务展开。介绍了项目框架,包括Spring Boot 2.0.x、Spring MVC和MyBatis。阐述多数据源mapper的两种处理方式,作者采用将各数据源mapper放不同package的方案。着重说明在项目中配置XA的步骤,以两个数据源为例构建数据源,配置好后加注解即可实现事务回滚。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近工作中在同一项目中用到了多数据源,虽然项目本身对多数据源的事务没有要求,甚至可以不使用事务。但是本着精益求精的原则,加上各种资料的查阅,终于实现了多数据源的XA(分布式事务)

项目框架
  • springboot2.0.x
  • springmvc
  • mybatis
对多数据源的mapper的处理

针对多个数据源,mapper通常有两种处理方式:一种是将各个数据源对应的mapper放置在不同的package中,通过配置各个对应的SqlSessionFactory来实例化各自的mapper;另一种是使用动态数据源,即在程序运行时,通过注解or函数动态的设置当前使用哪个数据源,而mapper本身是不知道自己会使用哪个数据源的

目前我使用的是前一种方案,毕竟这种方案各个mapper都很清楚的知道自己将会使用哪个数据源,第二种方案我目前还没使用的需求。

对于如何配置各个数据源对应不同的mapper,我会再后续的文章中阐述。本文着重阐述如何在springboot中配置XA

第一步,引入spring-boot-starterspring-boot-starter-jta-atomikos
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-jta-atomikos</artifactId>
		</dependency>

springboot中配置xa共需要配置两个东西,一个是支持xa的数据源,一个是基于xa的分布式事务管理器
因为在项目中我们已经引入了spring-boot-starter-jta-atomikos,它会自带一个支持xa的分布式事务管理器(此依赖还会自动创建一个支持xa的数据源,但是只能自动创建一个,而我们如果用xa的话,肯定是多数据源,所以这里感觉springboot的这个“默认”配置有点zz),因此我们只需要配置好多个数据源即可(必须是支持xa的数据源)

我们这里以两个数据源为例:
我们先构造一个数据源创建器

@Component
public class DataSourceConfig implements BeanClassLoaderAware{
	
	@Setter
	ClassLoader beanClassLoader;
	
	protected XADataSource createXaDataSource(DataSourceProperties properties) {
		String className = properties.getXa().getDataSourceClassName();
		if (!StringUtils.hasLength(className)) {
			className = DatabaseDriver.fromJdbcUrl(properties.determineUrl())
					.getXaDataSourceClassName();
		}
		Assert.state(StringUtils.hasLength(className),
				"No XA DataSource class name specified");
		XADataSource dataSource = createXaDataSourceInstance(className);
		bindXaProperties(dataSource, properties);
		return dataSource;
	}

	private XADataSource createXaDataSourceInstance(String className) {
		try {
			Class<?> dataSourceClass = ClassUtils.forName(className, this.beanClassLoader);
			Object instance = BeanUtils.instantiateClass(dataSourceClass);
			Assert.isInstanceOf(XADataSource.class, instance);
			return (XADataSource) instance;
		}
		catch (Exception ex) {
			throw new IllegalStateException(
					"Unable to create XADataSource instance from '" + className + "'");
		}
	}

	private void bindXaProperties(XADataSource target,
			DataSourceProperties dataSourceProperties) {
		Binder binder = new Binder(getBinderSource(dataSourceProperties));
		binder.bind(ConfigurationPropertyName.EMPTY, Bindable.ofInstance(target));
	}

	private ConfigurationPropertySource getBinderSource(
			DataSourceProperties dataSourceProperties) {
		MapConfigurationPropertySource source = new MapConfigurationPropertySource();
		source.put("user", dataSourceProperties.determineUsername());
		source.put("password", dataSourceProperties.determinePassword());
		source.put("url", dataSourceProperties.determineUrl());
		source.putAll(dataSourceProperties.getXa().getProperties());
		ConfigurationPropertyNameAliases aliases = new ConfigurationPropertyNameAliases();
		aliases.addAliases("user", "username");
		return source.withAliases(aliases);
	}
}

这里思路是使用特定的配置文件(DataSourceProperties)来构建一个DataSource对象,这里有不少代码都参考的SpringBoot中XADataSourceAutoConfiguration.java的源码
然后我们构建两个数据源:
第一个数据源

	@Configuration
	public static class MybatisDbBConfigODS extends DataSourceConfig{
		
		@Autowired
		XADataSourceWrapper wrapper;

		@Bean
		@Primary
		@ConfigurationProperties("spring.datasource.ods")
		public DataSourceProperties getDataSourceProperties() {
			return new DataSourceProperties();
		}

		@Bean
		@Primary
		public DataSource getDataSource() throws Exception {
			XADataSource xaDataSource = createXaDataSource(getDataSourceProperties());
			return this.wrapper.wrapDataSource(xaDataSource);
		}
	
	}

这里的思路是先将当前数据源需要的属性(例如用户名、密码、数据库连接地址等)加载到DataSourceProperties中,然后使用此对象来创建DataSource对象,注意这里的XADataSourceWrapper对象,它的作用是将XA数据源转化为普通的数据源。
第二个数据源的创建方法类似:

	@Configuration
	@MapperScan(basePackages = "com.xinbo.ods.dao.cdr", sqlSessionFactoryRef = "sqlSessionFactory4cdr", sqlSessionTemplateRef = "sqlSessionTemplate4cdr", annotationClass = Mapper.class)
	public static class MybatisDbBConfigCDR  extends DataSourceConfig{
		
		@Autowired
		XADataSourceWrapper wrapper;

		@Bean("UNJi29SsAF19WVCkeKdt")
		@ConfigurationProperties("spring.datasource.cdr")
		public DataSourceProperties getDataSourceProperties() {
			return new DataSourceProperties();
		}

		@Bean("ahS54La6KNmWnchdQQil")
		public DataSource getDataSource4cdr() throws Exception {
			XADataSource xaDataSource = createXaDataSource(getDataSourceProperties());
			return this.wrapper.wrapDataSource(xaDataSource);
		}
	}

要注意的是,最好每个@Bean注解中使用唯一的名字,否则会和上的重名,然后出现问题
到这里所有的配置已经配置好了,然后我们使用事务时,只需要在需要的类或方法上加上@Transactional注解即可,当一个事务中同时操作多个数据库时,遇到异常会回滚对所有数据库的操作。

PS:对于springboot是如何创建支持XA的事务管理器TransactionManager的,如果有兴趣,可以参考这个类的源码:AtomikosJtaConfiguration.java

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值