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
- When the Bean is created, the
UserService
Generate 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#setAutoCommit
This is in theMysql connector - Java - 8.0.25. Jar
Under 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?