SpringBoot多数据源事务管理

采用SpringBoot进行后端开发,项目里面配置了多台Mysql数据库,需要涉及场景为新增或修改数据时需要同时写入多台数据库,并保证事务一致,即要么所有数据库都写入成功入库,要么都写入失败回滚;

我们知道,Spring提供了事务管理,有声明式事务和编程式事务,声明式事务我们可以直接用@transactional注解作用于需要事务支持的方法上即可,该注解属性有:

 

当项目中存在多个数据源时,我们可以通过@Transactional(name="xxxTransactionManager")来指定使用的事务管理器,其实就是使用哪个数据源嘛,但是如果被注解的方法需要同时支持两个事务管理器呢,这个时候如果用@Transactional注解就不合适了,属性name只支持字符串类型,所以只能填一个,如果不传name属性,而且项目没有配置默认的事务管理器,在调用方法时则会抛出未指定默认事务管理器的异常。

这个时候,我们可以用编程式事务来解决这个问题。

首先,事务管理器还是需要配置的。比如我下面就配置了两个Mysql的数据源和事务管理器

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

@Configuration
public class DataSourceConfig {

	// A库datasource
	@Bean
	@Primary
	@ConfigurationProperties(prefix = "spring.datasource.primary")
	public DataSource primaryDataSource() {
		return DataSourceBuilder.create().build();
	}

	// B库datasource
	@Bean(name = "secondaryDataSource")
	@Qualifier("secondaryDataSource")
	@ConfigurationProperties(prefix = "spring.datasource.secondary")
	public DataSource secondaryDataSource() {
		return DataSourceBuilder.create().build();
	}

	// A库JdbcTemplate
	@Bean(name = "primaryJdbcTemplate")
	@Primary
	public JdbcTemplate primaryJdbcTemplate(DataSource dataSource) {
		return new JdbcTemplate(dataSource);
	}

	// B库JdbcTemplate
	@Bean(name="secondaryJdbcTemplate")
	public JdbcTemplate secondaryJdbcTemplate(@Qualifier("secondaryJdbcTemplate") DataSource dataSource){
		return new JdbcTemplate(dataSource);
	}
	
	// A库事务管理器
	@Bean
	@Qualifier("primaryTransaction")
	public PlatformTransactionManager primaryTransactionManager(@Qualifier("primaryDataSource") DataSource primaryDataSource) {
	    return new DataSourceTransactionManager(primaryDataSource);
	}

	// B库事务管理器
	@Bean
	@Qualifier("secondaryTransaction")
	public PlatformTransactionManager secondaryManager(@Qualifier("secondaryDataSource") DataSource secondaryDataSource) {
	    return new DataSourceTransactionManager(secondaryDataSource);
	}
}
 

配置了事务管理器,那怎么使用呢?

我们可以写一个注解,作用其实和Spring提供的@Transactional一样,只是这个注解接收多个不同是事务管理,然后将这个注解作用到对应的Service方法上。

自定义的注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 * 自定义事务注解
 * value为String类型数组,传入为定义的事务管理器
 * @author 一iTa
 *
 */
@Retention(RetentionPolicy.RUNTIME)  
@Target({ElementType.METHOD,ElementType.PARAMETER})  
public @interface CustomTransaction {  
    String[] value() default {};  
}  

作用到方法上

@CustomTransaction(value = {"primaryTransaction","secondaryTransaction"})
public void insertLine(List<Line> lines){
    log.info("xxx pre");
    // 各种操作....
    xxxDao.insertLine(lines);
    log.info("xxx post");
    return result;
}

ok,注解现在作用到具体的方法上了,但是现在还是不能生效的,我们需要在Aop里面去通过编程式事务使之生效。

用的是SpringBoot,所以采用注解的方式实现Aop还是比较容易的。

import java.util.Stack;

import org.apache.commons.lang.ArrayUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

@Aspect
@Configuration
public class TransactionAop {

	final Logger logger = LogManager.getLogger(TransactionAop.class);

	
	@Pointcut("@annotation(com.only.annotation.CustomTransaction)")
	public void CustomTransaction() {
	}


	@Pointcut("execution(* com.only.custom.controller.*.**(..))")
	public void excudeController() {
	}


	@Around(value = "CustomTransaction()&&excudeController()&&@annotation(annotation)")
	public Object twiceAsOld(ProceedingJoinPoint thisJoinPoint, CustomTransaction annotation) throws Throwable {
		Stack<DataSourceTransactionManager> dataSourceTransactionManagerStack = new Stack<DataSourceTransactionManager>();
		Stack<TransactionStatus> transactionStatuStack = new Stack<TransactionStatus>();

		try {

			if (!openTransaction(dataSourceTransactionManagerStack, transactionStatuStack, annotation)) {
				return null;
			}

			Object ret = thisJoinPoint.proceed();

			commit(dataSourceTransactionManagerStack, transactionStatuStack);

			return ret;
		} catch (Throwable e) {

			rollback(dataSourceTransactionManagerStack, transactionStatuStack);

			logger.error(String.format("MultiTransactionalAspect, method:%s-%s occors error:",
					thisJoinPoint.getTarget().getClass().getSimpleName(), thisJoinPoint.getSignature().getName()), e);
			throw e;
		}
	}

