Inventory of JPA: Spring-based transaction management

Inventory of JPA: Spring-based transaction management

Total documentation: article directory
Github: github.com/black-ant

I. Introduction

This article talks about transaction management related processes, taking Spring Data JPA as an example.

The purpose of the article:

  • Sort out the main process of Spring Transaction
  • Explain the core principles of Spring Transaction
  • Parameter passing in the process

One sentence principle:

  • Implement database-level transaction management through SQL START TRANSACTION; + COMMIT;
  • The overall control is carried out through the proxy, and the START TRANSACTION is turned on through the proxy class
  • Transaction rollback through Mysql rollback

Main process:

  • TransactionManager
  • JpaTransactionManager
  • PlatformTransactionManager

2. Transaction processing flow

The core of transaction management is the object TransactionManager . Let's take a look at his family system:

As you can probably see, the main implementation classes are:

C- DataSourceTransactionManager C- JdoTransactionManager C- JpaTransactionManager C- HibernateTransactionManager C- JtaTransactionManager C- OC4JjtaTransactionManager C- WebSphereUowTransactionManager C- WebLogicJtaTransactionManager Copy code

2.1 Transaction interception entry

The starting point of the transaction is intercepted by Interceptor, and its proxy method is also implemented through AOP, and its entry class is CglibAopProxy

C01- TransactionInterceptor E- TransactionAspectSupport -> PS: 001 M01_01- invoke(MethodInvocation invocation) //M01_01 @Override @Nullable public Object invoke (MethodInvocation invocation) throws Throwable { //Pass the target class and method to TransactionAttributeSource Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis( )): null ); //Call invokeWithinTransaction here to officially handle return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed); } Copy code

PS: 001 The role of TransactionAspectSupport

The role of class: Transaction base class, sub-class is responsible for the correct order to call methods in this class
features: Design Policy-based mode

2.2 Interception of the process

  • Step 1: Property preparation
    • TransactionAttributeSource : TransactionInterceptor's strategy interface for metadata retrieval
    • TransactionAttribute : This interface adds the rollbackOn specification to the TransactionDefinition.
    • PlatformTransactionManager : Transaction Management Platform
    • joinpointIdentification
  • Step 2: Divided into 2 types of processing
    • There is a method for executing a given callback in a transaction
    • There is no callback method

C02- TransactionAspectSupport M02_01- invokeWithinTransaction(Method method, Class<?> targetClass, InvocationCallback invocation) P- method: current original method P- targetClass: the current original class P- invocation: proxy object //1- Property preparation -getTransactionAttributeSource(): Get TransactionAttributeSource -tas.getTransactionAttribute(method, targetClass): Get TransactionAttribute -determineTransactionManager(txAttr): Get PlatformTransactionManager -methodIdentification(method, targetClass, txAttr): //Type A: ------------- A2- Main tone operation -createTransactionIfNecessary(tm, txAttr, joinpointIdentification) ?- If necessary, create a transaction based on the given TransactionAttribute -> M02_02 -invocation.proceedWithInvocation(): core interface -> PS:M02_01_01 ?- Simple callback interface for target call, the specific interceptor/aspect is determined by their call mechanism A3- catch operation -completeTransactionAfterThrowing(txInfo, ex) A4- finally operation -cleanupTransactionInfo(txInfo) -> PS:M02_04_01 A5- Commit the transaction -commitTransactionAfterReturning(txInfo) -> PS:M02_05_01 //Type B: ------------------------ B2- CallbackPreferringPlatformTransactionManager.execute B3- prepareTransactionInfo(tm, txAttr, joinpointIdentification, status): Build TransactionInfo B4- invocation.proceedWithInvocation() M02_02- createTransactionIfNecessary(PlatformTransactionManager tm,TransactionAttribute txAttr, String joinpointIdentification) 1 -tm.getTransaction(txAttr): Get TransactionStatus -> M03_01 2 -prepareTransactionInfo(tm, txAttr, joinpointIdentification, status) ?- deal with M02_03- prepareTransactionInfo 1 - new TransactionInfo(tm, txAttr, joinpointIdentification): Construct a TransactionInfo 2 -txInfo.newTransactionStatus(status): If an incompatible tx already exists, the transaction manager will mark an error 3 -txInfo.bindToThread(): even if it is not in Create a new transaction here, and also bind TransactionInfo to the thread ?- This ensures that even if no transaction is created in this area, the TransactionInfo stack will be managed correctly M02_04- cleanupTransactionInfo: reset TransactionInfo ThreadLocal -txInfo.restoreThreadLocalStatus() -> PS:M02_04_01 //PS: M02_01_01 core processing //This method will execute the final state method, the main process is C- RepositoryComposition # invoke(Method method, Object... args) C- RepositoryFactorySupport # doInvoke C- SimpleJpaRepository # save C- Loader # loadEntity C- Loader # doQuery C- ConnectionImpl # prepareStatement //PS: The role of M02_04_01 TODO: The role of this attribute protected void cleanupTransactionInfo ( @Nullable TransactionInfo txInfo) { if (txInfo != null ) { txInfo.restoreThreadLocalStatus(); } } Copy code

