zoukankan      html  css  js  c++  java
  • Spring中实现多数据源事务管理

    文章转自  https://www.2cto.com/kf/201507/424229.html

    前言

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

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

    研究

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

    1
    2
    3
    4
    <code class="hljs" cs="">    @Transactional(value = database2TransactionManager)
        public void test(String a) {
            // business operation
        }</code>

    关于不指定TransactionManager时会使用哪一个TransactionManager,有兴趣的童鞋可以参考另一篇文章,讲的比较清晰:https://blog.sina.com.cn/s/blog_8f61307b0100ynfb.html

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

    进一步研究

    看来@Transactional是没有提供这种功能了,那么就自己写了一个吧。我记得Spring中的事务管理分编程式事务和声明式事务。我们平时使用的@Transactional就是声明式事务,它的好处其实也就是灵活度更高、代码的耦合性更低,最终的事务管理实现还是一样的,只不过将具体逻辑都剥离到了切面中。所以我们可以手写一个切面来写一次“编程式事务”,当然在具体应用时,还是声明式的。

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    <code class="hljs" 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;
        }
    }</code>

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    <code class="hljs" java="">/**
     * @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());
            }
        }</transactionstatus></datasourcetransactionmanager></transactionstatus></datasourcetransactionmanager></transactionstatus></datasourcetransactionmanager></transactionstatus></transactionstatus></datasourcetransactionmanager></datasourcetransactionmanager></code>

    整体结构很清晰:
    1. 首先根据指定的多个TransactionManager依次开启事务,这个次序不影响,因为其实大家都是平等的。
    2. 其次就是调用目标方法执行具体的业务逻辑
    3. 若是成功返回则提交每个事务,若中途报错,那么就回滚每个事务

    其中为什么要用Stack来保存TransactionManagerTransactionStatus呢?那是因为Spring的事务处理是按照LIFO/stack behavior的方式进行的。如若顺序有误,则会报错:

    1
    2
    3
    4
    5
    6
    <code avrasm="" class="hljs">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</code>

    题外话

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

  • 相关阅读:
    Linux 内核剖解(转)
    计算机系统的分层结构
    Linux学习之路三:重要概念之Linux系统层次结构
    库函数与系统调用的联系与区别
    库函数与系统调用
    库函数调用和系统调用的区别
    标准库函数和系统调用的区别
    关于Linux操作系统层次结构分析
    linux标准输入输出
    C语言的标准输入输出
  • 原文地址:https://www.cnblogs.com/shuaiandjun/p/8667815.html
Copyright © 2011-2022 走看看