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

An example Analysis of transaction start of Spring declarative transaction Management Source Code

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

Share

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

This article shares with you the content of a sample analysis of the transaction start of Spring declarative transaction management source code. The editor thinks it is very practical, so share it with you as a reference and follow the editor to have a look.

In spring's declarative transaction management, how does it determine whether an and mark a method should be in the transaction body?

The first thing to understand is how spring marks whether a method should be in the transaction body. There is an interface, TransactionDefinition, where many constants are defined, and a subinterface TransactionAttribute, where there is only one method, rollback.

There are many constant definitions in TransactionDefinition, which belong to two types, transmission path and isolation level.

Code:

/ * Supportacurrenttransaction,createanewoneifnoneexists. * AnalogoustoEJBtransactionattributeofthesamename. * Thisistypicallythedefaultsettingofatransactiondefinition. * / intPROPAGATION_REQUIRED=0

Of course, the isolation level is also defined.

/ * *

Code

* Aconstantindicatingthatdirtyreadsareprevented;non-repeatablereads * andphantomreadscanoccur.Thislevelonlyprohibitsatransaction * fromreadingarowwithuncommittedchangesinit. * @ seejava.sql.Connection#TRANSACTION_READ_COMMITTED * / intISOLATION_READ_COMMITTED = Connection.TRANSACTION_READ_COMMITTED;

At the same time, there are two corresponding ways to get this route of transmission and the level of isolation.

Code

/ * Returnthepropagationbehavior. * MustreturnoneofthePROPAGATIONconstants. * @ see#PROPAGATION_REQUIRED * @ seeorg.springframework.transaction.support.TransactionSynchronizationManager#isActualTransactionActive () * / intgetPropagationBehavior (); / * * Returntheisolationlevel. * MustreturnoneoftheISOLATIONconstants. *

This interface has a default implementation DefaultTransactionDefinition. And then it has subclasses, such as

DefaultTransactionAttribute . Spring actually creates an instance of a TransactionAttribute implementation when determining whether a method requires a transaction body.

With the brief introduction above, you can enter the place where you can really determine whether a transaction is needed or not. This method is in the TransactionAspectSupport class

Code

/ * Createatransactionifnecessary. * @ parammethodmethodabouttoexecute * @ paramtargetClassclassthemethodison * @ returnaTransactionInfoobject,whetherornotatransactionwascreated. * ThehasTransaction () methodonTransactionInfocanbeusedtotellifthere * wasatransactioncreated. * / protectedTransactionInfocreateTransactionIfNecessary (Methodmethod,ClasstargetClass) {/ / Ifthetransactionattributeisnull,themethodisnon-transactional. FinalTransactionAttributesourceAttr= this.transactionAttributeSource.getTransactionAttribute (method,targetClass); / / it is here that the transaction attribute TransactionAttributetxAttr=sourceAttr; / / Ifnonamespecified,applymethodidentificationastransactionname of this method is determined. If (txAttr.getName () = = null) {finalStringname=methodIdentification (method); txAttr=newDelegatingTransactionAttribute (sourceAttr) {publicStringgetName () {returnname;}};} TransactionInfotxInfo=newTransactionInfo (txAttr,method); / / TransactionInfo is an inner class of TransactionAspectSupport, its main function is to record methods and corresponding transaction attributes if (txAttrics null) {/ / Weneedatransactionforthismethod if (logger.isDebugEnabled ()) {logger.debug ("Gettingtransactionfor" + txInfo.joinpointIdentification ()) } / / Thetransactionmanagerwillflaganerrorifanincompatibletxalreadyexists txInfo.newTransactionStatus (this.transactionManager.getTransaction (txAttr)) / / this method should take a closer look at} else {/ / TheTransactionInfo.hasTransaction () methodwillreturn / / false.Wecreateditonlytopreservetheintegrityof / / theThreadLocalstackmaintainedinthisclass. If (logger.isDebugEnabled ()) logger.debug ("Don'tneedtocreatetransactionfor [" + methodIdentification (method) + "]: thismethodisn'ttransactional");} / / WealwaysbindtheTransactionInfotothethread,evenifwedidn'tcreate / / anewtransactionhere.ThisguaranteesthattheTransactionInfostack / / willbemanagedcorrectlyevenifnotransactionwascreatedbythisaspect. TxInfo.bindToThread (); returntxInfo;}

TransactionInfo is an inner class of TransactionAspectSupport, its main function is to record the method and the corresponding transaction properties, in the above method, the TransactionInfo object is saved to the current thread.

This method is called in the transaction interceptor TransactionInterceptor, where TransactionInterceptor is actually a subclass of TransactionAspectSupport. Look at the invoke method in it:

Code

/ / Workoutthetargetclass:maybenull.

/ / TheTransactionAttributeSourceshouldbepassedthetargetclass

/ / aswellasthemethod,whichmaybefromaninterface

ClasstargetClass= (invocation.getThis ()! = null)? invocation.getThis () .getClass (): null;

/ / Createtransactionifnecessary.

TransactionInfotxInfo=createTransactionIfNecessary (invocation.getMethod (), targetClass);

ObjectretVal=null;

Try {

/ / Thisisanaroundadvice.

/ / Invokethenextinterceptorinthechain.

/ / Thiswillnormallyresultinatargetobjectbeinginvoked.

RetVal=invocation.proceed ();

}

Catch (Throwableex) {

/ / targetinvocationexception

DoCloseTransactionAfterThrowing (txInfo,ex);

Throwex;

}

Finally {

DoFinally (txInfo);

}

DoCommitTransactionAfterReturning (txInfo); / / perform the operations required after the method ends here

ReturnretVal;

This method is just like what a normal interceptor needs to implement. It's just in this method to determine whether the reflected method requires a transaction.

Then let's focus on this sentence in the createTransactionIfNecessary method:

TxInfo.newTransactionStatus (this.transactionManager.getTransaction (txAttr));

Then we should take a look at this getTransaction method, assuming that we are using hibernate3, and so on. Before we look at getTransaction, let's take a look at these two categories and one interface.

Interface PlatformTransactionManager

Abstract class public abstract class AbstractPlatformTransactionManager implements PlatformTransactionManager

Class public class HibernateTransactionManager extends AbstractPlatformTransactionManager, it is obvious that there is a method template pattern.

Let's take a look at the getTransaction method in AbstractPlatformTransactionManager:

Code

PublicfinalTransactionStatusgetTransaction (TransactionDefinitiondefinition) throwsTransactionException {

Objecttransaction=doGetTransaction (); / / Abstract methods, which also require subclass implementation, are equally important

/ / Cachedebugflagtoavoidrepeatedchecks.

BooleandebugEnabled=logger.isDebugEnabled ();

If (debugEnabled) {

Logger.debug ("Usingtransactionobject [" + transaction+ "]");

}

If (definition==null) {

/ / Usedefaultsifnotransactiondefinitiongiven.

Definition=newDefaultTransactionDefinition ();

}

If (isExistingTransaction (transaction)) {

/ / Existingtransactionfound- > checkpropagationbehaviortofindouthowtobehave.

ReturnhandleExistingTransaction (definition,transaction,debugEnabled);

}

/ / Checkdefinitionsettingsfornewtransaction.

If (definition.getTimeout () checkpropagationbehaviortofindouthowtobehave.

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

ThrownewIllegalTransactionStateException (

"Transactionpropagation'mandatory'butnoexistingtransactionfound")

}

Elseif (definition.getPropagationBehavior ()) = = TransactionDefinition.PROPAGATION_REQUIRED | |

Definition.getPropagationBehavior () = = TransactionDefinition.PROPAGATION_REQUIRES_NEW | |

Definition.getPropagationBehavior () = = TransactionDefinition.PROPAGATION_NESTED) {

If (debugEnabled) {

Logger.debug ("Creatingnewtransactionwithname [" + definition.getName () + "]");

}

DoBegin (transaction,definition);

BooleannewSynchronization= (this. TransactionSynchronizationSYNCHRONIZATION newer);

ReturnnewTransactionStatus (definition,transaction,true,newSynchronization,debugEnabled,null);

}

Else {

/ / Create "empty" transaction:noactualtransaction,butpotentiallysynchronization.

BooleannewSynchronization= (this.transactionSynchronization==SYNCHRONIZATION_ALWAYS);

ReturnnewTransactionStatus (definition,null,false,newSynchronization,debugEnabled,null);

}

}

