您的位置:首页 > 编程语言 > Java开发

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

2017-05-09 00:00 253 查看
摘要:http://blog.csdn.net/u012345283/article/details/47059537

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

前言

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

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

研究

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

?

1
2
3
4
<codeclass="hljs"cs="">@Transactional(value=database2TransactionManager)
publicvoidtest(Stringa){
//businessoperation
}</code>
关于不指定TransactionManager时会使用哪一个TransactionManager,有兴趣的童鞋可以参考另一篇文章,讲的比较清晰:http://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
<codeclass="hljs"java="">publicclassUserServiceImplimplementsUserService{
@Resource
privateTransactionManagertxManager;
@Resource
privateUserDaouserDao;
@Resource
privateAddressDaoaddressDao;

publicbooleansaveUser(Useruser){
TransactionDefinitiontxDefinition=newTransactionDefinition();
TransactionStatustxStatus=txManager.getTransaction(txDefinition);
booleanresult=false;
try{
result=userDao.save(user);
if(!result){
returnfalse;
}
result=addressDao.save(user.getId(),user.getAddress());
txManager.commit(txStatus);
}catch(Exceptione){
result=false;
txManager.rollback(txStatus);
}
returnresult;
}
}</code>
我们借用这个逻辑将事务管理相关提取到切面中,并在进入目标方法之前,让多个TransactionManager都开启事务,并在成功执行后一并提交或失败后一并回滚,具体代码:

8
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
<codeclass="hljs"java="">/**
*@authorZhu
*@date2015-7-15
*@version0.0.1
*@description
*/
publicclassMultiTransactionalAspect{

privateLoggerlogger=LoggerFactory.getLogger(getClass());

publicObjectaround(ProceedingJoinPointpjp,
MultiTransactionalmultiTransactional)throwsThrowable{

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

try{

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

Objectret=pjp.proceed();

commit(dataSourceTransactionManagerStack,transactionStatuStack);

returnret;
}catch(Throwablee){

rollback(dataSourceTransactionManagerStack,transactionStatuStack);

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

/**
*@authorZhu
*@date2015-7-25下午7:55:46
*@description
*@paramdataSourceTransactionManagerStack
*@paramtransactionStatuStack
*@paramvalues
*/
privatebooleanopenTransaction(
Stack<datasourcetransactionmanager>dataSourceTransactionManagerStack,
Stack<transactionstatus>transactionStatuStack,
MultiTransactionalmultiTransactional){

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

for(StringbeanName:transactionMangerNames){
DataSourceTransactionManagerdataSourceTransactionManager=(DataSourceTransactionManager)ContextHolder
.getBean(beanName);
TransactionStatustransactionStatus=dataSourceTransactionManager
.getTransaction(newDefaultTransactionDefinition());
transactionStatuStack.push(transactionStatus);
dataSourceTransactionManagerStack
.push(dataSourceTransactionManager);
}
returntrue;
}

/**
*@authorZhu
*@date2015-7-25下午7:56:39
*@description
*@paramdataSourceTransactionManagerStack
*@paramtransactionStatuStack
*/
privatevoidcommit(
Stack<datasourcetransactionmanager>dataSourceTransactionManagerStack,
Stack<transactionstatus>transactionStatuStack){
while(!dataSourceTransactionManagerStack.isEmpty()){
dataSourceTransactionManagerStack.pop().commit(
transactionStatuStack.pop());
}
}

/**
*@authorZhu
*@date2015-7-25下午7:56:42
*@description
*@paramdataSourceTransactionManagerStack
*@paramtransactionStatuStack
*/
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来保存TransactionManager和TransactionStatus呢?那是因为Spring的事务处理是按照LIFO/stackbehavior的方式进行的。如若顺序有误,则会报错:

?

1
2
3
4
5
6
<codeavrasm=""class="hljs">java.lang.IllegalStateException:Cannotdeactivatetransactionsynchronization-notactive
atorg.springframework.transaction.support.TransactionSynchronizationManager.clearSynchronization(TransactionSynchronizationManager.java:313)
atorg.springframework.transaction.support.TransactionSynchronizationManager.clear(TransactionSynchronizationManager.java:451)
atorg.springframework.transaction.support.AbstractPlatformTransactionManager.cleanupAfterCompletion(AbstractPlatformTransactionManager.java:986)
atorg.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:782)
atorg.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactio</code>

题外话

刚开始碰到这个问题的时候,先想到的是分布式事务管理,也去看了JTA相关的文章,但是好像比较麻烦,而且都是一些老文章,于是想试试自己实现,最后也实现了。所以想知道JTATransactionManager究竟有什么用呢?
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: