SpringBoot多数据源事务管理

本文介绍如何在Spring Boot项目中配置多个MySQL数据源,并通过自定义注解和AOP实现跨数据源的事务一致性管理。

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

采用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方法开启并往下执行。

 

 

 

智能网联汽车的安全员高级考试涉及多个方面的专业知识,包括但不限于自动驾驶技术原理、车辆传感器融合、网络安全防护以及法律法规等内容。以下是针对该主题的一些核心知识解析: ### 关于智能网联车安全员高级考试的核心内容 #### 1. 自动驾驶分级标准 国际自动机工程师学会(SAE International)定义了六个级别的自动驾驶等级,从L0到L5[^1]。其中,L3及以上级别需要安全员具备更高的应急处理能力。 #### 2. 车辆感知系统的组成与功能 智能网联车通常配备多种传感器,如激光雷达、毫米波雷达、摄像头和超声波传感器等。这些设备协同工作以实现环境感知、障碍物检测等功能[^2]。 #### 3. 数据通信与网络安全 智能网联车依赖V2X(Vehicle-to-Everything)技术进行数据交换,在此过程中需防范潜在的网络攻击风险,例如中间人攻击或恶意软件入侵[^3]。 #### 4. 法律法规要求 不同国家和地区对于无人驾驶测试及运营有着严格的规定,考生应熟悉当地交通法典中有关自动化驾驶部分的具体条款[^4]。 ```python # 示例代码:模拟简单决策逻辑 def decide_action(sensor_data): if sensor_data['obstacle'] and not sensor_data['emergency']: return 'slow_down' elif sensor_data['pedestrian_crossing']: return 'stop_and_yield' else: return 'continue_driving' example_input = {'obstacle': True, 'emergency': False, 'pedestrian_crossing': False} action = decide_action(example_input) print(f"Action to take: {action}") ``` 需要注意的是,“同学”作为特定平台上的学习资源名称,并不提供官方认证的标准答案集;建议通过正规渠道获取教材并参加培训课程来准备此类资格认证考试
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值