SpringBoot实现Oracle和MongoDB跨库事务

最近在做一个项目,项目基于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

标签:事务   切面   注解   管理器   异常   操作   方式   数据库   项目   平台

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2008-2024 All Rights Reserved. Powered By bs178.com 闽ICP备11008920号-3
闽公网安备35020302034844号

Top