I. Transaction communication

1, PROPAGATION_REQUIRED(default mode) :

This is the most common, that is, if servicea. method calls Serviceb. method, if servicea. method opens a transaction, and then serviceB. method declares a transaction, The serviceB. method does not start a separate transaction, but executes its operations in a servicea. method transaction. An error reported by either ServiceA or ServiceB will cause the transaction to roll back. That’s the default behavior, and that’s what we need in general.

2, PROPAGATION_SUPPORTS:

If Servicea. method opens a transaction, ServiceB will add itself to the ServiceA and run it. If Servicea. method does not open a transaction, ServiceB does not open a transaction

3, PROPAGATION_MANDATORY:

Must be called by a method with transaction enabled, otherwise an error is reported

4, the PROPAGATION_REQUIRES_NEW:

The ServiceB. Method forces itself to start a new transaction, and then the Servicea. method transaction gets stuck, waiting for the ServiceB transaction to finish. If ServiceB reports an error, ServiceB will not be affected. If ServiceB reports an error, ServiceA can optionally roll back or commit.

5, PROPAGATION_NOT_SUPPORTED:

The serviceb. method does not support transactions. A ServiceA transaction executes to a ServiceB, suspends, and the ServiceB executes nontransactionatively, and the ServiceA transaction continues. The benefit is that ServiceB code errors do not cause ServiceA to roll back.

6, PROPAGATION_NEVER:

Servicea. method cannot be called by a transaction, but ServiceB calls an error

7, PROPAGATION_NESTED:

When a nested transaction is enabled, ServiceB starts a subtransaction, and if rolled back, ServiceB rolls back to the save point where the subtransaction was started.

The ServiceA loop 51 calls ServiceB, and the 51st call fails. The first option is to set both transactions to PROPAGATION_REQUIRED, and all ServiceB operations are PROPAGATION_REQUIRED in a large transaction started by ServiceA. Any failure will cause the entire transaction to be rolled back. The second option is to set ServiceB to PROPAGATION_REQUIRES_NEW, so that each call to ServiceB is executed in a separate transaction. In this case, even if an error is reported on the 51st, only the operation on the 51st is rolled back. The previous 50 attempts were successful in a separate transaction. It doesn’t roll back.

“PROPAGATION_REQUIRES_NEW” is commonly used, and the effect is that the nested transaction is an independent transaction, PROPAGATION_REQUIRES_NEW, which commits or rolls back without any impact on the larger transaction, which can retrieve the exception thrown and decide whether to continue to commit or roll back the larger transaction.

Second, AOP dynamic proxy transaction support

At its core, a Controller calls a Service using a dynamic proxy object. AOP uses the @Transactional annotation to weave in Transactional code.

Start a transaction, execute all the logic in the transaction, execute the logic, if error, perform mysql rollback, if successful, execute commit.

Spring transaction source code

Spring – tx packet is transaction source, the core is the org. Springframework. Transaction. The interceptor under this package, the content of the TransactionInterceptor this class is the core entrance, that is to say, If we implement @Transactional, we intercept this class method first.

public Object invoke(final 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); Return invokeWithinTransaction(Invocation. GetMethod (), targetClass, Invocation invocation. new InvocationCallback() { @Override public Object proceedWithInvocation() throws Throwable { return invocation.proceed(); }}); }Copy the code

3.1 invokeWithinTransaction () entry

protected Object invokeWithinTransaction(Method method, Class<? > targetClass, Final InvocationCallback Invocation throws Throwable {// Initialize some object instances final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass); 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. // If @transactional is marked, Create a transaction object 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. // This line is mainly implemented within a transaction logic retVal = invocation. ProceedWithInvocation (); } Catch (Throwable ex) {// Target Invocation exception // If an exception occurs during transaction invocation, Will the transaction closed and rollback completeTransactionAfterThrowing (txInfo, ex); throw ex; } finally {cleanupTransactionInfo(txInfo); } / / execution success finally, commit the transaction commitTransactionAfterReturning (txInfo); return retVal; }... . . . . }Copy the code

3.2 createTransactionIfNecessary () open transactions

protected TransactionInfo createTransactionIfNecessary( PlatformTransactionManager tm, TransactionAttribute txAttr, final String joinpointIdentification) { // If no name specified, apply method identification as 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) { status = tm.getTransaction(txAttr); } else { if (logger.isDebugEnabled()) { logger.debug("Skipping transactional joinpoint [" + joinpointIdentification + "]  because no transaction manager has been configured"); } } } return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status); }Copy the code

The main process is to enable transactions through hibernate-EntityManager project and Hibernate-Core project, both of which are also calling JDBC at the bottom, and through the Druid link pool information that we configured, Once the Connection object is obtained, a transaction is finally started using setAutocommid(false).

3.3 After the transaction is started, the service logic is executed

This is quite simple, not much to look at, just executing our logic by calling the invoke() method of the interceptor and invoking the method of the relevant service.

3.4 Transaction Submission

    protected void commitTransactionAfterReturning(TransactionInfo txInfo) {
        if (txInfo != null && txInfo.hasTransaction()) {
            if (logger.isTraceEnabled()) {
                logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "]");
            }
            txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
        }
    }
Copy the code

This is actually the same as above, and finally also through Hibernate call JDBC commit

3.4 Transaction Rollback

The main principle of a Transaction is to control it through the TransactionInterceptor’s invoke() method, the main entry point, by first creating a Transaction object, By calling the Relevant JAR package of the Hibernate framework, the Connection object is finally obtained via JDBC, and the AutoCommit property is set to false through this object to start a transaction.

Then the business logic we wrote will be executed. If the logic runs normally and no errors are reported, the transaction will be committed via Connection.mit () via Hibernate. If an exception occurs during execution, Finally, the transaction is rolled back by Hibernate calling the JDBC bottom layer and executing Connection.rollback().