Spring中实现多数据源事务管理

2018/03/06 16:53
阅读数 10

Spring中实现多数据源事务管理

前言

由于项目中引入了多个数据源,并且须要对多个数据源进行写操作。那么多数据源的事务管理自然成了不可避免的问题,这也让我对@Transactional注解有了进一步的理解(但实际上也并非非常深入)

然而这是一个演进的过程,刚開始项目中并没有使用@Transactional指定详细的TransactionManager。所以新增一个数据源后,对原有的事务产生了影响了,这也是偶尔在一次測试报错而结果没有回滚之后才发现的。遂对于@Transactional注解的一些參数项进行了了解。

研究

由于容器中存在两个TransactionManager,那么被@Transactional注解的方法究竟使用了哪个TransactionManager来进行事务管理。抑或是同一时候使用了两个TransactionManager来进行事务管理都是我们须要搞清晰的问题。
首先我们先看看@Transactional注解上有没有提供配置项来指定TransactionManager,果不其然。发现value属性就是用来指定详细TransactionManager的,通过id或者name来指定唯一一个TransactionManager,那么对于仅仅须要一个事务管理的方法,问题就简单多了:

    @Transactional(value = "database2TransactionManager")
    public void test(String a) {
        // business operation
    }

关于不指定TransactionManager时会使用哪一个TransactionManager,有兴趣的童鞋能够參考还有一篇文章,讲的比較清晰:http://blog.sina.com.cn/s/blog_8f61307b0100ynfb.html

好了,回到我们研究的问题。那么对于须要写入多个数据源的业务方法该怎么办呢?

进一步研究

看来@Transactional是没有提供这样的功能了,那么就自己写了一个吧。

我记得Spring中的事务管理分编程式事务和声明式事务。我们平时使用的@Transactional就是声明式事务。它的优点事实上也就是灵活度更高、代码的耦合性更低,终于的事务管理实现还是一样的,仅仅只是将详细逻辑都剥离到了切面中。所以我们能够手写一个切面来写一次“编程式事务”,当然在详细应用时,还是声明式的。

Java中一般编程式事务的写法:

public class UserServiceImpl implements UserService {
    @Resource
    private TransactionManager txManager;
    @Resource
    private UserDao userDao;
    @Resource
    private AddressDao addressDao;

    public boolean saveUser(User user) {
        TransactionDefinition txDefinition = new TransactionDefinition();
        TransactionStatus txStatus = txManager.getTransaction(txDefinition);
        boolean result = false;
        try {
            result = userDao.save(user);
            if(!result){
                return false;
            }
            result = addressDao.save(user.getId(), user.getAddress());
            txManager.commit(txStatus);
        } catch (Exception e) {
            result = false;
            txManager.rollback(txStatus);
        }
        return result;
    }
}

我们借用这个逻辑将事务管理相关提取到切面中,并在进入目标方法之前。让多个TransactionManager都开启事务,并在成功运行后一并提交或失败后一并回滚,详细代码:

/**
 * @author Zhu
 * @date 2015-7-15
 * @version 0.0.1
 * @description
 */
public class MultiTransactionalAspect {

    private Logger logger = LoggerFactory.getLogger(getClass());

    public Object around(ProceedingJoinPoint pjp,
            MultiTransactional multiTransactional) throws Throwable {

        Stack<DataSourceTransactionManager> dataSourceTransactionManagerStack = new Stack<DataSourceTransactionManager>();
        Stack<TransactionStatus> transactionStatuStack = new Stack<TransactionStatus>();

        try {

            if (!openTransaction(dataSourceTransactionManagerStack,
                    transactionStatuStack, multiTransactional)) {
                return null;
            }

            Object ret = pjp.proceed();

            commit(dataSourceTransactionManagerStack, transactionStatuStack);

            return ret;
        } catch (Throwable e) {

            rollback(dataSourceTransactionManagerStack, transactionStatuStack);

            logger.error(String.format(
                    "MultiTransactionalAspect, method:%s-%s occors error:", pjp
                            .getTarget().getClass().getSimpleName(), pjp
                            .getSignature().getName()), e);
            throw e;
        }
    }

    /**
     * @author Zhu
     * @date 2015-7-25下午7:55:46
     * @description
     * @param dataSourceTransactionManagerStack
     * @param transactionStatuStack
     * @param values
     */
    private boolean openTransaction(
            Stack<DataSourceTransactionManager> dataSourceTransactionManagerStack,
            Stack<TransactionStatus> transactionStatuStack,
            MultiTransactional multiTransactional) {

        String[] transactionMangerNames = multiTransactional.values();
        if (ArrayUtils.isEmpty(multiTransactional.values())) {
            return false;
        }

        for (String beanName : transactionMangerNames) {
            DataSourceTransactionManager dataSourceTransactionManager = (DataSourceTransactionManager) ContextHolder
                    .getBean(beanName);
            TransactionStatus transactionStatus = dataSourceTransactionManager
                    .getTransaction(new DefaultTransactionDefinition());
            transactionStatuStack.push(transactionStatus);
            dataSourceTransactionManagerStack
                    .push(dataSourceTransactionManager);
        }
        return true;
    }

    /**
     * @author Zhu
     * @date 2015-7-25下午7:56:39
     * @description
     * @param dataSourceTransactionManagerStack
     * @param transactionStatuStack
     */
    private void commit(
            Stack<DataSourceTransactionManager> dataSourceTransactionManagerStack,
            Stack<TransactionStatus> transactionStatuStack) {
        while (!dataSourceTransactionManagerStack.isEmpty()) {
            dataSourceTransactionManagerStack.pop().commit(
                    transactionStatuStack.pop());
        }
    }

    /**
     * @author Zhu
     * @date 2015-7-25下午7:56:42
     * @description
     * @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来保存TransactionManagerTransactionStatus呢?那是由于Spring的事务处理是依照LIFO/stack behavior的方式进行的。

如若顺序有误,则会报错:

java.lang.IllegalStateException: Cannot deactivate transaction synchronization - not active
        at org.springframework.transaction.support.TransactionSynchronizationManager.clearSynchronization(TransactionSynchronizationManager.java:313)
        at org.springframework.transaction.support.TransactionSynchronizationManager.clear(TransactionSynchronizationManager.java:451)
        at org.springframework.transaction.support.AbstractPlatformTransactionManager.cleanupAfterCompletion(AbstractPlatformTransactionManager.java:986)
        at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:782)
        at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactio

题外话

刚開始碰到这个问题的时候,先想到的是分布式事务管理,也去看了JTA相关的文章,可是好像比較麻烦,并且都是一些老文章,于是想试试自己实现,最后也实现了。所以想知道JTA TransactionManager究竟有什么用呢?

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部