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

Introduction to transaction problems of Springboot

2025-01-19 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >

Share

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

This article mainly explains "the introduction of Springboot transaction". The content of the explanation is simple and clear, and it is easy to learn and understand. Please follow the editor's train of thought to study and learn "introduction to Springboot transaction".

Summary:

Transactions are ubiquitous in back-end development and are the most basic guarantee of data consistency. To understand that the essence of a transaction is to enter the transaction aspect of the proxy method, the most common is that a non-transactional method of the same class calls a method annotated by the transaction that does not enter the transaction. We take the cglib agent as an example, because of Spring's implementation of the cglib AOP agent, we have actually left the "proxy shell" when entering the proxied method, so we can think that the code goes to a simple bean, and the method in the same bean naturally has nothing to do with the agent. Declarative transactions are generally entered by calling the @ Transactional annotated public method of another class.

Spring transaction key processing flow

EnableTransactionManagement comments are imported into TransactionManagementConfigurationSelector

TransactionManagementConfigurationSelector loads InfrastructureAdvisorAutoProxyCreator (but not necessarily it, usually AnnotationAwareAspectJAutoProxyCreator). A key step of BeanFactoryTransactionAttributeSourceAdvisor,TransactionInterceptor-AnnotationAwareAspectJAutoProxyCreator in the ioc process is to find Advisor, which has two aspects, the first is a class that implements the Advisor interface, and the second is based on annotated Aspectj. The point is that BeanFactoryTransactionAttributeSourceAdvisor is loaded into the proxy cache.

When the agent calls the method, it executes the DefaultAdvisorChainFactory#getInterceptorsAndDynamicInterceptionAdvice, which will set our

BeanFactoryTransactionAttributeSourceAdvisor is useful. The most important thing is that the TransactionAttributeSourcePointcut in it matches and executes the TransactionInterceptor method.

