Review of the spring transaction propagation mechanism
Spring transaction One is misinformation very broadly: one transaction method should not invoke another transaction method, otherwise two transactions will be generated. As a result, developers shackled in the design of transaction methods, fearing that they would step on a mine accidentally.
In fact, this is a misunderstanding caused by the spring transaction propagation mechanism, spring's support for transaction control is described in the Transactiondefinition class, which has several important interface methods:
- int Getpropagationbehavior (): The propagation behavior of a transaction
- int Getisolationlevel (): The isolation level of the transaction
- int GetTimeout (): The expiration time of the transaction
- Boolean isreadonly (): Transactional read-write characteristics
Obviously, in addition to the propagation behavior of the transaction, other features of the transaction spring is done with the functionality of the underlying resources, and spring is nothing more than a proxy role. But the spread of the transaction is spring's ability to provide the developer with its own framework, the most precious gift that spring offers to developers, and misinformation's claim to tarnish the most beautiful aura of the spring transaction framework.
The so-called transaction propagation behavior is how transactions are propagated between these methods when multiple transactional methods are called each other. Spring supports the following 7 types of transactional propagation behavior.
- Propagation_required: If there is no current transaction, create a new transaction, and if one already exists, add it to the transaction . This is the most common choice.
- Propagation_supports: Supports the current transaction and is executed in a non-transactional manner if no transaction is currently in use.
- Propagation_mandatory: Uses the current transaction and throws an exception if there is no current transaction.
- Propagation_requires_new: Creates a new transaction and suspends the current transaction if a transaction is currently present.
- Propagation_not_supported: Executes the operation in a non-transactional manner, suspending the current transaction if a transaction is currently present.
- Propagation_never: Executes in a non-transactional manner and throws an exception if a transaction is currently present.
- Propagation_nested: Executes within a nested transaction if a transaction is currently present. If there is currently no transaction, perform a similar operation as propagation_required.
Spring's default transactional propagation behavior is propagation_required, which is suitable for most situations where multiple servivex#methodx () are working in a transactional environment (that is, both are enhanced by spring transactions). And the following call chain exists in the program: Service1#method1 ()->service2#method2 ()->service3#method3 (), Then the 3 methods of these 3 service classes work in the same transaction through spring's transaction propagation mechanism.
Mutually nested service methods
Let's take a look at the example, the Userservice#logon () method internally calls the Userservice#updatelastlogon time () and the Scoreservice#addscore () method, These two classes are inherited from Baseservice. The class structure between them is as follows:
The Userservice#logon () method internally invokes the Scoreservice#addscore () method, each of which has transaction enhancements through Spring AOP, which works in the same transaction. To see the specific code:
PackageCom.baobaotao.nestcall; @Service ("UserService") Public classUserServiceextendsBaseservice {@AutowiredPrivateJdbcTemplate JdbcTemplate; @AutowiredPrivateScoreservice Scoreservice; //① This method nested calls to other methods of this class and methods of other service classes Public voidlogon (String userName) {System.out.println ("Before Userservice.updatelastlogontime ..."); Updatelastlogontime (userName);//①-1 Other methods of this service classSystem.out.println ("After userservice.updatelastlogontime ..."); System.out.println ("Before Scoreservice.addscore ..."); Scoreservice.addscore (UserName,20);//other ways to ①-2 other service classesSystem.out.println ("After Scoreservice.addscore ..."); } Public voidupdatelastlogontime (String userName) {String sql= "UPDATE t_user u SET u.last_logon_time =?" WHERE user_name =? "; Jdbctemplate.update (SQL, System.currenttimemillis (), userName); }
The Scoreservice bean is injected into the userservice, and the code for Scoreservice is as follows:
package Com.baobaotao.nestcall; ... @Service ( "Scoreuserservice" public class scoreservice extends baseservice{@Autowired private JdbcTemplate JdbcTemplate; public void addscore (String UserName, int toadd) {String sql = "UPDATE T_user u SET u.score = U.score +? WHERE user_name =? " ; Jdbctemplate.update (SQL, Toadd, userName); }}
Add Spring AOP Transaction enhancements to all public methods in Scoreservice and UserService through spring configuration, letting UserService logon () and Updatelastlogontime () and Scoreservice Addscore () methods work in a transactional environment. The following are the key configuration codes:
<?xml version= "1.0" encoding= "UTF-8"? ><beans xmlns= "Http://www.springframework.org/schema/beans"Xmlns:xsi= "Http://www.w3.org/2001/XMLSchema-instance"Xmlns:context= "Http://www.springframework.org/schema/context"xmlns:p= "http://www.springframework.org/schema/p" xmlns:aop= "HTTP://WWW.SPRINGFRAMEWORK.ORG/SCHEMA/AOP"Xmlns:tx= "Http://www.springframework.org/schema/tx"xsi:schemalocation= "Http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http//Www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-3.0.xsd
HTTP://WWW.SPRINGFRAMEWORK.ORG/SCHEMA/AOP http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
Http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"><context:component-scan base- Package= "Com.baobaotao.nestcall"/>... ..<bean id= "Jdbcmanager"class= "Org.springframework.jdbc.datasource.DataSourceTransactionManager"P:datasource-ref= "DataSource"/> <!--① All public methods with the following configuration for all of the subclasses of the inherited Baseservice class are added transaction enhancement--<aop:config proxy-target-class= "true" > <aop:pointcut id= "Servicejdbcmethod"expression= "Within (com.baobaotao.nestcall.baseservice+)"/> <aop:advisor pointcut-ref= "Servicejdbcmethod" advice-ref= " Jdbcadvice "order=" 0 "/> </aop:config> <tx:advice id=" Jdbcadvice "transaction-manager=" Jdbcmanager "> <tx:attributes> <tx:method name= "*"/> </tx:attributes> </tx:advice>< /beans>
Set the log level to debug, start the Spring container and execute the Userservice#logon () method, and carefully observe the output log as follows:
before Userservice.logon method ...//① creates a transactionCreatingNewtransaction with Name [Com.baobaotao.nestcall.UserService.logon]: Propagation_required,isolation_default Acquired Connection [Jdbc:mysql://localhost:3306/sampledb, [email protected], mysql-ab JDBC Driver] for JDBC transactionSwitching JDBC Connection [jdbc:mysql://localhost:3306/sampledb, [email protected], Mysql-ab JDBC Driver] to manual commitbefore Userservice.updatelastlogontime ...<!--②updatelastlogontime () and logon () in the same bean do not take an action that joins an already existing transaction context, but instead "naturally" work in the same transaction contexts-executing prepared SQL update executing prepared SQL statement [Update t_user u SET u.last_logon_time= ? WHERE user_name =?] SQL Update affected1rows after userservice.updatelastlogontime ... before Scoreservice.addscore ...//The ③scoreservice#addscore method is added to the transaction context started at ①participating in existing transaction executing prepared SQL update executing prepared SQL statement [Update t_user U SET U.score= U.score +? WHERE user_name =?] SQL Update affected1rows after Scoreservice.addscore ... Initiating transaction commit committing JDBC transaction on Connection [Jdbc:mysql://localhost:3306/sampledb, [email protected], Mysql-ab JDBC Driver]... after Userservice.logon method ...
From the output log above, it is clear that spring has started a new transaction for the Userservice#logon () method, while Userserive#updatelastlogontime () and Userservice#logon () are in the same class , the occurrence of transactional propagation behavior is not observed, and its code blocks appear to be "merged directly" into Userservice#logon ().
However, when executing to the Scoreservice#addscore () method, we observe the behavior of a transaction propagating: " participating in existing transaction ",
This means that scoreservice#addscore () is added to the transaction context of Userservice#logon (), and both share the same transaction .
So the end result is that UserService's logon (), Updatelastlogontime (), and Scoreservice Addscore all work in the same transaction.
Note: The above content is from "Spring 3.x enterprise application development actual combat"
http://blog.csdn.net/hy6688_/article/details/44763869
Analysis of the propagation characteristics of spring transaction--The confusion of nested invocation of transaction method