Parameter details

Take a look at the relevant parameter details here

M02_01 source code

protected Object invokeWithinTransaction (Method method, @Nullable Class<?> targetClass, final InvocationCallback invocation) throws Throwable { //Step 1: Property preparation 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 = null ; 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; } } } Copy code

3.3 Process processing

How to call this method:

  • TransactionAspectSupport # invokeWithinTransaction
  • TransactionAspectSupport # createTransactionIfNecessary
  • AbstractPlatformTransactionManager # getTransaction

M02_02 source code, first look at how to getTransaction in the second step

protected TransactionInfo createTransactionIfNecessary ( @Nullable PlatformTransactionManager tm, @Nullable TransactionAttribute txAttr, final String joinpointIdentification) { //If no name is specified, apply the method ID as the transaction name if (txAttr != null && txAttr.getName() == null ) { txAttr = new DelegatingTransactionAttribute(txAttr) { @Override public String getName () { return joinpointIdentification; } }; } TransactionStatus status = null ; if (txAttr != null ) { if (tm != null ) { //When both txAttr and tm exist, call AbstractPlatformTransactionManager status = tm.getTransaction(txAttr); } else { //log only } } return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status); } Copy code

As you can see, the relevant code is status = tm.getTransaction(txAttr)

There are 2 TransactionManagers in total:

  • AbstractPlatformTransactionManager : Total abstract transaction manager
  • JpaTransactionManager : Because it is based on JPA, here is JpaTransactionManager
C03- AbstractPlatformTransactionManager M03_01- getTransaction(TransactionDefinition definition) 1 -doGetTransaction(): Call subclass transaction management -> M04_01 2 -newTransactionStatus(definition, transaction, true , newSynchronization, debugEnabled, suspendedResources) -> PS: M03_01_01 3 -doBegin(transaction, definition) ?- According to the given transaction definition, use semantics to start a new transaction -> PS:M03_01_02 4 -prepareSynchronization(status, definition) M03_02- commit(TransactionStatus status) 1 -processRollback(defStatus, false ): Process callback operation -> M03_03 2 -processCommit(defStatus): -> M03_04 M03_04- processCommit(defStatus): The main commit process 1 -pre-processing -prepareForCommit(status); -triggerBeforeCommit(status); -triggerBeforeCompletion(status); 2 -Perform different logic through different DefaultTransactionStatus 3 -Trigger the callback after commit, and propagate the exception thrown there to the caller, but the transaction is still considered committed -triggerAfterCommit(status) -triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED) 4 -Clean up after completion, clear synchronization if necessary, and call doCleanupAfterCompletion -cleanupAfterCompletion(status) //Call subclass transaction management, use JpaTransactionManager here C04- JpaTransactionManager M04_01- doGetTransaction() ?- This method is mainly to create and process JpaTransactionObject 1 -setSavepointAllowed -TransactionSynchronizationManager.getResource: Get EntityManagerHolder 2 -setEntityManagerHolder: -TransactionSynchronizationManager.getResource(getDataSource()): ConnectionHolder 3 -setConnectionHolder: Wrap the resource holder of JDBC M04_02- doBegin(Object transaction, TransactionDefinition definition) 1 -Get the JpaTransactionObject created by M04_01 2 -txObject.getEntityManagerHolder().getEntityManager(): Get EntityManager 3 -determineTimeout(definition): Prepare timeout 4 -getJpaDialect().beginTransaction: Delegate to JpaDialect to start the actual transaction -> PS :M04_02_05 5 -txObject.getEntityManagerHolder().setTimeoutInSeconds(timeoutToUse): If timeout, set the timeout period 6 -IF- If the JDBC connection of JPA EntityManager is set -getJpaDialect().getJdbcConnection(em, definition.isReadOnly()): Get ConnectionHandle -Generate ConnectionHolder through ConnectionHandle -TransactionSynchronizationManager.bindResource(getDataSource(), conHolder): -txObject.setConnectionHolder(conHolder) 7 -txObject.getEntityManagerHolder().setSynchronizedWithTransaction( true ): mark the resource as synchronized with the transaction //PS: M04_02_05 actual transaction processing flow: mainly 2 sentences Object transactionData = getJpaDialect().beginTransaction(em, new JpaTransactionDefinition(definition, timeoutToUse, txObject.isNewEntityManagerHolder())); txObject.setTransactionData(transactionData); //The parameter situation of the process processing attention class C- NativeSession # execSQL //1 select orgentity0_.id as id1_0_0_, orgentity0_.org_name as org_name2_0_0_, orgentity0_.org_type as org_type3_0_0_ from org orgentity0_ where orgentity0_.id=? select userentity0_.userid as userid1_1_0_, userentity0_.isactive as isactive2_1_0_, userentity0_.orgid as orgid3_1_0_, userentity0_.remark as remark4_1_0_, userentity0_.userlink as userlink5_1_0_.userlink as userlink5_1_0_.userlink as userlink5_1_0_ usertype as user_entity_username from user_entity_0 user_entity_0 user_entity_0 2177281 select orgentity0_.id as id1_0_0_, orgentity0_.org_name as org_name2_0_0_, orgentity0_.org_type as org_type3_0_0_ from org orgentity0_ where orgentity0_.id = 5052474 select userentity0_.userid as useridentity_1 is_0_0, useridentity as active_0_0. remark4_1_0_, userentity0_.userlink as userlink5_1_0_, userentity0_.username as username6_1_0_, userentity0_.usertype as usertype7_1_0_ from user userentity0_ where userentity0_.userid = 4412226 ....... Copy code

PS: M03_01_01 newTransactionStatus

Role: Create a TransactionStatus instance with the given parameters

protected DefaultTransactionStatus newTransactionStatus (TransactionDefinition definition, @Nullable Object transaction, boolean newTransaction, boolean newSynchronization, boolean debug, @Nullable Object suspendedResources) { boolean actualNewSynchronization = newSynchronization &&!TransactionSynchronizationManager.isSynchronizationActive(); return new DefaultTransactionStatus(transaction, newTransaction, actualNewSynchronization, definition.isReadOnly(), debug, suspendedResources); } Copy code

S: M03_01_02 specific process

Role: According to the given transaction definition, use semantics to start a new transaction . Since it has been processed by the abstract manager, there is no need to care about the application of the propagation behavior. This method is called when the transaction manager decides to actually start a new transaction . Either there was no previous transaction, or the previous transaction has been suspended.

S: What did you do in M02_05_01?

txInfo.getTransactionManager().commit(txInfo.getTransactionStatus())

As you can see, the specific Commit -> AbstractPlatformTransactionManager is called here through the TransactionManager

C03- AbstractPlatformTransactionManager M03_02- commit(TransactionStatus status) //The main logic of commit public final void commit (TransactionStatus status) throws TransactionException { if (status.isCompleted()) { throw new IllegalTransactionStateException( "....." ); } DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status; if (defStatus.isLocalRollbackOnly()) { processRollback(defStatus, false ); return ; } if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) { processRollback(defStatus, true ); return ; } //Core call logic, submit commit processing processCommit(defStatus); } Copy code

3.5 Exception handling

Exception handling mainly the rollback of the relevant operation, which mainly C03- AbstractPlatformTransactionManager in

Calling Rollback: Observing that Rollback is not only called when an exception occurs, there will be different scenarios here, there are two main types :

  • Transaction code request rollback:
    processRollback(defStatus, false)
  • The global transaction is marked as rollback only but the transaction code is requested:
    processRollback(defStatus, true)
//The main flow of transaction rollback C03- AbstractPlatformTransactionManager M03_03- processRollback(DefaultTransactionStatus status, boolean unexpected): Process the actual rollback, check the completed flag //Type 1: Rollback under normal execution 1 -triggerBeforeCompletion(status) 2 -triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK) //Type 2: Call doCallback to roll back the situation 1 -triggerBeforeCompletion(status) 2 -doRollback(status) 3 -triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK) M03_05- triggerBeforeCompletion(status) 1 -TransactionSynchronizationUtils.triggerBeforeCompletion() M03_06- triggerAfterCompletion(status) M03_07- doRollback(status) ?- This is an interface that needs to be implemented by subclasses. Take JPA as an example, the JpaTransactionManager used here C04- JpaTransactionManager F04_01- EntityManagerFactory entityManagerFactory; M04_03- doRollback //Step 1: Get properties -> PS:M04_03_01 1 -status.getTransaction(): Get JpaTransactionObject //Step 2: Execute EntityTransaction 2 -txObject.getEntityManagerHolder().getEntityManager().getTransaction() -> PS:M04_03_02 3 -tx.rollback() -> PS:M04_03_03 -The main calling object //Step 3: finally mandatory handling 4 -txObject.getEntityManagerHolder().getEntityManager().clear() C05- TransactionImpl M05_01- rollback -TransactionStatus status = getStatus(): Get TransactionStatus --> PS:M05_01_01 -status.canRollback(): Whether it can be rolled back, if not, an exception will be thrown --> PS: M05_01_02 -internalGetTransactionDriverControl().rollback(): execute rollback--> PS:M05_01_03 //Initiate command callback Copy code

PS: M04_03_01 parameter details

JpaTransactionObject parameter details

As you can see here, each operation will form an EntityInsertAction

PS: M05_01_03 fallback logic details

The core class is JdbcResourceLocalTransactionCoordinatorImpl, and the fallback has the following call stack ( some parts are omitted in the middle ):

setAutoCommit link

  • JpaTransactionManager # doBegin
  • HibernateJpaDialect # beginTransaction
  • TransactionImpl # begin
  • JdbcResourceLocalTransactionCoordinatorImpl # begin
  • HikariProxyConnection # setAutoCommit
  • ConnectionImpl # setAutoCommit
  • NativeProtocol # sendQueryString

SQL execution link

  • HikariProxyPreparedStatement # executeQuery
  • NativeSession # execSQL
  • NativeProtocol # sendQueryString

PS: The final execution statements are:

  • NativeProtocol # sendQueryString
C11- JdbcResourceLocalTransactionCoordinatorImpl M- rollback() C12- AbstractLogicalConnectionImplementor M- rollback() -getConnectionForTransactionManagement().rollback() ?- Call ProxyConnection -status = TransactionStatus.ROLLED_BACK C13- ProxyConnection M13_01- rollback() //M13_01 rollback() source code public void rollback () throws SQLException { delegate.rollback(); isCommitStateDirty = false ; lastAccess = currentTime(); } //Here is com.mysql.cj.jdbc C14- ConnectionImpl M14_01- rollback() -rollbackNoChecks() private void rollbackNoChecks () throws SQLException { synchronized (getConnectionMutex()) { if ( this .useLocalTransactionState.getValue()) { if (! this .session.getServerSession().inTransactionOnServer()) { return ;//effectively a no- op } } //Rollback core logic this .session.execSQL( null , "rollback" ,- 1 , null , false , this .nullStatementResultSetFactory, this .database, null , false ); } } //Final processing C- NativeProtocol # sendQueryString return sendQueryPacket (callingQuery, sendPacket, maxRows, streamResults, catalog, cachedMetadata, getProfilerEventHandlerInstanceFunction, resultSetFactory) ; sendPacket -> ?rollback //PS: MySQL transactions are involved here MySQL's ROLLBACK command is used to roll back (undo) MySQL statements START TRANSACTION or BEGIN starts a new transaction. COMMIT commits the current transaction, making its changes permanent. ROLLBACK rolls back the current transaction and cancels its changes SET auto-submit disables or enables the default auto-submit mode of the current session. By default, automatic commit mode is enabled when MySQL is running. This means that if there are no other statements in the transaction, then each statement is atomic, as if it were surrounded by START TRANSACTION and COMMIT. You cannot use ROLLBACK to undo this effect; however, if an error occurs during statement execution, the statement will be rolled back. //To implicitly disable auto-commit mode for a series of statements, use the START TRANSACTION statement: START TRANSACTION; SELECT @A :=SUM(salary) FROM table1 WHERE type = 1 ; UPDATE table2 SET summary = @A WHERE type = 1 ; COMMIT; //For details, please refer to the official document: https://dev.mysql.com/doc/refman/5.7/en/commit.html Copy code

summary

The overall process is probably going through the JPA Transaction process, but there has always been a little doubt. At this stage, what we can see is that it is done through SQL's own transaction management, and it is not sure whether there are other links to control or through business processing .

appendix

Mysql transaction rollback mechanism

HTTPS official document @: //dev.mysql.com/doc/refman/8.0/en/innodb-autocommit-commit-rollback.html copy the code

Let's take a look first, the official case:

In InnoDB, all user activities occur in one transaction. If auto-commit mode is enabled, each SQL statement forms a transaction by itself.

Note that this processing is invalid in MyMyISAM!!

CREATE TABLE customer (a INT , b CHAR ( 20 ), INDEX (a)); # As you can see here, you can directly insert it when running INSERT INTO customer VALUES ( 10 , 'Heikki' ); Copy code

Type 1: Multi-statement transaction commit

  • If commit is not executed here, the current operation will not be submitted
# Step 1 : Open transaction and automatic commit # A session with auto-commit enabled can execute a multi-statement transaction by starting with an explicit START TRANSACTION or BEGIN statement and ending with a COMMIT or ROLLBACK statement. START TRANSACTION; # Step 2 : Insert a record, under the premise of automatic submission, a SQL is a transaction INSERT INTO customer VALUES ( 10 , 'Heikki' ); # Step 3 : Submit (after submission, the data will be officially added) # If a session with auto-commit disabled ends without explicitly committing the final transaction, MySQL will roll back the transaction. # COMMIT means that the changes made in the current transaction are permanent and COMMIT is visible to other sessions ; Copy code

Type 2: Rollback operation

# Step 1 : Set non-automatic submission # If the auto-commit mode is disabled in a session with setautocommit = 0 , the session always opens a transaction. The COMMIT or ROLLBACK statement ends the current transaction and starts a new transaction. SET autocommit = 0 ; # Step 2 : Insert all data INSERT INTO customer VALUES ( 15 , 'John' ); INSERT INTO customer VALUES ( 20 , 'Paul' ); DELETE FROM customer WHERE b = 'Heikki' ; # Step 3 : Roll back the transaction ROLLBACK ; # As you can see, DELETE Heikki is not executed, and the first insert is successful SELECT * FROM customer; copy the code

Transaction processing for this case

START TRANSACTION; SET autocommit = 0 ; # Step 2 : Insert a record, under the premise of automatic submission, a SQL is a transaction INSERT INTO org VALUES ( 10 , '111' , 'Heikki' ); INSERT INTO org VALUES ( 20 , '222' , 'Paul' ); # DELETE FROM org WHERE id = 'Heikki' ; # Step 3 : Roll back the transaction ROLLBACK ; # As you can see, DELETE Heikki is not executed, and the first insert is successful SELECT * FROM org; copy the code

Observed SQL operations in this case

# When the project starts ? SET autocommit = 1 # Step 1 : How to enter ? SET autocommit = 0 The Step # 2 : execute SQL ? The SELECT userentity0_.userid AS userid1_1_0_, userentity0_.isactive AS isactive2_1_0_, userentity0_.orgid AS orgid3_1_0_, userentity0_.remark AS remark4_1_0_, userentity0_.userlink AS userlink5_1_0_, userentity0_.username AS username6_1_0_, userentity0_.usertype AS usertype7_1_0_ from user userentity0_ where userentity0_.userid = 3138559 ? select orgentity0_.id as id1_0_0_, orgentity0_.org_name asorg_name2_0_0_, orgentity0_.org_type AS org_type3_0_0_ from ORG orgentity0_ WHERE orgentity0_.id = 7614913 tity0_.userlink AS userlink5_1_0_, userentity0_.username AS username6_1_0_, userentity0_.usertype AS usertype7_1_0_ from User userentity0_ WHERE userentity0_.userid = 3,138,559 // The Step . 3 : insertion ? insert into user ..... ? insert into org ........ ? commit ? SET autocommit = 1 // If an exception occurs, will call ROLLBACK // PS: To automatically submit reservations ( the SET autocommit = 1 ), please Start Transaction beginning of each transaction, and to COMMIT or ROLLBACK end Copy the code

PS: Here you can get Debug C- NativeProtocol # sendQueryString

M04_01: JpaTransactionManager # doGetTransaction() source code

protected Object doGetTransaction () { JpaTransactionObject txObject = new JpaTransactionObject(); txObject.setSavepointAllowed(isNestedTransactionAllowed()); //Encapsulation of resource holders in JPA EntityManagerHolder emHolder = (EntityManagerHolder) //The central delegate that manages the resources and transaction synchronization of each thread TransactionSynchronizationManager.getResource(obtainEntityManagerFactory()); if (emHolder != null ) { txObject.setEntityManagerHolder(emHolder, false ); } if (getDataSource() != null ) { //ConnectionHolder: Wrap JDBC resource holder //For a specific javax.sql. datasource, bind an instance of this class to the thread. //Note: This is an SPI class and is not intended to be used by applications. ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(getDataSource()); txObject.setConnectionHolder(conHolder); } return txObject; } Copy code

M04_02: JpaTransactionManager # doBegin() source code

protected void doBegin (Object transaction, TransactionDefinition definition) { //JPA transaction object, representing EntityManagerHolder. JpaTransactionManager is used as a transaction object. JpaTransactionObject txObject = (JpaTransactionObject) transaction; if (txObject.hasConnectionHolder() && !txObject.getConnectionHolder().isSynchronizedWithTransaction()) { throw new IllegalTransactionStateException(....); } try { if (!txObject.hasEntityManagerHolder() ||txObject.getEntityManagerHolder().isSynchronizedWithTransaction()) { //Entity class manager EntityManager newEm = createEntityManagerForTransaction(); txObject.setEntityManagerHolder( new EntityManagerHolder(newEm), true ); } EntityManager em = txObject.getEntityManagerHolder().getEntityManager(); final int timeoutToUse = determineTimeout(definition); //Start a transaction Object transactionData = getJpaDialect().beginTransaction(em, new JpaTransactionDefinition(definition, timeoutToUse, txObject.isNewEntityManagerHolder())); // Mark transaction attributes for the current transaction object txObject.setTransactionData(transactionData); if (timeoutToUse != TransactionDefinition.TIMEOUT_DEFAULT) { //control timeout txObject.getEntityManagerHolder().setTimeoutInSeconds(timeoutToUse); } if (getDataSource() != null ) { //Connection management, perform related connection operations ConnectionHandle conHandle = getJpaDialect().getJdbcConnection(em, definition.isReadOnly()); if (conHandle != null ) { ConnectionHolder conHolder = new ConnectionHolder(conHandle); if (timeoutToUse != TransactionDefinition.TIMEOUT_DEFAULT) { conHolder.setTimeoutInSeconds(timeoutToUse); } //Resources are bound here, and a database data is bound to map objects //TODO: This should be the core of the three-state change, let s look at it later TransactionSynchronizationManager.bindResource(getDataSource(), conHolder); txObject.setConnectionHolder(conHolder); } else { //log..... } } if (txObject.isNewEntityManagerHolder()) { TransactionSynchronizationManager.bindResource(obtainEntityManagerFactory(), txObject.getEntityManagerHolder()); } //Mark the resource as synchronized with the transaction txObject.getEntityManagerHolder().setSynchronizedWithTransaction( true ); } catch (TransactionException ex) { closeEntityManagerAfterFailedBegin(txObject); throw ex; } catch (Throwable ex) { closeEntityManagerAfterFailedBegin(txObject); throw new CannotCreateTransactionException( "Could not open JPA EntityManager for transaction" , ex); } } Copy code