This article introduces Spring’s seven transaction propagation behaviors and demonstrates them in code.
I. What is transaction propagation behavior?
Propagation Behavior Refers to how a transaction method should run when it is called by another transaction method.
For example, when methodA calls methodB, whether methodB continues to run a transaction or starts a new transaction for methodB depends on the transaction propagation behavior of methodB.
Second, seven kinds of communication behavior of transaction
Spring specifies seven types of transaction propagation behavior in the TransactionDefinition interface. Transaction propagation behavior is a transaction-enhanced feature unique to the Spring framework. This is a powerful toolkit that Spring provides for us, and using transactional propagation behavior can provide a lot of convenience for our development efforts.
The seven transaction propagation behaviors are as follows:
1.PROPAGATION_REQUIRED
Creating a new transaction if there is none currently, and joining the transaction if there is, is the most common choice and Spring’s default transaction propagation behavior.
2.PROPAGATION_SUPPORTS
Supports current transactions. If a transaction currently exists, join the transaction. If no transaction currently exists, execute it as non-transaction.
3.PROPAGATION_MANDATORY
Supports the current transaction, joins the transaction if it exists, and throws an exception if it does not exist.
4.PROPAGATION_REQUIRES_NEW
Create a new transaction, whether or not a transaction currently exists.
5.PROPAGATION_NOT_SUPPORTED
Performs the operation nontransactionally, suspending the current transaction if one exists.
6.PROPAGATION_NEVER
Executes nontransactionally, throwing an exception if a transaction currently exists.
7.PROPAGATION_NESTED
If a transaction currently exists, it is executed within a nested transaction. If there are no transactions currently, the REQUIRED attribute is executed.
In fact, I did not understand the 7, but don’t worry, let’s go straight to the effect.
Three, 7 kinds of communication behavior actual combat
To start the demo, create two tables, the user table and the user role table, with no data in the first two tables.
Note that the user and user_ROLE tables are cleared each time the code is executed to make the data more intuitive.
The user list:
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`password` varchar(255) DEFAULT NULL,
`sex` int(11) DEFAULT NULL,
`des` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Copy the code
User_role table:
CREATE TABLE `user_role` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) DEFAULT NULL,
`role_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Copy the code
1. PROPAGATION_REQUIRED for testing
Creating a new transaction if there is none currently, and joining the transaction if there is, is the most common choice and Spring’s default transaction propagation behavior.
Scene 1:
The scenario peripheral method does not open a transaction.
1. Verification method
The two implementation classes UserServiceImpl and UserRoleServiceImpl specify the transaction propagation behavior Propagation =Propagation.REQUIRED, and then call both methods in the test method and throw an exception when the call is complete.
2. Main code
Outer calling method code:
/** * Test PROPAGATION_REQUIRED ** @author: java_suisui */ @test voidtest_PROPAGATION_REQUIRED() {// add User table User User = new User(); user.setName("Java Chatter");
user.setPassword("123456"); userService.add(user); UserRole UserRole = new UserRole(); userRole.setUserId(user.getId()); userRole.setRoleId(200); userRoleService.add(userRole); // throw new RuntimeException(); }Copy the code
UserServiceImpl code:
@transactional (Propagation = Propagation.REQUIRED) @override public int add(User User) {return userMapper.add(user);
}
Copy the code
UserRoleServiceImpl code:
@transactional (Propagation = Propagation.REQUIRED) @override public int add(UserRole UserRole) {return userRoleMapper.add(userRole);
}
Copy the code
3. Database screenshot after code execution
The data of the two tables is added successfully. The screenshot is as follows:
4. Result analysis
The external method does not open the transaction, and the methods of inserting the user table and user role table run independently in their own transaction. The external method exception does not affect the internal insert, so the two records are added successfully.
Scene 2:
This scenario peripheral method opens the transaction.
1. Main code
The test method code is as follows:
/** * Test PROPAGATION_REQUIRED ** @author: java_suisui */ @transactional @test voidtest_PROPAGATION_REQUIRED() {// add User table User User = new User(); user.setName("Java Chatter");
user.setPassword("123456"); userService.add(user); UserRole UserRole = new UserRole(); userRole.setUserId(user.getId()); userRole.setRoleId(200); userRoleService.add(userRole); // throw new RuntimeException(); }Copy the code
2. Database screenshot after code execution
The data in both tables is empty. The screenshot is as follows:
3. Result analysis
The outer method starts the transaction, the inner method joins the outer method transaction, the outer method rolls back, and the inner method rolls back, so both records fail to be inserted.
Conclusion: Propagation.REQUIRED and peripheral methods belong to the same transaction. If one method is rolled back, the whole transaction will be rolled back.
2. PROPAGATION_SUPPORTS test
Supports current transactions. If a transaction currently exists, join the transaction. If no transaction currently exists, execute it as non-transaction.
Scene 1:
The scenario peripheral method does not open a transaction.
1. Verification method
The two implementation classes UserServiceImpl and UserRoleServiceImpl specify the transaction propagation behavior Propagation =Propagation.SUPPORTS, and then call both methods in the test method and throw an exception when the call ends.
2. Main code
Outer calling method code:
/** * Test ** @author: java_suisui */ @test voidtest_PROPAGATION_SUPPORTS() {// add User table User User = new User(); user.setName("Java Chatter");
user.setPassword("123456"); userService.add(user); UserRole UserRole = new UserRole(); userRole.setUserId(user.getId()); userRole.setRoleId(200); userRoleService.add(userRole); // throw new RuntimeException(); }Copy the code
UserServiceImpl code:
@transactional (Support) @override public int add(User User) {return userMapper.add(user);
}
Copy the code
UserRoleServiceImpl code:
@transactional (Propagation = Propagation.SUPPORTS) @override public int add(UserRole UserRole) {Transactional(propagation = propagation.SUPPORTS) @override public int add(UserRole UserRole) {return userRoleMapper.add(userRole);
}
Copy the code
3. Database screenshot after code execution
The data of the two tables is added successfully. The screenshot is as follows:
4. Result analysis
The external method does not open the transaction, and the methods of inserting the user table and user role table run independently in a non-transactional manner. The external method exception does not affect the internal insert, so the two records are successfully added.
Scene 2:
This scenario peripheral method opens the transaction.
1. Main code
The test_PROPAGATION_SUPPORTS method simply adds the annotation @transactional.
2. Database screenshot after code execution
The data in both tables is empty. The screenshot is as follows:
3. Result analysis
The outer method starts the transaction, the inner method joins the outer method transaction, the outer method rolls back, and the inner method rolls back, so both records fail to be inserted.
Conclusion: Propagation.SUPPORTS will be added to the Propagation.SUPPORTS inner method will be added to the Propagation.
3. PROPAGATION_MANDATORY test
Supports the current transaction, joins the transaction if it exists, and throws an exception if it does not exist.
This Transactional behavior only tests scenarios in which the Transactional layer does not start a transaction when both records fail after the outer layer adds the @Transactional annotation.
Scene 1:
The scenario peripheral method does not open a transaction.
1. Verification method
Two implementation classes, UserServiceImpl and UserRoleServiceImpl, specify propagation = Propagation.Mandatory.
2. Main code
Outer calling method code:
/** * Tests PROPAGATION_MANDATORY ** @author: java_suisui */ @test voidtest_PROPAGATION_MANDATORY() {// add User table User User = new User(); user.setName("Java Chatter");
user.setPassword("123456"); userService.add(user); UserRole UserRole = new UserRole(); userRole.setUserId(user.getId()); userRole.setRoleId(200); userRoleService.add(userRole); // throw new RuntimeException(); }Copy the code
UserServiceImpl code:
@transactional (Propagation = Propagation.MANDATORY) @override public int add(User User) {return userMapper.add(user);
}
Copy the code
UserRoleServiceImpl code:
*/ @transactional (Propagation = Propagation.MANDATORY) @override public int add(UserRole UserRole) {return userRoleMapper.add(userRole);
}
Copy the code
3. Database screenshot after code execution
The data in both tables is empty. The screenshot is as follows:
4. Result analysis
As shown in the run log, an error was reported when userservice.add () was called, so no new data was added to both tables, verifying that “if there is no transaction, throw exception”.
at com.example.springboot.mybatisannotation.service.impl.UserServiceImpl$$EnhancerBySpringCGLIB$$50090f18.add(<generated>) at com.example.springboot.mybatisannotation.SpringBootMybatisAnnotationApplicationTests.test_PROPAGATION_MANDATORY(SpringBo otMybatisAnnotationApplicationTests.java:78) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498)Copy the code
4. The PROPAGATION_REQUIRES_NEW test
Create a new transaction, whether or not a transaction currently exists.
The transaction is created each time in this case, so let’s just validate one case.
Scene 1:
This scenario peripheral method opens the transaction.
1. Verification method
The two implementation classes UserServiceImpl and UserRoleServiceImpl specify propagation = Propagation.REQUIRES_NEW.
2. Main code
Outer calling method code:
REQUIRES_NEW ** @author: Java_suisui */ @test@transactional voidtest_REQUIRES_NEW() {// add User table User User = new User(); user.setName("Java Chatter");
user.setPassword("123456"); userService.add(user); UserRole UserRole = new UserRole(); userRole.setUserId(user.getId()); userRole.setRoleId(200); userRoleService.add(userRole); // throw new RuntimeException(); }Copy the code
UserServiceImpl code:
@transactional (propagation = propagation.REQUIRES_NEW) @override public int add(User User) {return userMapper.add(user);
}
Copy the code
UserRoleServiceImpl code:
@transactional (Propagation = Propagation.REQUIRES_NEW) @override public int add(UserRole UserRole) {return userRoleMapper.add(userRole);
}
Copy the code
3. Database screenshot after code execution
The data of the two tables is added successfully. The screenshot is as follows:
4. Result analysis
The new transaction is created regardless of whether the transaction currently exists, so the two data additions are successful.
5. PROPAGATION_NOT_SUPPORTED test
Performs the operation nontransactionally, suspending the current transaction if one exists.
Scene 1:
This scenario peripheral method does not open transactions.
1. Verification method
The two implementation classes UserServiceImpl and UserRoleServiceImpl specify propagation behavior propagation = Propagation.NOT_SUPPORTED.
2. Main code
Outer calling method code:
/** * Test PROPAGATION_NOT_SUPPORTED ** @author: java_suisui */ @test voidtest_PROPAGATION_NOT_SUPPORTED() {// add User table User User = new User(); user.setName("Java Chatter");
user.setPassword("123456"); userService.add(user); UserRole UserRole = new UserRole(); userRole.setUserId(user.getId()); userRole.setRoleId(200); userRoleService.add(userRole); // throw new RuntimeException(); }Copy the code
UserServiceImpl code:
@transactional (propagation = propagation.NOT_SUPPORTED) @override public int add(User User) {return userMapper.add(user);
}
Copy the code
UserRoleServiceImpl code:
@transactional (Propagation = propagation.NOT_SUPPORTED) @override public int add(UserRole UserRole) {return userRoleMapper.add(userRole);
}
Copy the code
3. Database screenshot after code execution
The data of the two tables is added successfully. The screenshot is as follows:
4. Result analysis
The execution is non-transactional, so both data additions are successful.
Scene 2:
This scenario peripheral method opens the transaction.
1. Main code
The test_PROPAGATION_NOT_SUPPORTED method simply adds the annotation @Transactional.
2. Database screenshot after code execution
The data of the two tables is added successfully. The screenshot is as follows:
3. Result analysis
If a transaction exists, the current transaction is suspended, which is equivalent to non-transaction execution, so the two data added successfully.
6. PROPAGATION_NEVER test
Executes nontransactionally, throwing an exception if a transaction currently exists.
Has had a similar situation above, the outer no transaction will run in the form of the transaction, two tables of new success; If there is a transaction, an exception is thrown, and no new data is added to either table.
7. PROPAGATION_NESTED test
If a transaction currently exists, it is executed within a nested transaction. If there are no transactions currently, the REQUIRED attribute is executed.
In a similar situation, no transactions in the outer layer will run with the REQUIRED attribute, and both tables will be added successfully. With a transaction but with a transaction, the method ends up throwing an exception that causes a rollback, and no new data is added to either table.
To this Spring 7 transaction propagation behavior has been all introduced to complete, have a question welcome message communication oh!
Full source address: github.com/suisui2019/…
Recommended reading
1.SpringBoot series – Integrate Mybatis
2.SpringBoot series – Mybatis (XML configuration)
3. Print logs in Java. These four points are important!
4.SpringBoot integrates JWT to implement permission authentication
5. JWT Certification in a minute!
Limited time to receive free Java related materials, covering Java, Redis, MongoDB, MySQL, Zookeeper, Spring Cloud, Dubbo/Kafka, Hadoop, Hbase, Flink and other high concurrency distributed, big data, machine learning and other technologies. Follow the public account below to get free: