接口操作MySQL跟MongoDB事务回滚问题

接口操作MySQL跟MongoDB事务回滚问题,MySQL可正常回滚,MongoDB无法正常回滚

问题描述

通常使用的事务注解:@Transactional 是不会对MongoDB生效的,但是在一些生产接口中无法避免同时使用MySQL跟MongoDB的操作。

问题处理方式

1、创建 MultiTransactional.java

import java.lang.annotation.*;
 
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MultiTransactional {
    String[] value() default {};
}

2、创建 TransactionConfig.java

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.mongodb.MongoDatabaseFactory;
import org.springframework.data.mongodb.MongoTransactionManager;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
 
import javax.sql.DataSource;
 
@Configuration
public class TransactionConfig {
 
    @Bean(name = "mybatisTransactionManager")
    @Primary //事务默认使用mysql数据库
    public DataSourceTransactionManager testTransactionManager(@Qualifier("dataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
 
    @Bean(name = "mongoTransactionManager")
    MongoTransactionManager transactionManager(MongoDatabaseFactory factory){
    return new MongoTransactionManager(factory);
    }
}

3、创建 DbTxBroker.java

import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
 
import java.util.concurrent.Callable;
 
@Component
public class DbTxBroker {
 
    @Transactional(value = "mybatisTransactionManager")
    public <V> V inTransactionMybatisTransactionManager(Callable<V> callable) {
        try {
            return callable.call();
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
 
    @Transactional(value = "mongoTransactionManager")
    public <V> V inTransactionMongoTransactionManager(Callable<V> callable) {
        try {
            return callable.call();
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

4、创建 ComboTransaction.java

import com.alibaba.druid.util.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
 
import java.util.concurrent.Callable;
import java.util.stream.Stream;
 
@Component
public class ComboTransaction {
 
    @Autowired
    private com.skeqi.plims.config.DbTxBroker dbTxBroker;
 
    public <V> V inCombinedTx(Callable<V> callable, String[] transactions) {
        if (callable == null) {
            return null;
        }
 
        Callable<V> combined = Stream.of(transactions).filter(ele -> !StringUtils.isEmpty(ele)).distinct()
                .reduce(callable, (r, tx) -> {
                    switch (tx) {
                    case "mybatisTransactionManager":
                        return () -> dbTxBroker.inTransactionMybatisTransactionManager(r);
                    case "mongoTransactionManager":
                        return () -> dbTxBroker.inTransactionMongoTransactionManager(r);
                    default:
                        return null;
                    }
                }, (r1, r2) -> r2);
 
        try {
            return combined.call();
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

5、创建 MultiTransactionAop.java

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.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
 
@Aspect
@Component
public class MultiTransactionAop {
 
    private final com.skeqi.plims.config.ComboTransaction comboTransaction;
 
    @Autowired
    public MultiTransactionAop(com.skeqi.plims.config.ComboTransaction comboTransaction) {
        this.comboTransaction = comboTransaction;
    }
 
    @Pointcut("@annotation(com.skeqi.plims.config.MultiTransactional)")
    public void pointCut() {
    }
 
    @Around("pointCut() && @annotation(multiTransactional)")
    public Object inMultiTransactions(ProceedingJoinPoint pjp, com.skeqi.plims.config.MultiTransactional multiTransactional) {
        return comboTransaction.inCombinedTx(() -> {
            try {
                return pjp.proceed();
            } catch (Throwable throwable) {
                if (throwable instanceof RuntimeException) {
                    throw (RuntimeException) throwable;
                }
                throw new RuntimeException(throwable);
            }
        }, multiTransactional.value());
    }
 
}

6、最后在使用接口上使用 @MultiTransactional 的注解
mybatisTransactionManager跟mongoTransactionManager表示同时启用数据mybatis操作的数据库跟MongoDB同时开启事务同时提交事务

@MultiTransactional(value = {"mybatisTransactionManager", "mongoTransactionManager"})
  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Java中,可以使用事务来回滚操作。事务是指一组操作,这些操作要么全部成功,要么全部失败。如果其中某个操作失败,整个事务就会回滚,使所有操作都恢复到事务开始之前的状态。 Java中可以使用JDBC事务和Spring事务两种方式实现回滚。下面分别介绍这两种方式的回滚实现方法。 1. JDBC事务回滚 使用JDBC事务回滚需要以下步骤: - 调用Connection对象的setAutoCommit(false)方法来关闭自动提交。 - 在事务执行过程中,如果任何一个操作失败,就调用Connection对象的rollback()方法回滚事务。 - 在事务执行结束后,如果所有操作都成功,就调用Connection对象的commit()方法提交事务。 例如: ```java try { Connection conn = getConnection(); // 获取数据库连接 conn.setAutoCommit(false); // 关闭自动提交 // 执行一系列数据库操作 conn.commit(); // 提交事务 } catch (SQLException e) { conn.rollback(); // 回滚事务 } finally { conn.setAutoCommit(true); // 恢复自动提交 conn.close(); // 关闭数据库连接 } ``` 2. Spring事务回滚 使用Spring事务回滚需要以下步骤: - 在Spring配置文件中开启事务管理器。 - 在需要实现事务的方法上添加@Transactional注解。 - 在事务执行过程中,如果任何一个操作失败,就抛出RuntimeException或继承自RuntimeException的异常。 - 在Spring事务管理器的配置中设置事务的回滚策略,例如使用默认的回滚策略org.springframework.transaction.interceptor.RollbackRuleAttribute。 例如: ```java @Transactional public void doTransaction() { // 执行一系列数据库操作 if (someCondition) { throw new RuntimeException("操作失败"); } } ``` 以上就是Java中实现回滚操作的方法,需要根据具体情况选择适合自己的方式。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值