	/**
	 * 开启事务处理方法
	 * 
	 * @param dataSourceTransactionManagerStack
	 * @param transactionStatuStack
	 * @param multiTransactional
	 * @return
	 */
	private boolean openTransaction(Stack<DataSourceTransactionManager> dataSourceTransactionManagerStack,
			Stack<TransactionStatus> transactionStatuStack, CustomTransaction multiTransactional) {

		String[] transactionMangerNames = multiTransactional.value();
		if (ArrayUtils.isEmpty(multiTransactional.value())) {
			return false;
		}

		for (String beanName : transactionMangerNames) {
			DataSourceTransactionManager dataSourceTransactionManager = (DataSourceTransactionManager) SpringUtil
					.getBean(beanName);
			TransactionStatus transactionStatus = dataSourceTransactionManager
					.getTransaction(new DefaultTransactionDefinition());
			transactionStatuStack.push(transactionStatus);
			dataSourceTransactionManagerStack.push(dataSourceTransactionManager);
		}
		return true;
	}

	/**
	 * 提交处理方法
	 * 
	 * @param dataSourceTransactionManagerStack
	 * @param transactionStatuStack
	 */
	private void commit(Stack<DataSourceTransactionManager> dataSourceTransactionManagerStack,
			Stack<TransactionStatus> transactionStatuStack) {
		while (!dataSourceTransactionManagerStack.isEmpty()) {
			dataSourceTransactionManagerStack.pop().commit(transactionStatuStack.pop());
		}
	}

	/**
	 * 回滚处理方法
	 * 
	 * @param dataSourceTransactionManagerStack
	 * @param transactionStatuStack
	 */
	private void rollback(Stack<DataSourceTransactionManager> dataSourceTransactionManagerStack,
			Stack<TransactionStatus> transactionStatuStack) {
		while (!dataSourceTransactionManagerStack.isEmpty()) {
			dataSourceTransactionManagerStack.pop().rollback(transactionStatuStack.pop());
		}
	}
}

1. 首先根据指定的多个TransactionManager依次开启事务,这个次序不影响,因为其实大家都是平等的

2. 其次就是调用目标方法执行具体的业务逻辑

3. 若是成功返回则提交每个事务,若中途报错,那么就回滚每个事务

这里用了Stack来保存TransactionManager和TransactionStatus主要是为了保证顺序一致,当然也可以使用其他容器类。

到此,整个Aop就配置好了,启动工程,当调用insertLine方法时,事务将由aop的openTransaction方法开启并往下执行。

 

 

 

  • 8
    点赞
  • 51
    收藏
    觉得还不错? 一键收藏
  • 12
    评论
在Spring Boot中,可以通过使用`@Transactional`注解来管理多个数据源事务。首先,需要在配置文件中定义多个数据源,并将它们分别配置为`DataSource` bean。然后,可以使用`@Transactional`注解在需要进行事务管理的方法上进行标记。 在使用`@Transactional`注解时,可以指定`transactionManager`属性来指定使用的事务管理器。可以为每个数据源配置一个事务管理器,并在不同的方法上使用不同的事务管理器。例如: ```java @Configuration @EnableTransactionManagement public class DataSourceConfig { @Bean @ConfigurationProperties(prefix = "datasource1") public DataSource dataSource1() { return DataSourceBuilder.create().build(); } @Bean @ConfigurationProperties(prefix = "datasource2") public DataSource dataSource2() { return DataSourceBuilder.create().build(); } @Bean(name = "transactionManager1") public PlatformTransactionManager transactionManager1() { return new DataSourceTransactionManager(dataSource1()); } @Bean(name = "transactionManager2") public PlatformTransactionManager transactionManager2() { return new DataSourceTransactionManager(dataSource2()); } } ``` 然后,在需要进行事务管理的方法上使用`@Transactional`注解,并指定使用的事务管理器。例如: ```java @Service public class MyService { @Autowired private MyRepository repository; @Transactional(transactionManager = "transactionManager1") public void method1() { // 使用第一个数据源执行数据库操作 repository.save(entity1); } @Transactional(transactionManager = "transactionManager2") public void method2() { // 使用第二个数据源执行数据库操作 repository.save(entity2); } } ``` 这样,当调用`method1()`时,将使用第一个数据源和对应的事务管理器来管理事务;调用`method2()`时,将使用第二个数据源和对应的事务管理器来管理事务。这样就可以实现多数据源事务管理。注意,`@Transactional`注解应该放在具体的方法上,而不是放在类级别上。
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值