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 {};
} [/code]作用到方法上
@ItsysTransaction(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方法开启并往下执行。
- spring boot多数据源 分布式事务管理
- springboot多数据源指定不同事务管理器
- SpringBoot多数据源事务管理机制
- Springboot+Mybatis+Druid 实现多数据源,与事务管理
- springboot+分包,实现多数据源切换和事务管理(靠谱版)
- Spring Boot多数据源及其事务管理配置方法
- Spring Boot2.0之多数据源事务管理
- Spring Boot多数据源及其事务管理配置
- Spring Boot中的事务管理
- Spring3.0配置多个事务管理器(即操作多个数据源)的方法
- springboot快速入门(五)——事务管理
- spring 多数据源手动管理事务,最大程度保障数据一致性
- 3分钟搞定SpringBoot+Mybatis+druid多数据源和分布式事务
- SpringCloud SpringBoot mybatis 分布式微服务(十八)Spring Boot中的事务管理
- SpringBoot学习(六)——事务的管理
- 【SpringBoot】2小时学会SpringBoot学习笔记( 第6章 事务管理 )
- spring boot 中的事务管理
- Spring Boot 中使用 @Transactional 注解配置事务管理
- Spring中实现多数据源事务管理
- 【SpringBoot】数据库操作与事务管理