General documentation: Article directory Github: github.com/black-ant

A. The preface

This article covers the process related to transaction management, using SpringDataJPA as an example.

The purpose of the article:

  • Sort out the main flow of Spring Transaction
  • This section describes the core principles of Spring Transaction
  • Parameter passing in the process

One sentence principle:

  • SQL START TRANSACTION; + COMMIT; Implement database level transaction management
  • The whole TRANSACTION is controlled by proxy and the START TRANSACTION is started by proxy class
  • The Mysql ROLLBACK transaction was rolled back

Main process:

  • TransactionManager
  • JpaTransactionManager
  • PlatformTransactionManager

2. Transaction processing process

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

As you can see, the main implementation classes are:

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

2.1 Interception entry of transactions

The starting point of a transaction is intercepted by Interceptor, and its proxy is implemented by AOP. 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 {
    / / to TransactionAttributeSource transfer target classes and methodsClass<? > targetClass = (invocation.getThis() ! =null ? AopUtils.getTargetClass(invocation.getThis()) : null);

    // Here invokeWithinTransaction is called for formal processing
    return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
}


Copy the code

PS:001 TransactionAspectSupport

Function of class: transaction base class. Subclasses are responsible for calling methods in this class in the correct order

2.2 Process Interception

  • Step 1: Prepare properties
    • TransactionAttributeSource: TransactionInterceptor interface for metadata retrieval strategy
    • TransactionAttribute: This interface adds the rollbackOn specification to TransactionDefinition.
    • PlatformTransactionManager: transaction management platform
    • joinpointIdentification
  • Step 2: Divide the processing into two types
    • A method exists to perform a given callback in the transaction
    • There is no callback method

C02- TransactionAspectSupport M02_01- invokeWithinTransaction(Method method, Class<? > targetClass, InvocationCallback Invocation) P-method: Current original INVOCATION P- targetClass - Current original INVOCATION P- Object// 1- Attribute preparation- getTransactionAttributeSource () : get TransactionAttributeSource - tas. GetTransactionAttribute (method, targetClass) : Get TransactionAttribute - determineTransactionManager (txAttr) : Get PlatformTransactionManager - methodIdentification (method, targetClass, txAttr) :// Type A: -------------A2 - tone operation - createTransactionIfNecessary (tm, txAttr joinpointIdentification)? - if need be, according to the given TransactionAttribute create a transaction - > M02_02 - invocation. ProceedWithInvocation () : the core interface - > PS: M02_01_01? - Simple callback interface to make the target invocation, the specific interceptors/aspects are determined by their invocation mechanism A3-catchOperations - completeTransactionAfterThrowing A4 - (txInfo, ex)finallyOperation - cleanupTransactionInfo (txInfo) - > PS: M02_04_01 A5 - to 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) : Obtain TransactionStatus -> M03_012- prepareTransactionInfo(tm, txAttr, joinpointIdentification, status) ? - Process M02_03- prepareTransactionInfo1- newTransactionInfo(TM, txAttr, joinpointIdentification) : Builds a TransactionInfo2- txInfo. NewTransactionStatus (status) : if incompatible tx already exists, the transaction manager will mark a mistake3-txinfo.bindtothread () : Binds TransactionInfo to the thread even if a new transaction is not created here? - This ensures that the TransactionInfo stack will be properly managed even if this aspect does not create any transactions. M02_04- cleanupTransactionInfo: Reset TransactionInfo ThreadLocal - txInfo. RestoreThreadLocalStatus () - > PS: M02_04_01// PS:M02_01_01 core processing
// This method executes the final state methodC- RepositoryComposition # invoke(Method method, Object... args) C- RepositoryFactorySupport # doInvoke C- SimpleJpaRepository # save C- Loader # loadEntity C- Loader # doQuery C-  ConnectionImpl # prepareStatement// PS:M02_04_01 functionTODO: What this property doesprotected void cleanupTransactionInfo(@Nullable TransactionInfo txInfo) {
    if(txInfo ! =null) { txInfo.restoreThreadLocalStatus(); }}Copy the code

Parameters for details

Take a look at the parameters here

M02_01 source code

    protected Object invokeWithinTransaction(Method method, @NullableClass<? > targetClass,final InvocationCallback invocation) throws Throwable {

        // Step 1: Prepare attributes
        TransactionAttributeSource tas = getTransactionAttributeSource();
        finalTransactionAttribute txAttr = (tas ! =null ? tas.getTransactionAttribute(method, targetClass) : null);
        final PlatformTransactionManager tm = determineTransactionManager(txAttr);
        final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

        if (txAttr == null| |! (tminstanceof 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 newThrowableHolderException(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);
                }
                throwex2; }}}Copy the code