The above code is explained in many places, so it is easy to understand that the key part of this code is here in doBegin (transaction,definition) (this is an abstract method, and the subclass must implement this method

Concrete depends on abstraction, which is a summary of the method template pattern. ), as mentioned earlier, let's assume that we are using hibernate, so let's take a look at the class HibernateTransactionManager. The parameter 1 HibernateTransactionObject transaction in doBegin is actually an instance of HibernateTransactionObject. What is mainly stored in this instance is the session and transaction objects that start the transaction in sessionholder,sessionholder. If no sessionholder is stored in the thread before, then the property of the instance of HibernateTransactionObject is actually empty, which can be seen in the implementation of the doBegin method.

Code

ProtectedvoiddoBegin (Objecttransaction,TransactionDefinitiondefinition) {

If (getDataSource ()! = null&&TransactionSynchronizationManager.hasResource (getDataSource () {

ThrownewIllegalTransactionStateException (

"Pre-boundJDBCConnectionfound-HibernateTransactionManagerdoesnotsupport" +

"runningwithinDataSourceTransactionManageriftoldtomanagetheDataSourceitself." +

"ItisrecommendedtouseasingleHibernateTransactionManagerforalltransactions" +

"onasingleDataSource,nomatterwhetherHibernateorJDBCaccess."

}

Sessionsession=null;

Try {

HibernateTransactionObjecttxObject= (HibernateTransactionObject) transaction;

If (txObject.getSessionHolder ()) = = null) {

InterceptorentityInterceptor=getEntityInterceptor ();

SessionnewSession= (entityInterceptorships) null?

GetSessionFactory (). OpenSession (entityInterceptor): getSessionFactory (). OpenSession ();

If (logger.isDebugEnabled ()) {

Logger.debug ("OpenednewSession [" + newSession+ "] forHibernatetransaction");

}

TxObject.setSessionHolder (newSessionHolder (newSession), true);} /

We see that if there is no sessionholder in the incoming transaction, then create a new session, put it in the new sessionholder, and then put it in the instance of HibernateTransactionObject. By the way, the name of this variable is really bad. Although it is written by Juergen Hoeller, you have to approve it, so that others will think it is an instance of Transaction.

Code

TxObject.getSessionHolder () setSynchronizedWithTransaction (true);

Session=txObject.getSessionHolder (). GetSession ();

Connectioncon=session.connection ();

IntegerpreviousIsolationLevel=DataSourceUtils.prepareConnectionForTransaction (con,definition);

TxObject.setPreviousIsolationLevel (previousIsolationLevel);

If (definition.isReadOnly () & & txObject.isNewSessionHolder ()) {

/ / JustsettoNEVERincaseofanewSessionforthistransaction.

Session.setFlushMode (FlushMode.NEVER);

} / / if it is a read-only transaction and sessionholder is newly created, set the flushmode of hibernate to never

If (! definition.isReadOnly () & &! txObject.isNewSessionHolder ()) {

/ / WeneedAUTOorCOMMITforanon-read-onlytransaction.

FlushModeflushMode=session.getFlushMode ();

If (FlushMode.NEVER.equals (flushMode)) {

Session.setFlushMode (FlushMode.AUTO);

/ / if the flushmode of session is nerver, set it to auto, because if the transaction is defined as non-readonly, then the session must be flush.

TxObject.getSessionHolder () setPreviousFlushMode (flushMode);

}

}

/ / AddtheHibernatetransactiontothesessionholder.

TxObject.getSessionHolder (). SetTransaction (session.beginTransaction ()); / / start a transaction and put the transaction object into the sessionholder, and then the sessionholder is put into the thread through threadlocal for use in the commit

/ / Registertransactiontimeout.

If (definition.getTimeout ()! = TransactionDefinition.TIMEOUT_DEFAULT) {

TxObject.getSessionHolder (). SetTimeoutInSeconds (definition.getTimeout ()); / / sets the timeout. If the timeout is-1, it is not set. If it is not-1, then the timeout is set as newDate (System.currentTimeMillis () + millis*1000). The programmer actually specifies the number of seconds in the configuration file.

}

/ / RegistertheHibernateSession'sJDBCConnectionfortheDataSource,ifset.

If (getDataSource ()! = null) {

ConnectionHolderconHolder=newConnectionHolder (con);

If (definition.getTimeout ()! = TransactionDefinition.TIMEOUT_DEFAULT) {

ConHolder.setTimeoutInSeconds (definition.getTimeout ())

}

If (logger.isDebugEnabled ()) {

Logger.debug ("ExposingHibernatetransactionasJDBCtransaction [" + con+ "]");

}

TransactionSynchronizationManager.bindResource (getDataSource (), conHolder);

TxObject.setConnectionHolder (conHolder);

}

/ / Bindthesessionholdertothethread.

If (txObject.isNewSessionHolder ()) {

TransactionSynchronizationManager.bindResource (getSessionFactory (), txObject.getSessionHolder ()); / / bind to the thread if it is a new sessionholder. So you can get the entire sessionholder when you enter the next method in the method stack, and so does connectionholder.

}

}

Catch (Exceptionex) {

SessionFactoryUtils.releaseSession (session,getSessionFactory ()); / / release the session if an exception is thrown, and this operation will occur later

ThrownewCannotCreateTransactionException ("CouldnotopenHibernateSessionfortransaction", ex);

}

}

As you can see from the above comments on the code, if you set up declarative transaction management for service, assume that the transaction propagation path is required, and then when one service calls another service, they actually share a session. The principle is that if there is no creation, it will not be created, and the previously created session and transaction will be returned. That is to say, spring puts the session and the corresponding transaction into the thread through threadlocal, ensuring that the same session and transaction can be obtained anywhere in the entire method stack.

So if your method is within the transaction body, then you just need to get the session through hibernatesupportdao or hibernatetemplate, then this session must be the session that started the transaction, and the main way to get session is in SessionFactoryUtils. Let's take a look.

(there is also a small detail here. Public abstract class SessionFactoryUtils, Juergen Hoeller uses abstract when writing tool classes in order not to have instances, while our general practice is the construction method of final class plus private, which does not look very elegant. You can still learn a lot of writing skills by looking at the source code.)

In the doGetSession of SessionFactoryUtils, it is written that if the current thread has bound session, the session is returned. If session is not bound, it depends on whether creation is allowed (whether the parameter allowCreate is true or false, which will be designed in many places, such as hibernatetemplate and hibernatedaosupport). If creation is not allowed, throw a raw hibernateException, for example, if you do not configure a service method with declarative transaction management Instead, you need to get the current session in the dao called by the service, so you will report this error:

Code

If (method.getName (). Equals ("getCurrentSession")) {/ / HandlegetCurrentSessionmethod:returntransactionalSession,ifany Try {returnSessionFactoryUtils.doGetSession ((SessionFactory) proxy,false); / / * one parameter is false, which means that this method cannot return a new session. If there is no exception,} catch (IllegalStateExceptionex) {thrownewHibernateException (ex.getMessage ());}} Thank you for reading! On "Spring declarative transaction management source code transaction start example analysis" this article is shared here, I hope the above content can be of some help to you, so that you can learn more knowledge, if you think the article is good, you can share it out for more people to see it!

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