TransactionInterceptor @ Override @ Nullable public Object invoke (MethodInvocation invocation) throws Throwable {/ / Work out the target class: may be {@ code null} / / The TransactionAttributeSource should be passed the target class / / as well as the method, which may be from an interface. Class targetClass = (invocation.getThis ()! = null? AopUtils.getTargetClass (invocation.getThis ()): null); / / Adapt to TransactionAspectSupport's invokeWithinTransaction... Return invokeWithinTransaction (invocation.getMethod (), targetClass, invocation::proceed);} TransactionAspectSupport @ Nullable protected Object invokeWithinTransaction (Method method, @ Nullable Class targetClass, final InvocationCallback invocation) throws Throwable {/ / If the transaction attribute is null, the method is non-transactional. TransactionAttributeSource tas = getTransactionAttributeSource (); final TransactionAttribute txAttr = (tas! = null? Tas.getTransactionAttribute (method, targetClass): null); final PlatformTransactionManager tm = determineTransactionManager (txAttr); final String joinpointIdentification = methodIdentification (method, targetClass, txAttr); if (txAttr = = null | |! (tm instanceof CallbackPreferringPlatformTransactionManager)) {/ / Standard transaction demarcation with getTransaction and commit/rollback calls. TransactionInfo txInfo = createTransactionIfNecessary (tm, txAttr, joinpointIdentification); Object retVal; try {/ / This is an around advice: Invoke the next interceptor in the chain. / / This will normally result in a target object being invoked. RetVal = invocation.proceedWithInvocation ();} catch (Throwable ex) {/ / target invocation exception completeTransactionAfterThrowing (txInfo, ex); throw ex;} finally {cleanupTransactionInfo (txInfo);} commitTransactionAfterReturning (txInfo); return retVal } else {final ThrowableHolder throwableHolder = new ThrowableHolder (); / / It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in. Try {Object result = ((CallbackPreferringPlatformTransactionManager) tm) .execute (txAttr, status-> {TransactionInfo txInfo = prepareTransactionInfo (tm, txAttr, joinpointIdentification, status); try {return invocation.proceedWithInvocation () } catch (Throwable ex) {if (txAttr.rollbackOn (ex)) {/ / A RuntimeException: will lead to a rollback. If (ex instanceof RuntimeException) {throw (RuntimeException) ex;} else {throw new ThrowableHolderException (ex);}} else {/ / A normal return value: will lead to a commit. ThrowableHolder.throwable = ex; return null;}} finally {cleanupTransactionInfo (txInfo);}}); / / Check result state: It might indicate a Throwable to rethrow. If (throwableHolder.throwable! = null) {throw throwableHolder.throwable;} return result;} catch (ThrowableHolderException ex) {throw ex.getCause () } catch (TransactionSystemException ex2) {if (throwableHolder.throwable! = null) {logger.error ("Application exception overridden by commit exception", throwableHolder.throwable); ex2.initApplicationException (throwableHolder.throwable);} throw ex2 } catch (Throwable ex2) {if (throwableHolder.throwable! = null) {logger.error ("Application exception overridden by commit exception", throwableHolder.throwable);} throw ex2;}

This time the method is analyzed, but the problem is analyzed from the point of view of transaction exception, ineffectiveness and so on. The core idea of annotated transactions is the same as that of programmers. Let's analyze the logic of annotated transactions.

If (txAttr = = null | |! (tm instanceof CallbackPreferringPlatformTransactionManager)) {/ / Standard transaction demarcation with getTransaction and commit/rollback calls. TransactionInfo txInfo = createTransactionIfNecessary (tm, txAttr, joinpointIdentification); Object retVal; try {/ / This is an around advice: Invoke the next interceptor in the chain. / / This will normally result in a target object being invoked. RetVal = invocation.proceedWithInvocation ();} catch (Throwable ex) {/ / target invocation exception completeTransactionAfterThrowing (txInfo, ex); throw ex } finally {/ / rebind the TxInfo of the previous transaction to the ThreadLocal cleanupTransactionInfo (txInfo);} commitTransactionAfterReturning (txInfo); return retVal }

Please remember the logical order of these core methods and exception catch!

Protected void completeTransactionAfterThrowing (@ Nullable TransactionInfo txInfo) Throwable ex) {if (txInfo! = null & & txInfo.getTransactionStatus ()! = null) {if (logger.isTraceEnabled ()) {logger.trace ("Completing transaction for [" + txInfo.getJoinpointIdentification () + "] after exception:" + ex) } / / transaction rollback exception supports if (txInfo.transactionAttribute! = null & & txInfo.transactionAttribute.rollbackOn (ex)) {try {txInfo.getTransactionManager () .rollback (txInfo.getTransactionStatus () } catch (TransactionSystemException ex2) {logger.error ("Application exception overridden by rollback exception", ex); ex2.initApplicationException (ex) Throw ex2;} catch (RuntimeException | Error ex2) {logger.error ("Application exception overridden by rollback exception", ex) Throw ex2;}} else {/ / We don't roll back on this exception. / / Will still roll back if TransactionStatus.isRollbackOnly () is true. Try {txInfo.getTransactionManager () .commit (txInfo.getTransactionStatus ());} catch (TransactionSystemException ex2) {logger.error ("Application exception overridden by commit exception", ex) Ex2.initApplicationException (ex); throw ex2 } catch (RuntimeException | Error ex2) {logger.error ("Application exception overridden by commit exception", ex); throw ex2 }} the exception of transaction rollback supports @ Override public boolean rollbackOn (Throwable ex) {return (ex instanceof RuntimeException | | ex instanceof Error);}

Note that only runtime exceptions and error mechanisms are supported, otherwise they will not be rolled back. And carry on the direct condition.

AbstractPlatformTransactionManager private void processRollback (DefaultTransactionStatus status, boolean unexpected) {try {/ / default false boolean unexpectedRollback = unexpected; try {/ / callback the beforeCompletion method of the TransactionSynchronization object. TriggerBeforeCompletion (status); if (status.hasSavepoint ()) {if (status.isDebug ()) {logger.debug ("Rolling back transaction to savepoint");} status.rollbackToHeldSavepoint () } / / rollback else if (status.isNewTransaction ()) {if (status.isDebug ()) {logger.debug ("Initiating transaction rollback") at the outermost transaction boundary;} / / rollback is implemented by the concrete TxMgrr subclass. DoRollback (status);} else {/ / Participating in larger transaction if (status.hasTransaction ()) {/ * * when the inner transaction is marked as rollBackOnly or globalRollbackOnParticipationFailure switch on, marking the current transaction needs to be rolled back. * * if the inner transaction is explicitly marked with rollBackOnly, eventually the whole transaction must be rolled back. * * but if it is not marked with rollBackOnly, the globalRollbackOnParticipationFailure switch is important. * the globalRollbackOnParticipationFailure switch is enabled by default, which means that the inner transaction is dead, and the final result can only be a full transaction rollback. * however, if the globalRollbackOnParticipationFailure switch is turned off, the inner transaction dies, and the business method of the outer transaction can control whether to roll back according to the situation. * / if (status.isLocalRollbackOnly () | | isGlobalRollbackOnParticipationFailure ()) {if (status.isDebug ()) {logger.debug ("Participating transaction failed-marking existing transaction as rollback-only");} / / rollback is implemented by the concrete TxMgra subclass. DoSetRollbackOnly (status);} else {if (status.isDebug ()) {logger.debug ("Participating transaction failed-letting transaction originator decide on rollback") } else {logger.debug ("Should rollback transaction but cannot-no transaction available");} / / Unexpected rollback only matters here if we're asked to fail early if (! isFailEarlyOnGlobalRollbackOnly ()) {unexpectedRollback = false } catch (RuntimeException | Error ex) {triggerAfterCompletion (status, TransactionSynchronization.STATUS_UNKNOWN); throw ex;} / / callback the afterCompletion method of the TransactionSynchronization object. TriggerAfterCompletion (status, TransactionSynchronization.STATUS_ROLLED_BACK); / / Raise UnexpectedRollbackException if we had a global rollback-only marker if (unexpectedRollback) {throw new UnexpectedRollbackException ("Transaction rolled back because it has been marked as rollback-only");}} finally {cleanupAfterCompletion (status);}} case study

Experienced students must know that the whole transaction was eventually rolled back, and TransactionB#test did not execute System.out.println ("TransactionB#test after"); in fact, for Spring transactions, this result is correct, but to developers, the result does seem a bit "incomprehensible".

We might as well analyze the reasons:

First of all, TransactionB#test itself throws RuntimeException directly, then after dropping back to the transaction aspect, the transaction aspect will find that it needs to be rolled back, but because TransactionB#test is not the outermost boundary of the transaction, the AbstractPlatformTransactionManager#processRollback method will only call doSetRollbackOnly (status); the subclass DataSourceTransactionManager will take out the transaction object in DefaultTransactionStatus and mark it with a rollback mark. Specifically, the transaction object (DataSourceTransactionObject for DataSourceTransactionManager) will fetch ConnectionHolder and call setRollbackOnly. We know that this is equivalent to a global tag, because any Spring transaction that belongs to the same physical transaction can read the same ConnectionHolder.

Protected void doSetRollbackOnly (DefaultTransactionStatus status) {DataSourceTransactionManager.DataSourceTransactionObject txObject = (DataSourceTransactionManager.DataSourceTransactionObject) status.getTransaction (); if (status.isDebug ()) {this.logger.debug ("Setting JDBC transaction [" + txObject.getConnectionHolder (). GetConnection () + "] rollback-only");} / / key point txObject.setRollbackOnly ();}

Going back to the upper transaction aspect, read the if (! shouldCommitOnGlobalRollbackOnly () & & defStatus.isGlobalRollbackOnly ()) condition in the AbstractPlatformTransactionManager#commit method, and then call processRollback. Because it will be physically rolled back at the outermost boundary of the transaction, and it is at the outermost boundary of the transaction, Spring throws UnexpectedRollbackException.

How to solve?

So how to solve the problem? there are several solutions to this problem, but they have to be decided on a case-by-case basis.

According to the actual code and business situation, Spring will not throw a UnexpectedRollbackException if the embedded transaction annotation is cancelled. However, the method is not fully implemented, so this solution can easily lead to incomplete dirty data.

Manually controls whether to roll back. If an embedded transaction hang is not acceptable, you can add TransactionAspectSupport.currentTransactionStatus (). SetRollbackOnly () to the catch block; to explicitly control the rollback. This way Spring understands that you are asking for a rollback of transactions, not unexpected. Spring will not throw UnexpectedRollbackException either. So if you catch an exception in the upper-level transaction, you really don't want to roll back, even if there is an exception in the upper-level transaction, you want to finally commit the whole transaction? If there is such a requirement, you can configure a parameter setGlobalRollbackOnParticipationFailure (false) for the transaction manager.

If isGlobalRollbackOnParticipationFailure is false, the main transaction is left to decide to roll back, and if the exception fails to join the transaction, the caller can continue to decide whether to roll back or continue within the transaction. However, it is important to note that this method can be solved only in the case of data access failure and as long as all operational transactions can be committed, but obviously affects the global transaction properties, so it is highly recommended.

Public final void commit (TransactionStatus status) throws TransactionException {if (status.isCompleted ()) {throw new IllegalTransactionStateException ("Transaction is already completed-do not call commit or rollback more than once per transaction");} DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status; if (defStatus.isLocalRollbackOnly ()) {if (defStatus.isDebug ()) {logger.debug ("Transactional code has requested rollback") } processRollback (defStatus, false); return;} if (! shouldCommitOnGlobalRollbackOnly () & & defStatus.isGlobalRollbackOnly ()) {if (defStatus.isDebug ()) {logger.debug ("Global transaction is marked as rollback-only but transactional code requested commit");} processRollback (defStatus, true); return } processCommit (defStatus);} Thank you for your reading. The above is the content of "introduction to Springboot's transaction issues". After the study of this article, I believe you have a deeper understanding of the introduction of Springboot's transaction issues, and the specific use needs to be verified in practice. Here is, the editor will push for you more related knowledge points of the article, welcome to follow!

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

Internet Technology

Wechat

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

12
Report