3.3 Process processing

How to call this method:

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

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


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

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

There are two TransactionManagers:

  • AbstractPlatformTransactionManager: general abstract transaction manager
  • JpaTransactionManager: Because it is based on JPA, this is JpaTransactionManager
C03- AbstractPlatformTransactionManager
    M03_01- getTransaction(TransactionDefinition definition) 
        1- doGetTransaction() : call subclass Transaction Management -> M04_012- newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, suspendedResources) -> PS:M03_01_01
        3- doBegin(transaction, definition) ? - Start a new transaction using semantics based on the given transaction definition -> PS:M03_01_024- prepareSynchronization(status, definition)
    M03_02- commit(TransactionStatus status)    
        1- processRollback(defStatus, false) : Handles callback operations -> M03_032- processCommit(defStatus) : -> M03_04 M03_04- processCommit(defStatus) : indicates the main commit process1PrepareForCommit (status); - triggerBeforeCommit(status); - triggerBeforeCompletion(status);2- Perform different logic with different DefaultTransactionStatus3- Raises the callback after commit and propagates the exception thrown there to the caller, But the transaction is still seen as submitted - triggerAfterCommit (status) - triggerAfterCompletion (status, TransactionSynchronization STATUS_COMMITTED)4- cleanupAfterCompletion, cleanup synchronization if necessary, and call doCleanupAfterCompletion - cleanupAfterCompletion(status)// Call subclass transaction Management, using JpaTransactionManagerC04- JpaTransactionManager M04_01- doGetTransaction() ? - This method is mainly for the creation and processing of JpaTransactionObject1Get EntityManagerHolder setSavepointAllowed - TransactionSynchronizationManager. GetResource:2- setEntityManagerHolder : 
        - TransactionSynchronizationManager.getResource(getDataSource()) : ConnectionHolder
        3- setConnectionHolder: Wrap JDBC resource holder M04_02- doBegin(Object Transaction, TransactionDefinition Definition)1- Obtain the JpaTransactionObject created by M04_012- txObject. GetEntityManagerHolder (.) getEntityManager () : obtain the EntityManager3- determineTimeout(definition) : Indicates the timeout period of preparation4- getJpaDialect().beginTransaction: delegate to JpaDialect to start the actual transaction -> PS:M04_02_055- txObject. GetEntityManagerHolder (.) setTimeoutInSeconds (timeoutToUse) : if a timeout, set the timeout6-if -IF JPA EntityManager JDBC connection is set -getJPaDialect ().getJdbCConnection (em, definition.isreadonly ()) : Get the ConnectionHandle - Generate a ConnectionHolder from the ConnectionHandle - TransactionSynchronizationManager.bindResource(getDataSource(), conHolder) : - txObject.setConnectionHolder(conHolder)7- txObject.getEntityManagerHolder().setSynchronizedWithTransaction(true) : Marks the resource as synchronous with the transaction// PS:M04_02_05 Actual transaction process: mainly 2 sentences
Object transactionData = getJpaDialect().beginTransaction(em,new JpaTransactionDefinition(definition, timeoutToUse, txObject.isNewEntityManagerHolder()));
txObject.setTransactionData(transactionData);


 // The parameters that the process handles are concerned with the 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_, userentity0_.username as username6_1_0_, userentity0_.usertype as usertype7_1_0_ from user userentity0_ where userentity0_.userid=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 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=4412226.Copy the code

PS:M03_01_01 newTransactionStatus

Creates a TransactionStatus instance with the given arguments

protected DefaultTransactionStatus newTransactionStatus(TransactionDefinition definition, @Nullable Object transaction, boolean newTransaction,boolean newSynchronization, boolean debug, @Nullable Object suspendedResources) {

    booleanactualNewSynchronization = newSynchronization &&! TransactionSynchronizationManager.isSynchronizationActive();return new DefaultTransactionStatus(transaction, newTransaction, actualNewSynchronization,
                definition.isReadOnly(), debug, suspendedResources);
}

Copy the code

S:M03_01_02 Specific process

What it does: Starts a new transaction using semantics based on the given transaction definition. Since it is already handled by the abstract manager, the application of propagation behavior does not need to be concerned. This method is called when the transaction manager decides to actually start a new transaction. Either there has been no previous trading or previous trading has been suspended.

S: What’s going on in M02_05_01?

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

Here you can see, through the TransactionManager call specific Commit – > AbstractPlatformTransactionManager

