preface

Make sure you use the @Transactional annotation, but what’s the secret behind it?

How does Spring start transactions? How do you commit and close a transaction?

Drawing speculation

Before you start debugging the source code, you should already know how MySQL starts transactions.

So you can guess:

How does Spring’s @Transactional annotation perform transaction logic?

Spring transaction execution process

Open the transaction

Spring Boot + MySQL + Druid

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.2.6</version>
</dependency>
Copy the code

  1. When the Bean is created, theUserServiceGenerate proxy objects based on AOP;

AbstractAutowireCapableBeanFactory#initializeBean … WrapIfNecessary AbstractAutoProxyCreator#createProxy CglibAopProxy#getProxy generates a proxy object

  • Begin to execute userService. UpdateUserInfo (); Here userService is the proxy object; Will be CglibAopProxy. DynamicAdvisedInterceptor# intercept method;

  • TransactionInterceptor#invoke is intercepted by the transaction interceptor

  • TransactionAspectSupport#invokeWithinTransaction transaction processing

  • AbstractPlatformTransactionManager# getTransaction here call AbstractPlatformTransactionManager# startTransaction method, to open the transaction.

Does the word doBegin suddenly sound familiar?

Follow up DataSourceTransactionManager# doBegin method, pay attention to see, at this time is in the spring – JDBC – 5.3.8. Jar package below.

Because the druID Connection pool is used, this Connection is the Connection pool for DurID.

  • DruidPooledConnection#setAutoCommit(false)Turn off automatic submission;

Here is the logic of the druid, execution to com. The next alibaba. Druid. Filter. FilterChainImpl# connection_setAutoCommit.

  • ConnectionImpl#setAutoCommitThis is in theMysql connector - Java - 8.0.25. JarUnder the package.

SET autocommit=0

SET autocommit=0
Copy the code

Transaction started!

To summarize the process:

Execute SQL

After a transaction is started, the internal logic of the method is executed through callbacks.

  • Invoke mapPerProxyinvoke; invoke Mybatis;

  • DruidPooledPreparedStatement# execute;

  • ClientPreparedStatement# execute;

The execution process is relatively simple:

Commit the transaction

Last line in TransactionAspectSupport# invokeWithinTransaction, commitTransactionAfterReturning (txInfo); Commit the transaction.

  • AbstractPlatformTransactionManager# commit abstract transaction manager, and to commit the transaction

  • DataSourceTransactionManager# doCommit data source data manager, commit the transaction

This must be the method that calls the connection pool, so it will be executed in DruidPooledConnection

  • DruidPooledConnection commit

  • ConnectionImpl#commit (mysql-connector-java-8.0.25.jar

Call COMMIT to commit the transaction.

commit
Copy the code

Abnormal rollback

The exception here is TransactionAspectSupport#invokeWithinTransaction is caught.

AbstractPlatformTransactionManager# rollback rollback here

Perform DataSourceTransactionManager# doRollback

Connectionimpll #rollback() to connectionImpll #rollbackNoChecks

To execute the ROLLBACK statement

rollback
Copy the code

Restore the autocommit mode

cleanupTransactionInfo(txInfo);

In this method, autoCOMMIT is restored.

Java natively starts transactions

If that’s a little convoluted, let’s look at the simple version without Spring.

/ * * *@author liuzhihang
 * @date2021/6/18 16:51 * /
public class MainTest {


    public static void main(String[] args) throws Exception {

        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/demo");
        dataSource.setUsername("root");
        dataSource.setPassword("root");

        Connection connection = dataSource.getConnection();

        try {
            // Turn off auto commit
            connection.setAutoCommit(false);

            connection.prepareStatement("update user_info set user_name = 'liuzhihang' where user_id = '1001';").executeUpdate();
            connection.prepareStatement("update user_address set address = 'anhui' where user_id = '1001';").executeUpdate();
            // Commit the transaction
            connection.commit();
        } catch (Exception e) {
            / / rollback
            connection.rollback();
        } finally {
            // Enable automatic submission
            connection.setAutoCommit(true); }}}Copy the code

After looking at Java’s native way of committing transactions, it feels straightforward.

Spring @Transactional simply creates an AOP proxy that calls a native open/close transaction. Mybatis also implements the proxy to commit the SQL.

conclusion

Finally, the diagrams are combined to summarize the process.

At this point, the analysis of the transaction execution process is complete.

But one question remains, right?

Why use Set AutoCOMMIT = 0 to start a transaction instead of begin or Start transaction?

Related to recommend

  • How do Spring dynamic proxies address circular dependencies? Why use level 3 caching?
  • How does Spring address loop dependencies?
  • How did you resolve the Spring self-invocation transaction failure?