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)

    publicvoidtest(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="">publicclassUserServiceImpl implementsUserService {

    @Resource

    privateTransactionManager txManager;

    @Resource

    privateUserDao userDao;

    @Resource

    privateAddressDao addressDao;

 

    publicbooleansaveUser(User user) {

        TransactionDefinition txDefinition = newTransactionDefinition();

        TransactionStatus txStatus = txManager.getTransaction(txDefinition);

        booleanresult = false;

        try{

            result = userDao.save(user);

            if(!result){

                returnfalse;

            }

            result = addressDao.save(user.getId(), user.getAddress());

            txManager.commit(txStatus);

        } catch(Exception e) {

            result = false;

            txManager.rollback(txStatus);

        }

        returnresult;

    }

}</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

 */

publicclassMultiTransactionalAspect {

 

    privateLogger logger = LoggerFactory.getLogger(getClass());

 

    publicObject around(ProceedingJoinPoint pjp,

            MultiTransactional multiTransactional) throwsThrowable {

 

        Stack<datasourcetransactionmanager> dataSourceTransactionManagerStack = newStack<datasourcetransactionmanager>();

        Stack<transactionstatus> transactionStatuStack = newStack<transactionstatus>();

 

        try{

 

            if(!openTransaction(dataSourceTransactionManagerStack,

                    transactionStatuStack, multiTransactional)) {

                returnnull;

            }

 

            Object ret = pjp.proceed();

 

            commit(dataSourceTransactionManagerStack, transactionStatuStack);

 

            returnret;

        } catch(Throwable e) {

 

            rollback(dataSourceTransactionManagerStack, transactionStatuStack);

 

            logger.error(String.format(

                    MultiTransactionalAspect, method:%s-%s occors error:, pjp

                            .getTarget().getClass().getSimpleName(), pjp

                            .getSignature().getName()), e);

            throwe;

        }

    }

 

    /**

     * @author Zhu

     * @date 2015-7-25下午7:55:46

     * @description

     * @param dataSourceTransactionManagerStack

     * @param transactionStatuStack

     * @param values

     */

    privatebooleanopenTransaction(

            Stack<datasourcetransactionmanager> dataSourceTransactionManagerStack,

            Stack<transactionstatus> transactionStatuStack,

            MultiTransactional multiTransactional) {

 

        String[] transactionMangerNames = multiTransactional.values();

        if(ArrayUtils.isEmpty(multiTransactional.values())) {

            returnfalse;

        }

 

        for(String beanName : transactionMangerNames) {

            DataSourceTransactionManager dataSourceTransactionManager = (DataSourceTransactionManager) ContextHolder

                    .getBean(beanName);

            TransactionStatus transactionStatus = dataSourceTransactionManager

                    .getTransaction(newDefaultTransactionDefinition());

            transactionStatuStack.push(transactionStatus);

            dataSourceTransactionManagerStack

                    .push(dataSourceTransactionManager);

        }

        returntrue;

    }

 

    /**

     * @author Zhu

     * @date 2015-7-25下午7:56:39

     * @description

     * @param dataSourceTransactionManagerStack

     * @param transactionStatuStack

     */

    privatevoidcommit(

            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

     */

    privatevoidrollback(

            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究竟有什么用呢?

本文内容总结:前言,研究,进一步研究,题外话,

原文链接:https://www.cnblogs.com/shuaiandjun/p/8667815.html

以上是 Spring中实现多数据源事务管理 的全部内容, 来源链接: utcz.com/z/362721.html

回到顶部