java多数据源事务管理_Spring中实现多数据源事务管理 - 优快云博客

本文介绍了在项目中遇到的多数据源事务管理问题,以及如何通过自定义切面实现对多个TransactionManager的事务控制。作者首先讨论了@Transactional注解的value属性用于指定特定的TransactionManager,并在未指定时默认使用的TransactionManager。然后,针对需要同时操作多个数据源的场景,由于@Transactional无法直接支持,作者手动创建了一个切面,模拟编程式事务管理,实现了事务的开启、提交和回滚。文章还提到了使用Stack来管理事务状态以应对Spring事务管理的LIFO行为,确保事务处理的正确性。最后,文章提及了分布式事务JTA,但并未深入探讨其用途。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

由于项目中引入了多个数据源,并且需要对多个数据源进行写操作,那么多数据源的事务管理自然成了不可避免的问题,这也让我对 @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

}1

2

3

4

关于不指定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;

}

}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

我们借用这个逻辑将事务管理相关提取到切面中,并在进入目标方法之前,让多个 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 dataSourceTransactionManagerStack = new Stack();

Stack transactionStatuStack = new Stack();

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 dataSourceTransactionManagerStack,

Stack 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 dataSourceTransactionManagerStack,

Stack 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 dataSourceTransactionManagerStack,

Stack transactionStatuStack) {

while (!dataSourceTransactionManagerStack.isEmpty()) {

dataSourceTransactionManagerStack.pop().rollback(

transactionStatuStack.pop());

}

}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

整体结构很清晰:

1. 首先根据指定的多个 TransactionManager依次开启事务,这个次序不影响,因为其实大家都是平等的。

2. 其次就是调用目标方法执行具体的业务逻辑

3. 若是成功返回则提交每个事务,若中途报错,那么就回滚每个事务

其中为什么要用 Stack来保存 TransactionManager和 TransactionStatus呢?那是因为 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(AbstractPlatformTransactio1

2

3

4

5

6

题外话

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值