最近在做一个项目,项目基于SpringBoot实现后端,其中用到了Oracle数据库和MongoDB作为数据存储。
其中有的功能要求同时对Oracle和MongoDB进行读写操作,特别是写操作如果失败了需要对Oracle和MongoDB进行回滚,最初我们直接使用了SpringBoot提供的@Transactional(rollbackFor = Exception.class)注解进行事务控制,可系统上线后发现异常发生时并没有回滚MongoDB的写操作,导致Oracle和MongoDB之间出现了数据差异。
为了解决这个我们我们基于SpringBoo提供的平台事务管理器PlatformTransactionManager通过注解和AOP编程实现了一个跨数据库的事务异常管理器。实现方式如下:
1.先定义一个事务注解
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface MultiTransactional {
String[] values() default {};
}
2.使用AOP技术定一个事务处理的切面处理类
@Slf4j
@Aspect
@Component
public class MultiTransactionalAspect {
@Around("@annotation(multiTransactional)")
public Object around(ProceedingJoinPoint pjp, MultiTransactional multiTransactional) throws Throwable {
Stack transactionManagerStack = new Stack<>();
Stack transactionStatusStack = new Stack<>();
try {
if (!openTransaction(transactionManagerStack, transactionStatusStack, multiTransactional)) {
return null;
}
Object ret = pjp.proceed();
commit(transactionManagerStack, transactionStatusStack);
return ret;
} catch (Throwable e) {
rollback(transactionManagerStack, transactionStatusStack);
log.error("{}, class:{}, method:{}", e.getMessage(), pjp.getTarget().getClass().getSimpleName(), pjp.getSignature().getName(), e);
throw e;
}
}
private boolean openTransaction(Stack transactionManagerStack, Stack transactionStatusStack,
MultiTransactional multiTransactional) {
String[] transactionMangerNames = multiTransactional.values();
//判断是否配置了事务管理器,如果指定则使用指定的,如果未指定,则启动使用全部事务管理器
if (ArrayUtils.isNotEmpty(multiTransactional.values())) {
for (String beanName : transactionMangerNames) {
PlatformTransactionManager transactionManager = (PlatformTransactionManager) SpringContextHolder
.getBean(beanName);
TransactionStatus transactionStatus = transactionManager
.getTransaction(new DefaultTransactionDefinition());
transactionStatusStack.push(transactionStatus);
transactionManagerStack.push(transactionManager);
}
} else {
Map transactionManagerMap = SpringContextHolder.getBeansOfType(PlatformTransactionManager.class);
for (PlatformTransactionManager transactionManager : transactionManagerMap.values()) {
TransactionStatus transactionStatus = transactionManager
.getTransaction(new DefaultTransactionDefinition());
transactionStatusStack.push(transactionStatus);
transactionManagerStack.push(transactionManager);
}
}
return true;
}
private void commit(Stack transactionManagerStack, Stack transactionStatusStack) {
while (!transactionManagerStack.isEmpty()) {
transactionManagerStack.pop().commit(transactionStatusStack.pop());
}
}
private void rollback(Stack transactionManagerStack, Stack transactionStatusStack) {
while (!transactionManagerStack.isEmpty()) {
transactionManagerStack.pop().rollback(transactionStatusStack.pop());
}
}
}
3.MongoDB数据库事务配置(只有MongoDB4.0以上集群部署的情况下才支持事务)
@Configuration
public class MongoTransactionManagerConfiguration {
@Bean
@ConditionalOnProperty(name = "food.mongodb.transaction", havingValue = "true")
public MongoTransactionManager mongoTransactionManager(MongoDatabaseFactory factory) {
return new MongoTransactionManager(factory);
}
}
4.在SpringBoot中强制注册一个事务管理器
/**
* 强制添加一个默认的TransactionManager
*/
@Configuration(proxyBeanMethods = false)
public class TransactionManagerConfiguration {
@Bean
@Primary
DataSourceTransactionManager transactionManager(Environment environment, DataSource dataSource,
ObjectProvider transactionManagerCustomizers) {
DataSourceTransactionManager transactionManager = createTransactionManager(environment, dataSource);
transactionManagerCustomizers.ifAvailable((customizers) -> customizers.customize(transactionManager));
return transactionManager;
}
private DataSourceTransactionManager createTransactionManager(Environment environment, DataSource dataSource) {
return environment.getProperty("spring.dao.exceptiontranslation.enabled", Boolean.class, Boolean.TRUE)
? new JdbcTransactionManager(dataSource) : new DataSourceTransactionManager(dataSource);
}
}
至此程序就支持跨Oracle和MongoDB的事务处理了,在需要跨库事务处理的方法上加上@MultiTransactional注解即可。
例如:
@MultiTransactional
private void updateDataToDb(OracleDataInfo oracleDataInfo, MongoDBDataInfo MongoDBDataInfo) throws Exception {
//操作Oracle数据库
this.oracleDataService.deal(oracleDataInfo);
//操作MongoDB数据库
this.mongoDBDataService.update(MongoDBDataInfo);
}
这种事务处理的方式通过改造可以支持所有平台的事务,包括关系型数据库、MongoDB等等。
页面更新:2024-04-22
本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828
© CopyRight 2008-2024 All Rights Reserved. Powered By bs178.com 闽ICP备11008920号-3
闽公网安备35020302034844号