C03- AbstractPlatformTransactionManager
    M03_02- commit(TransactionStatus status)
    
    
// Commit main logic
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, commit processing
        processCommit(defStatus);
}

Copy the code

3.5 Exception Handling

Exception handling is mainly the fallback related operation, the operation is mainly in C03 – AbstractPlatformTransactionManager

Call Rollback: Observe that Rollback is not only called when an exception occurs. There are two types of scenarios:

  • Transaction code requests rollback:processRollback(defStatus, false)
  • Global transactions are marked as rollback only but request commit transaction code:processRollback(defStatus, true)
// The rollback main process of the transaction
C03- AbstractPlatformTransactionManager
    M03_03- processRollback(DefaultTransactionStatus status, booleanUnexpected) : Handle the actual rollback and check for completed flags// Type 1: indicates Rollback in normal cases
        1- triggerBeforeCompletion(status)
        2- triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK) 
        // Type 2: the case where doCallback is called to roll back
        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 requires subclasses to implement, for example, JPA, JpaTransactionManager C04- JpaTransactionManager F04_01- EntityManagerFactory EntityManagerFactory; M04_03- doRollback// Step 1: Obtain properties -> PS:M04_03_01
         1- status.getTransaction() : Obtains the JpaTransactionObject// Step 2: Execute EntityTransaction
         2- txObject.getEntityManagerHolder().getEntityManager().getTransaction() -> PS:M04_03_02
         3Rollback () -> PS:M04_03_03 - Main call object// Step 3: Finally force processing
         4- txObject.getEntityManagerHolder().getEntityManager().clear() C05- TransactionImpl M05_01- rollback - TransactionStatus Status = getStatus() : obtain TransactionStatus --> PS:M05_01_01 - status.canrollback () : Whether can you back, not throw an exception - > PS: M05_01_02 - internalGetTransactionDriverControl (). The rollback () : perform back - > PS: M05_01_03// Initiate the command callback
        


Copy the code

PS:M04_03_01 Parameter details

Details about parameter JpaTransactionObject

As you can see here, each operation forms an EntityInsertAction

PS:M05_01_03 Detailed rollback logic

Core classes for JdbcResourceLocalTransactionCoordinatorImpl, back has the following the call stack (middle part of the omitted) :

SetAutoCommit link

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

SQL execution

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

PS: The final execution statement is:

  • NativeProtocol # sendQueryString
C11- JdbcResourceLocalTransactionCoordinatorImpl M- rollback() C12- AbstractLogicalConnectionImplementor M- rollback() -  getConnectionForTransactionManagement().rollback() ? C13- ProxyConnection M13_01- rollback() -proxyConnection -status = transactionStatus.rolled_back// M13_01 rollback() source code
public void rollback(a) throws SQLException{
      delegate.rollback();
      isCommitStateDirty = false;
      lastAccess = currentTime();
}
    
// This is where com.mysql.cj.jdbc is concerned
C14- ConnectionImpl
    M14_01- rollback()
        - rollbackNoChecks()
    
