Implementing multi-data source transaction management in spring
Preface
Since multiple data sources are introduced in the project and there is a need to write to multiple data sources, transaction management of multiple data sources is naturally an unavoidable problem, which gives me a @Transactional
further understanding of the annotations (but not really in depth).
However, this is an evolutionary process, the beginning of the project did not use the @Transactional
specified specific TransactionManager
, so the new data source, the original transaction has an impact, which is occasionally in a test error and results did not rollback after the discovery, and then for @Transactional
the annotations of some parameter items are understood.
Research
Since there are two containers in the container TransactionManager
, then the @Transactional
annotated method is used TransactionManager
to manage the transaction, or the use of two at the same time for TransactionManager
transaction management is a problem we need to figure out.
First, let's see if @Transactional
there are any configuration items on the annotations to specify, sure enough TransactionManager
, the discovery value
attribute is used to specify TransactionManager
the specific, through id
or name
to specify the only one TransactionManager
, The problem is much simpler for a method that requires only one transaction management:
@Transactional(value"database2TransactionManager") publicvoidtest(String a) { // business operation }
About which TransactionManager will be used when TransactionManager is not specified, interested children's shoes can refer to another article, speaking more clearly: Http://blog.sina.com.cn/s/blog_ 8f61307b0100ynfb.html
Well, back to the question we're studying, what about business methods that need to write to multiple data sources?
further study
It @Transactional
does not seem to provide such a function, then you write a bar. I remember the transaction management of Spring in the sub-programming transaction and the declarative transaction. We usually use @Transactional
declarative transactions, the benefits of which are actually more flexible, the code is less coupled, the final transaction management implementation is the same, but the specific logic is stripped into the plane. So we can write a "programmatic transaction" by hand-writing a slice, which is, of course, declarative when used in a specific application.
General programmatic transaction notation in Java:
Public class Userserviceimpl implements userservice { @Resource PrivateTransactionManager Txmanager;@Resource PrivateUserdao Userdao;@Resource PrivateAddressdao Addressdao; Public Boolean Saveuser(User user) {Transactiondefinition txdefinition =NewTransactiondefinition (); Transactionstatus txstatus = txmanager.gettransaction (txdefinition);Booleanresult =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); }returnResult }}
We use this logic to extract the transaction management correlation into the tangent plane, and before entering the target method, let more than one TransactionManager
open the transaction, and after the successful execution together or fail after the rollback, the specific code:
/** * @author Zhu * @date 2015-7-15 * @version 0.0.1 * @description * * Public class multitransactionalaspect { PrivateLogger Logger = Loggerfactory.getlogger (GetClass ()); PublicObjectaround(Proceedingjoinpoint PJP, multitransactional multitransactional)throwsthrowable {stack<datasourcetransactionmanager> Datasourcetransactionmanagerstack =NewStack<datasourcetransactionmanager> (); Stack<transactionstatus> Transactionstatustack =NewStack<transactionstatus> ();Try{if(!opentransaction (Datasourcetransactionmanagerstack, Transactionstatustack, multitransactional)) {return NULL; } 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 (). g Etname ()), e);ThrowE } }/** * @author Zhu * @date 2015-7-25 pm 7:55:46 * @description * @param datasourcetransactionmanagerstack * @param transactionstatustack * @param Values * * Private Boolean opentransaction(Stack<datasourcetransactionmanager> Datasourcetransactionmanagerstack, stack<transactions Tatus> Transactionstatustack, multitransactional multitransactional) {string[] Transactionmangername s = multitransactional.values ();if(Arrayutils.isempty (Multitransactional.values ())) {return false; } for(String beanname:transactionmangernames) {Datasourcetransactionmanager Datasourcetransactionmanager = (datasourcetransactionmanager) ContextHolder . Getbean (Beanname); Transactionstatus transactionstatus = Datasourcetransactionmanager. Gettransaction (NewDefaulttransactiondefinition ()); Transactionstatustack.push (Transactionstatus); Datasourcetransactionmanagerstack. push (Datasourcetransactionmanager); }return true; }/** * @author Zhu * @date 2015-7-25 pm 7:56:39 * @description * @param datasourcetransactionmanagerstack * @param transactionstatustack * * Private void Commit(Stack<datasourcetransactionmanager> Datasourcetransactionmanagerstack, stack<transactions Tatus> transactionstatustack) { while(!datasourcetransactionmanagerstack.isempty ()) {Datasourcetransactionmanagerstack.pop (). Commit (Transactionstatustack.pop ()); } }/** * @author Zhu * @date 2015-7-25 pm 7:56:42 * @description * @param datasourcetransactionmanagerstack * @param transactionstatustack * * Private void rollback(Stack<datasourcetransactionmanager> Datasourcetransactionmanagerstack, stack<transactions Tatus> transactionstatustack) { while(!datasourcetransactionmanagerstack.isempty ()) {Datasourcetransactionmanagerstack.pop (). Rollback (Transactionstatustack.pop ()); } }
The overall structure is clear:
1. First of all, according to the specified number of TransactionManager
open transactions, this order does not affect, because in fact, everyone is equal.
2. The next step is to invoke the target method to execute the specific business logic
3. If the return is successful, each transaction is submitted, and if the error is dropped, then each transaction is rolled back
Why do you use Stack
it to TransactionManager
save TransactionStatus
and? That's because Spring 's transactions are done in the lifo/stack behavior Way. If the order is incorrect, the error will be:
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
Off Topic
Beginning to encounter this problem, the first thought is distributed transaction management, but also to see the JTA related articles, but it seems to be more troublesome, and are some old articles, so want to try their own implementation, and finally realized. So you want to know JTA TransactionManager
what's the use of it?
Copyright NOTICE: This article for Bo Master original article, without Bo Master permission not reproduced.
Implementing multi-data source transaction management in spring