preface

You may have used the @Transactional annotation before, but what is the secret behind it?

How does Spring enable transactions? How to commit a transaction and close a transaction?

Drawing speculation

Before Debug starts reading the source code, you should already know how MySQL starts transactions.

Therefore, we can make a guess:

Read the source code below. How does Spring’s @Transactional annotation execute transaction logic?

Spring transaction execution flow

Open the transaction

Spring Boot + MySQL + Druid is used here

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

  1. When you create the Bean, you will be able toUserServiceGenerate proxy objects based on AOP;

AbstractAutowireCapableBeanFactory#initializeBean


.


wrapIfNecessary


AbstractAutoProxyCreator#createProxy


CglibaOpProxy #getProxy generates the proxy object

  • Start to performuserService.updateUserInfo();Here the UserService is the proxy object; Will beCglibAopProxy.DynamicAdvisedInterceptor#interceptMethod interception;
  • TransactionInterceptor#invokeIntercepted by transaction interceptor
  • TransactionAspectSupport#invokeWithinTransactionTransaction processing
  • AbstractPlatformTransactionManager#getTransactionIt’s going to be called hereAbstractPlatformTransactionManager#startTransactionMethod to open the transaction.

The word doBegin suddenly sounds 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 of the Druid Connection pool used, this Connection is a Durid Connection pool.

  • DruidPooledConnection#setAutoCommit(false)Turn off auto-submit;

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

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

SET AUTOCOMMIT =0

SET autocommit=0

Begin the transaction!

To summarize the process:

Execute SQL

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

  • Because MyBatis is used here, it will still be proxied,MapperProxy#invoke;
  • DruidPooledPreparedStatement#execute;
  • ClientPreparedStatement#execute;

The execution process is relatively simple:

Commit the transaction

Last line in TransactionAspectSupport# invokeWithinTransaction, commitTransactionAfterReturning (txInfo); That is to commit the transaction.

  • AbstractPlatformTransactionManager#commitAbstract transaction manager to commit transactions
  • DataSourceTransactionManager#doCommitData source data manager, commit transactions

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

  • DruidPooledConnection commit
  • So it’s still going to be implementedMysql connector - Java - 8.0.25. JarUnder the bagConnectionImpl#commit

Call Commit to commit the transaction.

commit

Abnormal rollback

Exceptions where TransactionAspectSupport# InvokeWithInTransaction will be caught.

AbstractPlatformTransactionManager# rollback rollback here

Perform DataSourceTransactionManager# doRollback

Finally execute to mysql-connector-java-8.0.0.jar connectionImpl# rollback() to connectionImpl# rollbackNochecks

This executes the ROLLBACK statement

rollback

Restore the autocommit mode

cleanupTransactionInfo(txInfo);

In this method, the AUTOCOMMIT that you previously set is restored.

Java opens transactions natively

If this is a bit confusing, we can look at the simple version without Spring.

/** * @author liuzhihang * @date 2021/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 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(); // Submit the transaction connection.mit (); } catch (Exception E) {// rollback connection.rollback(); } finally {// turn on connection.setAutoCommit(true); }}}

After looking at the way Java commits transactions natively, does it feel simple and straightforward?

Spring@Transactional simply creates an AOP proxy that calls native open and close transactions. It is also used by MyBatis to perform the SQL execution and deliver the SQL.

conclusion

Finally, the diagrams are combined to summarize the process.

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

But there’s a question, right?

Why start a transaction using SET AUTOCOMMIT = 0 instead of BEGIN or START TRANSACTION?

Related to recommend

  • How does Spring resolve cyclic dependencies when proxying dynamically? Why use three-level caching?
  • How does Spring resolve circular dependencies?
  • How do you deal with Spring self-invoking transaction invalidation?