private void rollbackNoChecks(a) throws SQLException {
    synchronized (getConnectionMutex()) {
        if (this.useLocalTransactionState.getValue()) {
            if (!this.session.getServerSession().inTransactionOnServer()) {
                return; // effectively a no-op}}// Roll back 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: this is where the MySQL transaction is concernedThe MySQL ROLLBACK command starts a new TRANSACTION with the MySQL statement START TRANSACTION or BEGIN. COMMIT Commits the current transaction, making its changes permanent. ROLLBACK Rolls back the current transaction and cancles its changes SET Auto commit Disables or enables the default auto commit mode for the current session. By default, auto-commit mode is enabled at MySQL runtime. This means that if there are no other statements in the TRANSACTION, each statement is atomic, as if it were surrounded by a START TRANSACTION and a COMMIT. You cannot undo the effect with ROLLBACK; However, if an error occurs during statement execution, the statement is rolled back.// To implicitly disable automatic commit mode for a set 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;  
    
/ / specific can refer to the official document: https://dev.mysql.com/doc/refman/5.7/en/commit.html

Copy the code

conclusion

The overall process of JPA Transaction has probably been reviewed, but there has been a bit of doubt, at this stage it seems to be done through SQL’s own Transaction management, not sure whether there are other links to control or through business processing.

The appendix

Mysql transaction rollback mechanism

Official document @https:/ / dev.mysql.com/doc/refman/8.0/en/innodb-autocommit-commit-rollback.html
Copy the code

Let’s take a look at the official case:

In InnoDB, all user activity takes place in a single transaction. If auto-commit mode is enabled, each SQL statement forms its own transaction.

Note that in MyMyISAM, this processing does not work!!

CREATE TABLE customer (a INT, b CHAR (20), INDEX (a)); As you can see here, the run can be directly inserted at this timeINSERT INTO customer VALUES (10.'Heikki');

Copy the code

Type 1: Multi-statement transaction commit

  • If you do not commit here, the current operation will not commit

# Step 1: Enable transactions and autocommit # A session enabling autocommit can be passed explicitlySTARTThe TRANSACTION orBEGINStatement begins withCOMMITROLLBACKStatement terminations to execute multiple statement transactions.START TRANSACTION;


# Step 2: Insert a record, one if automatically committedSQLIt's a transactionINSERT INTO customer VALUES (10.'Heikki');


# Step 3If a session with automatic commit disabled ends without explicitly committing the final transaction, MySQL will roll back the transaction. #COMMITMeans that changes made in the current transaction are permanent and visible to other sessionsCOMMIT;


Copy the code

Type 2: Rollback operation

# Step 1: Set non-auto commit # if in setautoCOMMIT= 0If autocommit mode is disabled in the session, the session always opens a transaction.COMMITROLLBACKStatement ends the current transaction and starts a new one.SET autocommit=0;

# Step 2: Inserts all dataINSERT INTO customer VALUES (15.'John');
INSERT INTO customer VALUES (20.'Paul');
DELETE FROM customer WHERE b = 'Heikki';

# Step 3: Rolls back transactionsROLLBACK; As you can see,DELETEHeikki did not execute, first insertion succeededSELECT * FROM customer;
Copy the code

Transaction processing for this case

START TRANSACTION;
SET autocommit=0;
# Step 2: Insert a record, one if automatically committedSQLIt's a transactionINSERT INTO org VALUES (10.'111'.'Heikki');
INSERT INTO org VALUES (20.'222'.'Paul');
#DELETE FROM org WHERE id = 'Heikki';

# Step 3: Rolls back transactionsROLLBACK; As you can see,DELETEHeikki did not execute, first insertion succeededSELECT * FROM org;
Copy the code

SQL operations observed in this case

# When the project startsSET autocommit=1

# Step 1: Entry methodSET autocommit=0

# Step 2: performSQL
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 as org_name2_0_0_, orgentity0_.org_type as org_type3_0_0_ from org orgentity0_ where orgentity0_.id=7614913tity0_.userlink as userlink5_1_0_, userentity0_.username as username6_1_0_, userentity0_.usertype as usertype7_1_0_ from user userentity0_ where userentity0_.userid=3138559

// Step 3: Insert operationinsert into user.insert into org ........

commit
SET autocommit=1


//If an exception occurs, therollback
//PS: To keep autocommit (SET autocommit=1), pleasestartTransaction starts each transaction and begins withCOMMITROLLBACKThe end of theCopy the code

Debug C- NativeProtocol # sendQueryString

M04_01: JpaTransactionManager # doGetTransaction() source

protected Object doGetTransaction(a) {
    JpaTransactionObject txObject = new JpaTransactionObject();
    txObject.setSavepointAllowed(isNestedTransactionAllowed());
    // The encapsulation of the resource holder in JPA
    EntityManagerHolder emHolder = (EntityManagerHolder)
    // A central delegate that manages resource and transaction synchronization for each thread
    TransactionSynchronizationManager.getResource(obtainEntityManagerFactory());
    if(emHolder ! =null) {
        txObject.setEntityManagerHolder(emHolder, false);
    }
    
    if(getDataSource() ! =null) {
        // ConnectionHolder: the resource holder that wraps JDBC
        // Bind an instance of this class to a thread for a specific Javax.sql. Datasource.
        // 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 the code

M04_02: JpaTransactionManager # doBegin() source

protected void doBegin(Object transaction, TransactionDefinition definition) {
    
    // JPA transaction object, representing EntityManagerHolder. JpaTransactionManager is used as the transaction object.
    JpaTransactionObject txObject = (JpaTransactionObject) transaction;

    if(txObject.hasConnectionHolder() && ! txObject.getConnectionHolder().isSynchronizedWithTransaction()) {throw newIllegalTransactionStateException(....) ; }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()));
        // Specify transaction attributes for the current transaction object
        txObject.setTransactionData(transactionData);

        if(timeoutToUse ! = TransactionDefinition.TIMEOUT_DEFAULT) {// Control the timeout period
            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); }// This is where the resource is bound, and a database data is bound as a mapping object
                    // TODO: This should be the core of the three states, we will see 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 the code