接口操作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 inTransactionMybatisTransactionManager(Callable callable) {
        try {
            return callable.call();
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
 
    @Transactional(value = "mongoTransactionManager")
    public  V inTransactionMongoTransactionManager(Callable 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 inCombinedTx(Callable callable, String[] transactions) {
        if (callable == null) {
            return null;
        }
 
        Callable 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"})

你可能感兴趣的:(mongodb,mysql,数据库)