Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

Sample Analysis of transaction commit in Spring declarative transaction Management

2025-01-15 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/01 Report--

This article mainly shows you the "sample analysis of transaction commits in Spring declarative transaction management", which is easy to understand and well-organized. I hope it can help you solve your doubts. Let me lead you to study and learn the "sample analysis of transaction commits in Spring declarative transaction management".

Spring declarative transaction management source code interpretation of the transaction begins

In fact, my feeling is that transaction commit is more complex than transaction start. Depending on whether the transaction is committed or not, we still have to go back to the invoke method of the TransactionInterceptor class:

Java code

Public Object invoke (MethodInvocation invocation) throws Throwable {/ / Work out the target class: may be null.// The TransactionAttributeSource should be passed the target class// as well as the method, which may be from an interfaceClass targetClass = (invocation.getThis ()! = null)? Invocation.getThis () .getClass (): null;// Create transaction if necessary.TransactionInfo txInfo = createTransactionIfNecessary (invocation.getMethod (), targetClass); Object retVal = null;try {/ / This is an around advice.// Invoke the next interceptor in the chain.// This will normally result ina target object being invoked.retVal = invocation.proceed ();} catch (Throwable ex) {/ / target invocation exceptiondoCloseTransactionAfterThrowing (txInfo, ex); throw ex;} finally {doFinally (txInfo) / / A method} doCommitTransactionAfterReturning (txInfo); return retVal;} that must be executed after the business method is unstacked

The doFinally (txInfo) line is very important, that is, the doFinally method is called anyway. Why is it so important? for example:

Let's take propregation_required as an example. Suppose this is the case. There is a method in AService that calls the method in BService. Both methods are in the transaction body, and their route of transmission is required. So the call begins. The method of AService first enters the method stack and creates an instance of TransactionInfo, then the method of BService goes into the stack and creates an instance of TransactionInfo. The key point is that TransactionInfo is an inner class associated with itself. When the second method enters the stack, it will set a property to the instance of the newly created TransactionInfo, that is, the private TransactionInfo oldTransactionInfo in the TransactionInfo object. Property, which indicates that the TransactionInfo object created by the BService method has a transactionInfo object of old, which is the TransactionInfo object created when the AService method enters the stack. We remember that there is such a method in the createTransactionIfNecessary method:

Java code

Protected TransactionInfo createTransactionIfNecessary (Method method, Class targetClass) {/ / We always bind the TransactionInfo to the thread, even if we didn't create// a new transaction here. This guarantees that the TransactionInfo stack// will be managed correctly even if no transaction was created by this aspect.txInfo.bindToThread (); return txInfo;} is the problem with the bindToThread () method: private void bindToThread () {/ / Expose current TransactionStatus, preserving any existing transactionStatus for// restoration after this transaction is complete.oldTransactionInfo = (TransactionInfo) currentTransactionInfo.get (); currentTransactionInfo.set (this);}

If there is already a TransactionInfo in the current thread, put it in the oldTransactionInfo property of the newly created transactionInfo object, and then set the new TransactionInfo to the current thread.

A concept to be clear here is that the TransactionInfo object is not an object that indicates the state of the transaction, but that the object of the state of the transaction is a TransactionStatus object, which is also a property of TransactionInfo.

Next, the method in BService returns, so it's time for it to unstack. What it needs to do is the doFinally method, that is, to set its oldTransactionInfo to the current thread. (this TransactionInfo object is obviously created when the AService method enters the stack. Why should it be set to the thread now? the reason is that the BService method does not commit transactions when it leaves the stack, because BService is propagated through required. So set the transactioninfo created by the method at the top of the stack to the current thread), that is, the TransactionInfo object created when the method of AService is called. Then the oldTransactionInfo of the TransactionInfo object will also be set to the current thread when the method of AServie is unstacked, so obviously oldTransactionInfo is empty, but the method in AService commits the transaction, so its oldTransactionInfo should also be empty.

After this episode, it should be time to commit the transaction. Earlier, when AService's method was unstacked, we got the TransactionInfo object that was created when it entered the stack, which contains the method transaction state of AService. That is, the TransactionStatus object, obviously, it is too obvious that any property in the transaction commit is closely related to the object created at the beginning of the transaction. Where did this TransactionStatus object come from? let's go back to the createTransactionIfNessary method:

Java code

Protected TransactionInfo createTransactionIfNecessary (Method method, Class targetClass) {txInfo.newTransactionStatus (this.transactionManager.getTransaction (txAttr));

Take another look at the transactionManager.getTransaction (txAttr) method:

Java code

Public final TransactionStatus getTransaction (TransactionDefinition definition) throws TransactionException {else if (definition.getPropagationBehavior () = = TransactionDefinition.PROPAGATION_REQUIRED | | definition.getPropagationBehavior () = = TransactionDefinition.PROPAGATION_REQUIRES_NEW | | definition.getPropagationBehavior () = = TransactionDefinition.PROPAGATION_NESTED) {if (debugEnabled) {logger.debug ("Creating new transaction with name [" + definition.getName () + "]");} doBegin (transaction, definition); boolean newSynchronization = (this.transactionSynchronization! = SYNCHRONIZATION_NEVER); return newTransactionStatus (definition, transaction, true, newSynchronization, debugEnabled, null) / / notice that the return value here is a TransactionStatus object, which indicates the status of a transaction, such as whether it is a new transaction, whether the transaction has ended, and so on. This object is very important and will be used when the transaction commits. }}}

It is also important to note that the transactionstatus created by AService's method before execution is indeed created by this method, but the way BService's method creates transactionstatus before execution is different from this, which will be explained in more detail below.

After reviewing the methods called at the beginning of the transaction, do you feel that it is becoming clearer and clearer about how spring handles the transaction? Because of these several method calls, the transaction state of each method is set before it is put on the stack. This transaction state is prepared to be called when the method is off the stack.

Let's go back to the time when the method in BService went out of the stack and take a look at what spring has done. We know that the last method in the stack must be out of the stack first, and the method in BService must be out of the stack first. When it goes out of the stack, it needs to determine whether to commit the transaction and release resources. Let's take a look at the method doCommitTransactionAfterReturning of TransactionInterceptor's invoke:

Java code

Protected void doCommitTransactionAfterReturning (TransactionInfo txInfo) {if (txInfo! = null & & txInfo.hasTransaction ()) {if (logger.isDebugEnabled ()) {logger.debug ("Invoking commit for transaction on" + txInfo.joinpointIdentification ());} this.transactionManager.commit (txInfo.getTransactionStatus ()); / / look: the TransactionStatus object that indicates the status of the transaction is used when committing the transaction. }}

If you look at the name of this method, you can see that spring is going to commit a transaction when the business method is off the stack. It seems simple, but is this really the case? Let's move on.

Java code

Public final void commit (TransactionStatus status) throws TransactionException {

DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status

If (defStatus.isCompleted ()) {

Throw new IllegalTransactionStateException (

"Transaction is already completed-do not call commit or rollback more than once per transaction")

}

If (defStatus.isLocalRollbackOnly ()) {

If (defStatus.isDebug ()) {

Logger.debug ("Transactional code has requested rollback")

}

ProcessRollback (defStatus)

Return

}

If (! shouldCommitOnGlobalRollbackOnly () & & defStatus.isGlobalRollbackOnly ()) {

If (defStatus.isDebug ()) {

Logger.debug ("Global transaction is marked as rollback-only but transactional code requested commit")

}

ProcessRollback (defStatus)

Throw new UnexpectedRollbackException (

"Transaction has been rolled back because it has been marked as rollback-only")

}

ProcessCommit (defStatus)

}

The above code is the commit in transactionmanager, but it seems that it has assigned its own responsibility to someone else. We can see from the code that if the transaction is over, throw an exception, and if the transaction is rollbackonly, then rollback it, but according to the normal process, we still want to take a look at the transaction commit, which is the processCommit (status) method.

Java code

Private void processCommit (DefaultTransactionStatus status) throws TransactionException {

Try {

Boolean beforeCompletionInvoked = false

Try {

TriggerBeforeCommit (status)

TriggerBeforeCompletion (status)

BeforeCompletionInvoked = true

If (status.hasSavepoint ()) {

If (status.isDebug ()) {

Logger.debug ("Releasing transaction savepoint")

}

Status.releaseHeldSavepoint ()

}

Else if (status.isNewTransaction ()) {/ / this judgment is very important. The function of this judgment will be explained in detail below.

If (status.isDebug ()) {

Logger.debug ("Initiating transaction commit")

}

Boolean globalRollbackOnly = status.isGlobalRollbackOnly ()

DoCommit (status)

/ / Throw UnexpectedRollbackException if we have a global rollback-only

/ / marker but still didn't get a corresponding exception from commit.

``

}

We notice that there is a status.hasSavepoint () judgment before judging whether a transaction is a new transaction. I think this judgment is in fact the judgment of a nested transaction, that is, to judge whether the transaction is a nested transaction, and if it is not a nested transaction, then determine whether it is a new transaction. The following paragraph is very important. The method in BService is out of the stack first. That is to say, the transaction state object created before calling BService is judged here, but since a Transaction and Session have been created before calling the BService method (assuming we are using hibernate3), it is time to create a second TransactionInfo (again, TransactionInfo is not a real transaction object, TransactionInfo is just an auxiliary class The TransactionStatus of the helper class used to record a series of states will enter the following method (of course, before that, we will determine whether there is already a SessionHolder object in the current thread. For students who are not clear about the role of SessionHolder, please see * article):

Java code

Private TransactionStatus handleExistingTransaction (

TransactionDefinition definition, Object transaction, boolean debugEnabled)

Throws TransactionException {

If (definition.getPropagationBehavior ()) = = TransactionDefinition.PROPAGATION_NEVER) {

Throw new IllegalTransactionStateException (

"Transaction propagation 'never' but existing transaction found")

}

If (definition.getPropagationBehavior ()) = = TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {

If (debugEnabled) {

Logger.debug ("Suspending current transaction")

}

Object suspendedResources = suspend (transaction)

Boolean newSynchronization = (this.transactionSynchronization = = SYNCHRONIZATION_ALWAYS)

Return newTransactionStatus (

Definition, null, false, newSynchronization, debugEnabled, suspendedResources)

}

If (definition.getPropagationBehavior ()) = = TransactionDefinition.PROPAGATION_REQUIRES_NEW) {

If (debugEnabled) {

Logger.debug ("Suspending current transaction, creating new transaction with name [" +)

Definition.getName () + "]")

}

Object suspendedResources = suspend (transaction)

DoBegin (transaction, definition)

Boolean newSynchronization = (this.transactionSynchronization! = SYNCHRONIZATION_NEVER)

Return newTransactionStatus (

Definition, transaction, true, newSynchronization, debugEnabled, suspendedResources)

}

If (definition.getPropagationBehavior ()) = = TransactionDefinition.PROPAGATION_NESTED) {

If (! isNestedTransactionAllowed ()) {

Throw new NestedTransactionNotSupportedException (

"Transaction manager does not allow nested transactions by default -" +

"specify 'nestedTransactionAllowed' property with value' true'")

}

If (debugEnabled) {

Logger.debug ("Creating nested transaction with name [" + definition.getName () + "]")

}

If (useSavepointForNestedTransaction ()) {

/ / Create savepoint within existing Spring-managed transaction

/ / through the SavepointManager API implemented by TransactionStatus.

/ / Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization.

DefaultTransactionStatus status =

NewTransactionStatus (definition, transaction, false, false, debugEnabled, null)

Status.createAndHoldSavepoint ()

Return status

}

Else {

/ / Nested transaction through nested begin and commit/rollback calls.

/ / Usually only for JTA: Spring synchronization might get activated here

/ / in case of a pre-existing JTA transaction.

DoBegin (transaction, definition)

Boolean newSynchronization = (this.transactionSynchronization! = SYNCHRONIZATION_NEVER)

Return newTransactionStatus (definition, transaction, true, newSynchronization, debugEnabled, null)

}

}

/ / Assumably PROPAGATION_SUPPORTS.

If (debugEnabled) {

Logger.debug ("Participating in existing transaction")

}

Boolean newSynchronization = (this.transactionSynchronization! = SYNCHRONIZATION_NEVER)

Return newTransactionStatus (definition, transaction, false, newSynchronization, debugEnabled, null)

}

We see that this method is actually very clear, that is, what kind of transmission means to create what kind of transactionstatus, this method is called at the beginning of the transaction, from our previous example, we suddenly realized that if the transaction had been created before, the new transactionstauts should not belong to a newTransaction, so the third parameter is false.

That is, the method unstack in BService will execute processcommit, but since the TransactionStatus of BService is not a newTransaction, it will not trigger this action at all:

Java code

Else if (status.isNewTransaction ()) {/ / this judgment is very important. The function of this judgment will be explained in detail below: if (status.isDebug ()) {logger.debug ("Initiating transaction commit");} boolean globalRollbackOnly = status.isGlobalRollbackOnly (); doCommit (status);}

That is, after the BService method is unstacked, the transaction will not be committed. This fits perfectly with propragation_required 's model.

After the AService method is unstacked, the newTransaction property of the TransactionStatus object corresponding to the AService method is true, that is, it triggers the above code for the actual transaction commit. Let's recall that the TransactionStatus object was created before the AService method went into the stack:

NewTransactionStatus (definition, transaction, true, newSynchronization, debugEnabled, null)

You can see that the third parameter is true.

So it's time for the transaction to be committed. I think anyone who has used hibernate knows how to commit:

TxObject.getSessionHolder (). GetTransaction (). Commit ()

Get the SessionHolder from the current thread, get the Transaction object where the transaction started, and then commit the transaction. We used to do this before we used spring.

This is all the content of the article "sample Analysis of transaction commits in Spring declarative transaction Management". Thank you for reading! I believe we all have a certain understanding, hope to share the content to help you, if you want to learn more knowledge, welcome to follow the industry information channel!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report