javascript
java多数据源事务管理_Spring中实现多数据源事务管理 - CSDN博客
前言
由于項目中引入了多個數據源,并且需要對多個數據源進行寫操作,那么多數據源的事務管理自然成了不可避免的問題,這也讓我對 @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究竟有什么用呢?
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的java多数据源事务管理_Spring中实现多数据源事务管理 - CSDN博客的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 桂花粥的功效与作用、禁忌和食用方法
- 下一篇: 大叶苦丁茶的功效与作用、禁忌和食用方法