It is important for this article to explain in detail the seven propagation behaviors in Spring transactions.
environment
- jdk1.8
- Spring 5.2.3.RELEASE
- mysql5.7
What is transaction propagation behavior?
The propagation behavior of transactions is used to describe: some methods in the system are entrusted to spring to manage transactions. When nested calls occur between these methods, what is the behavior of transactions?
For example, the following two classes, m1 method in Service1 and m2 method in Service2, have @ Transactional annotation on them, indicating that spring controls the transactions of these two methods.
But notice the 2 lines of code in m1, first execute a insert, then call the m2 method in service2, and the m2 method in service2 also executes a insert.
So do you think these two insert s will run in one transaction? In other words, what is the behavior of the transaction at this time? This is what is controlled by the propagation behavior of spring transactions. Different propagation behaviors will behave differently. They may or may not be executed in a transaction. This depends on the configuration of propagation behavior.
@Component
public class Service1 {
@Autowired
private Service2 service2;
@Autowired
private JdbcTemplate jdbcTemplate;
@Transactional
public void m1() {
this.jdbcTemplate.update("INSERT into t1 values ('m1')");
this.service2.m2();
}
}
@Component
public class Service2 {
@Autowired
private JdbcTemplate jdbcTemplate;
@Transactional
public void m2() {
this.jdbcTemplate.update("INSERT into t1 values ('m2')");
}
}
How to configure transaction propagation behavior?
Specify the propagation behavior of the transaction through the propagation attribute in the @ Transactional annotation:
Propagation propagation() default Propagation.REQUIRED;
Propagation is an enumeration with seven values, as follows:
Transaction propagation behavior type | explain |
---|---|
REQUIRED | If there is no transaction in the current transaction manager, create a new transaction. If there is already a transaction, join it. This is the most common choice and is the default propagation behavior. |
SUPPORTS | The current transaction is supported. If there is no transaction in the current transaction manager, it will be executed in a non transactional manner. |
MANDATORY | Using the current transaction, if there is no transaction in the current transaction manager, an exception will be thrown. |
REQUIRES_NEW | Create a new transaction. If there is a transaction in the current transaction manager, suspend the current transaction, and then create a new transaction. |
NOT_SUPPORTED | Perform operations in a non transactional manner. If there is a transaction in the current transaction manager, suspend the current transaction. |
NEVER | Execute in a non transactional manner. If there is a transaction in the current transaction manager, an exception will be thrown. |
NESTED | If there is a transaction in the current transaction manager, it is executed within the nested transaction; If there is no transaction in the current transaction manager, execute the transaction and promotion_ Required similar operations. |
Note: there is a premise for these seven propagation behaviors. Only when their transaction managers are the same can they have the behavior described above.
The following is a case to illustrate the behaviors in 7. Before looking at the case, let's review some knowledge points
1. Spring declarative transaction process
spring declarative transaction implements the function of transaction management by intercepting the target method through the transaction interceptor TransactionInterceptor. The processing process of the transaction manager is roughly as follows:
1,Get transaction manager
2,Start a transaction through the transaction manager
try{
3,Call business method execution db operation
4,Commit transaction
}catch(RuntimeException | Error){
5,Rollback transaction
}
2. When will the transaction be rolled back?
By default, when the target method throws a RuntimeException or Error, the transaction will be rolled back.
3. How does the Connection in the Spring transaction manager and the Connection operating db in the business use the same?
Take DataSourceTransactionManager as the transaction manager, and use JdbcTemplate to explain the operation db.
When creating DataSourceTransactionManager and JdbcTemplate, both datasources need to be specified, and their datasources need to be specified as the same object.
When the transaction manager starts a transaction, it will obtain a db connection through the dataSource.getConnection() method, then throw the datasource - > connection into a map, and then put the map into ThreadLocal.
When the JdbcTemplate executes sql, use the JdbcTemplate.dataSource to find out whether there is an available connection in the ThreadLocal above. If so, use it directly. Otherwise, call the JdbcTemplate.dataSource.getConnection() method to obtain a connection for use.
Therefore, spring can ensure that the Connection in the transaction manager and the Connection of operation db in the JdbcTemplate are the same, so as to ensure that spring can control transactions.
Code verification
Prepare db
DROP DATABASE IF EXISTS javacode2018;
CREATE DATABASE if NOT EXISTS javacode2018;
USE javacode2018;
DROP TABLE IF EXISTS user1;
CREATE TABLE user1(
id int PRIMARY KEY AUTO_INCREMENT,
name varchar(64) NOT NULL DEFAULT '' COMMENT 'full name'
);
DROP TABLE IF EXISTS user2;
CREATE TABLE user2(
id int PRIMARY KEY AUTO_INCREMENT,
name varchar(64) NOT NULL DEFAULT '' COMMENT 'full name'
);
spring configuration class MainConfig6
Prepare the JdbcTemplate and transaction manager.
package com.javacode2018.tx.demo6;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
@EnableTransactionManagement //Enable spring transaction management
@Configuration //Specifies that the current class is a spring configuration class
@ComponentScan //Open bean scan registration
public class MainConfig6 {
//Define a data source
@Bean
public DataSource dataSource() {
org.apache.tomcat.jdbc.pool.DataSource dataSource = new org.apache.tomcat.jdbc.pool.DataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/javacode2018?characterEncoding=UTF-8");
dataSource.setUsername("root");
dataSource.setPassword("root123");
dataSource.setInitialSize(5);
return dataSource;
}
//Define a JdbcTemplate to perform db operations
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
//I define a transaction manager
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
Three service s
In the following cases, spring transactions will be used in these three service s to demonstrate the effect.
User1Service
package com.javacode2018.tx.demo6;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
@Component
public class User1Service {
@Autowired
private JdbcTemplate jdbcTemplate;
}
User2Service
package com.javacode2018.tx.demo6;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
@Component
public class User2Service {
@Autowired
private JdbcTemplate jdbcTemplate;
}
TxService
package com.javacode2018.tx.demo6;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class TxService {
@Autowired
private User1Service user1Service;
@Autowired
private User2Service user2Service;
}
Test case Demo6Test
The before method will be executed once before each method marked by @ Test. This method is mainly used to do some preparatory work: start the spring container and clean up the data in the two tables; The after method will be executed once after the execution of each @ Test marked method. We output the data of two tables in this method; Easy to view the Test case effect.
package com.javacode2018.tx.demo6;
import org.junit.Before;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Demo6Test {
private TxService txService;
private JdbcTemplate jdbcTemplate;
//Before executing each @ Test case, start the spring container and clean up the data in user1 and user2
@Before
public void before() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig6.class);
txService = context.getBean(TxService.class);
jdbcTemplate = context.getBean(JdbcTemplate.class);
jdbcTemplate.update("truncate table user1");
jdbcTemplate.update("truncate table user2");
}
@After
public void after() {
System.out.println("user1 Table data:" + jdbcTemplate.queryForList("SELECT * from user1"));
System.out.println("user2 Table data:" + jdbcTemplate.queryForList("SELECT * from user2"));
}
}
1,REQUIRED
User1Service
Add 1 method, transaction propagation behavior: REQUIRED
@Transactional(propagation = Propagation.REQUIRED)
public void required(String name) {
this.jdbcTemplate.update("insert into user1(name) VALUES (?)", name);
}
User2Service
Add two methods, and the transaction propagation behavior is REQUIRED. Note that the last line inside the second method will throw an exception.
@Transactional(propagation = Propagation.REQUIRED)
public void required(String name) {
this.jdbcTemplate.update("insert into user1(name) VALUES (?)", name);
}
@Transactional(propagation = Propagation.REQUIRED)
public void required_exception(String name) {
this.jdbcTemplate.update("insert into user1(name) VALUES (?)", name);
throw new RuntimeException();
}
Scenario 1 (1-1)
There is no transaction in the peripheral method. The peripheral method calls two REQUIRED level transaction methods internally.
In this case, the other two services are called in the methods of TxService, so the methods in TxService are collectively referred to as peripheral methods, and the methods in the other two services are called internal methods.
Verification method 1
TxService add
public void notransaction_exception_required_required() {
this.user1Service.required("Zhang San");
this.user2Service.required("Li Si");
throw new RuntimeException();
}
Test case, added in Demo6Test
@Test
public void notransaction_exception_required_required() {
txService.notransaction_exception_required_required();
}
Run output
user1 Table data:[{id=1, name=Zhang San}]
user2 Table data:[{id=1, name=Li Si}]
Verification method 2
TxService add
public void notransaction_required_required_exception() {
this.user1Service.required("Zhang San");
this.user2Service.required_exception("Li Si");
}
Test case, added in Demo6Test
@Test
public void notransaction_required_required_exception() {
txService.notransaction_required_required_exception();
}
Run output
user1 Table data:[{id=1, name=Zhang San}]
user2 Table data:[]
Result analysis
Verification method serial number | Database results | Result analysis |
---|---|---|
1 | "Zhang San" and "Li Si" are inserted. | The peripheral method does not start the transaction, and the inserted "Zhang San" and "Li Si" methods run independently in their own transaction. The exception of the peripheral method does not affect the independent transaction of the internal inserted "Zhang San" and "Li Si" methods. |
2 | "Zhang San" is inserted and "Li Si" is not inserted. | The peripheral method has no transaction, and the insertion of "Zhang San" and "Li Si" methods run independently in their own transaction. Therefore, the exception thrown by the insertion of "Li Si" method will only roll back the insertion of "Li Si" method, and the insertion of "Zhang San" method will not be affected. |
conclusion
Through these two methods, we prove that when the peripheral method does not open the transaction, the internal method modified by Propagation.REQUIRED will newly open its own transaction, and the opened transactions are independent of each other and do not interfere with each other.
Scenario 2 (1-2)
The peripheral method starts the transaction (Propagation.REQUIRED), which is used frequently.
Verification method 1
TxService add
@Transactional(propagation = Propagation.REQUIRED)
public void transaction_exception_required_required() {
user1Service.required("Zhang San");
user2Service.required("Li Si");
throw new RuntimeException();
}
Test case, added in Demo6Test
@Test
public void transaction_exception_required_required() {
txService.transaction_exception_required_required();
}
Run output
user1 Table data:[]
user2 Table data:[]
Verification method 2
TxService add
@Transactional(propagation = Propagation.REQUIRED)
public void transaction_required_required_exception() {
user1Service.required("Zhang San");
user2Service.required_exception("Li Si");
}
Test case, added in Demo6Test
@Test
public void transaction_required_required_exception() {
txService.transaction_required_required_exception();
}
Run output
user1 Table data:[]
user2 Table data:[]
Verification method 3
TxService add
@Transactional(propagation = Propagation.REQUIRED)
public void transaction_required_required_exception_try() {
user1Service.required("Zhang San");
try {
user2Service.required_exception("Li Si");
} catch (Exception e) {
System.out.println("Method rollback");
}
}
Test case, added in Demo6Test
@Test
public void transaction_required_required_exception_try() {
txService.transaction_required_required_exception_try();
}
Run output
Method rollback
user1 Table data:[]
user2 Table data:[]
Result analysis
Verification method serial number | Database results | Result analysis |
---|---|---|
1 | "Zhang San" and "Li Si" are not inserted. | The peripheral method starts the transaction, the internal method joins the peripheral method transaction, the peripheral method rolls back, and the internal method also rolls back. |
2 | "Zhang San" and "Li Si" are not inserted. | The peripheral method starts the transaction, the internal method joins the peripheral method transaction, the internal method throws an exception and rolls back, and the peripheral method senses the exception, resulting in the overall transaction rollback. |
3 | "Zhang San" and "Li Si" are not inserted. | The peripheral method starts the transaction, the internal method joins the peripheral method transaction, and the internal method throws an exception rollback. Even if the method is caught and not perceived by the peripheral method, the whole transaction is still rolled back. |
conclusion
The above test results show that when the peripheral method starts the transaction, the internal method modified by Propagation.REQUIRED will be added to the transaction of the peripheral method. All internal methods modified by Propagation.REQUIRED and peripheral methods belong to the same transaction. As long as one method rolls back, the whole transaction will roll back.
2,PROPAGATION_REQUIRES_NEW
User1Service
Add 1 method, transaction propagation behavior: Requirements_ NEW
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void requires_new(String name) {
this.jdbcTemplate.update("insert into user1(name) VALUES (?)", name);
}
User2Service
Add 2 methods, transaction propagation behavior: Requirements_ New, notice that the last line inside the second method will throw an exception.
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void requires_new(String name) {
this.jdbcTemplate.update("insert into user2(name) VALUES (?)", name);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void requires_new_exception(String name) {
this.jdbcTemplate.update("insert into user2(name) VALUES (?)", name);
throw new RuntimeException();
}
Scenario 1 (2-1)
Peripheral methods have no transactions.
Verification method 1
TxService add
public void notransaction_exception_requiresNew_requiresNew(){
user1Service.requires_new("Zhang San");
user2Service.requires_new("Li Si");
throw new RuntimeException();
}
Added in Demo6Test
@Test
public void notransaction_exception_requiresNew_requiresNew() {
txService.notransaction_exception_requiresNew_requiresNew();
}
Run output
user1 Table data:[{id=1, name=Zhang San}]
user2 Table data:[{id=1, name=Li Si}]
Verification method 2
TxService add
public void notransaction_requiresNew_requiresNew_exception(){
user1Service.requires_new("Zhang San");
user2Service.requires_new_exception("Li Si");
}
Test case, added in Demo6Test
@Test
public void notransaction_requiresNew_requiresNew_exception() {
txService.notransaction_requiresNew_requiresNew_exception();
}
Run output
user1 Table data:[{id=1, name=Zhang San}]
user2 Table data:[]
Result analysis
Verification method serial number | Database results | Result analysis |
---|---|---|
1 | Insert "Zhang San" and "Li Si". | The peripheral method has no transaction, and the "Zhang San" and "Li Si" methods inserted run independently in their own transactions. The exception rollback thrown by the peripheral method will not affect the internal method. |
2 | "Zhang San" is inserted, and "Li Si" is not inserted | The peripheral method does not start a transaction. Insert the "Zhang San" method and the "Li Si" method to start their own transactions respectively. Insert the "Li Si" method to throw an exception rollback, and other transactions will not be affected. |
conclusion
Through these two methods, we prove that propagation. Requirements does not open transactions in the peripheral method_ The internal method modified by new will newly open its own transactions, and the opened transactions are independent of each other and do not interfere with each other.
Scenario 2 (2-2)
The peripheral method starts the transaction.
Verification method 1
TxService add
@Transactional(propagation = Propagation.REQUIRED)
public void transaction_exception_required_requiresNew_requiresNew() {
user1Service.required("Zhang San");
user2Service.requires_new("Li Si");
user2Service.requires_new("Wang Wu");
throw new RuntimeException();
}
Test case, added in Demo6Test
@Test
public void transaction_exception_required_requiresNew_requiresNew() {
txService.transaction_exception_required_requiresNew_requiresNew();
}
Run output
user1 Table data:[]
user2 Table data:[{id=1, name=Li Si}, {id=2, name=Wang Wu}]
Verification method 2
TxService add
@Transactional(propagation = Propagation.REQUIRED)
public void transaction_required_requiresNew_requiresNew_exception() {
user1Service.required("Zhang San");
user2Service.requires_new("Li Si");
user2Service.requires_new_exception("Wang Wu");
}
Added in Demo6Test
@Test
public void transaction_required_requiresNew_requiresNew_exception() {
txService.transaction_required_requiresNew_requiresNew_exception();
}
Run output
user1 Table data:[]
user2 Table data:[{id=1, name=Li Si}]
Verification method 3
TxService add
@Transactional(propagation = Propagation.REQUIRED)
public void transaction_required_requiresNew_requiresNew_exception_try(){
user1Service.required("Zhang San");
user2Service.requires_new("Li Si");
try {
user2Service.requires_new_exception("Wang Wu");
} catch (Exception e) {
System.out.println("RollBACK ");
}
}
Added in Demo6Test
@Test
public void transaction_required_requiresNew_requiresNew_exception_try() {
txService.transaction_required_requiresNew_requiresNew_exception_try();
}
Run output
RollBACK
user1 Table data:[{id=1, name=Zhang San}]
user2 Table data:[{id=1, name=Li Si}]
Result analysis
Verification method serial number | Database results | Result analysis |
---|---|---|
1 | "Zhang San" is not inserted, "Li Si" is inserted, "Wang Wu" is inserted. | The peripheral method starts the transaction, inserts the "Zhang San" method and the peripheral method into a transaction, and inserts the "Li Si" method and the "Wang Wu" method respectively. In the independent new transaction, the peripheral method throws an exception and only rolls back the method of the same transaction as the peripheral method, so the "Zhang San" method rolls back. |
2 | "Zhang San" is not inserted, "Li Si" is inserted, "Wang Wu" is not inserted. | The peripheral method starts the transaction, inserts the "Zhang San" method and the peripheral method into a transaction, and inserts the "Li Si" method and the "Wang Wu" method into the independent new transaction respectively. Insert the "king five" method to throw an exception. First, the transaction inserted into the "king five" method is rolled back. If the exception continues to be thrown, it is perceived by the peripheral method, and the peripheral method transaction is also rolled back. Therefore, the "Zhang three" method is also rolled back. |
3 | "Zhang San" is inserted, "Li Si" is inserted, "Wang Wu" is not inserted. | The peripheral method starts the transaction, inserts the "Zhang San" method and the peripheral method into a transaction, and inserts the "Li Si" method and the "Wang Wu" method into the independent new transaction respectively. Inserting the "king five" method throws an exception. First, the transaction inserted into the "king five" method is rolled back. The exception is caught and will not be perceived by the peripheral method. The peripheral method transaction is not rolled back. Therefore, the "Zhang three" method is inserted successfully. |
conclusion
Propagation.requirements when a peripheral method starts a transaction_ The internal method modified by new will still open independent transactions, and is also independent of external method transactions. Internal methods, internal methods and external method transactions are independent of each other and do not interfere with each other.
3,PROPAGATION_NESTED
User1Service
Add a method, transaction propagation behavior: NESTED
@Transactional(propagation = Propagation.NESTED)
public void nested(String name) {
this.jdbcTemplate.update("insert into user1(name) VALUES (?)", name);
}
User2Service
Add two methods, transaction propagation behavior: NESTED. Note that the last line inside the second method will throw an exception.
@Transactional(propagation = Propagation.NESTED)
public void nested(String name) {
this.jdbcTemplate.update("insert into user2(name) VALUES (?)", name);
}
@Transactional(propagation = Propagation.NESTED)
public void nested_exception(String name) {
this.jdbcTemplate.update("insert into user2(name) VALUES (?)", name);
throw new RuntimeException();
}
Scenario 1 (3-1)
Peripheral methods have no transactions.
Verification method 1
TxService add
public void notransaction_exception_nested_nested(){
user1Service.nested("Zhang San");
user2Service.nested("Li Si");
throw new RuntimeException();
}
Added in Demo6Test
@Test
public void notransaction_exception_nested_nested() {
txService.notransaction_exception_nested_nested();
}
Run output
user1 Table data:[{id=1, name=Zhang San}]
user2 Table data:[{id=1, name=Li Si}]
Verification method 2
TxService add
public void notransaction_nested_nested_exception(){
user1Service.nested("Zhang San");
user2Service.nested_exception("Li Si");
}
Test case, added in Demo6Test
@Test
public void notransaction_nested_nested_exception() {
txService.notransaction_nested_nested_exception();
}
Run output
user1 Table data:[{id=1, name=Zhang San}]
user2 Table data:[]
Result analysis
Verification method serial number | Database results | Result analysis |
---|---|---|
1 | "Zhang San" and "Li Si" are inserted. | The peripheral method does not start the transaction, and the inserted "Zhang San" and "Li Si" methods run independently in their own transaction. The exception of the peripheral method does not affect the independent transaction of the internal inserted "Zhang San" and "Li Si" methods. |
2 | "Zhang San" is inserted and "Li Si" is not inserted. | The peripheral method has no transaction, and the insertion of "Zhang San" and "Li Si" methods run independently in their own transaction. Therefore, the exception thrown by the insertion of "Li Si" method will only roll back the insertion of "Li Si" method, and the insertion of "Zhang San" method will not be affected. |
conclusion
Through these two methods, we prove that when the peripheral method does not open the transaction, propagation.needed and Propagation.REQUIRED have the same effect. The modified internal method will newly open its own transaction, and the opened transactions are independent of each other and do not interfere with each other.
Scenario 2 (3-1)
The peripheral method starts the transaction.
Verification method 1
TxService add
@Transactional
public void transaction_exception_nested_nested(){
user1Service.nested("Zhang San");
user2Service.nested("Li Si");
throw new RuntimeException();
}
Test case, added in Demo6Test
@Test
public void transaction_exception_nested_nested() {
txService.transaction_exception_nested_nested();
}
Run output
user1 Table data:[]
user2 Table data:[]
Verification method 2
TxService add
@Transactional
public void transaction_nested_nested_exception(){
user1Service.nested("Zhang San");
user2Service.nested_exception("Li Si");
}
Added in Demo6Test
@Test
public void transaction_nested_nested_exception() {
txService.transaction_nested_nested_exception();
}
Run output
user1 Table data:[]
user2 Table data:[]
Verification method 3
TxService add
@Transactional
public void transaction_nested_nested_exception_try(){
user1Service.nested("Zhang San");
try {
user2Service.nested_exception("Li Si");
} catch (Exception e) {
System.out.println("Method rollback");
}
}
Added in Demo6Test
@Test
public void transaction_nested_nested_exception_try() {
txService.transaction_nested_nested_exception_try();
}
Run output
Method rollback
user1 Table data:[{id=1, name=Zhang San}]
user2 Table data:[]
Result analysis
Verification method serial number | Database results | Result analysis |
---|---|---|
1 | "Zhang San" and "Li Si" are not inserted. | The external method starts the transaction, and the internal transaction is a sub transaction of the external transaction. The external method rolls back, and the internal method also rolls back. |
2 | "Zhang San" and "Li Si" are not inserted. | The peripheral method starts the transaction. The internal transaction is a sub transaction of the peripheral transaction. The internal method throws an exception and rolls back, and the peripheral method perceives the exception, resulting in the rollback of the overall transaction. |
3 | "Zhang San" is inserted and "Li Si" is not inserted. | The external method starts the transaction, and the internal transaction is a sub transaction of the external transaction. Insert the "Li Si" internal method to throw an exception, and you can roll back the sub transaction separately. |
conclusion
The above test results show that when the peripheral method starts the transaction, the internal method modified by Propagation.NESTED belongs to the sub transaction of the external transaction. The peripheral main transaction is rolled back, and the sub transaction must be rolled back, while the internal sub transaction can be rolled back separately without affecting the peripheral main transaction and other sub transactions.
Principle of internal affairs
Take mysql as an example. There is a Function of savepoint , NESTED internal transactions are implemented through this.
REQUIRED,REQUIRES_NEW,NESTED comparison
From the comparison between "Scene 2 (1-2)" and "Scene 2 (3-2)", we can see:
The internal methods modified by REQUIRED and needed belong to peripheral method transactions. If the peripheral method throws an exception, the transactions of both methods will be rolled back. However, the REQUIRED transaction is added to the peripheral method transaction, so it belongs to the same transaction as the peripheral transaction. Once the REQUIRED transaction throws an exception and is rolled back, the peripheral method transaction will also be rolled back. The NESTED method is a sub transaction of the peripheral method and has a separate savepoint. Therefore, the exceptions thrown by the NESTED method are rolled back and will not affect the transactions of the peripheral method.
From the comparison between "Scene 2 (2-2)" and "Scene 2 (3-2)", we can see: