A deep understanding of Spring's transaction Propagation Behavior and a deep understanding of spring transactions

Source: Internet
Author: User

A deep understanding of Spring's transaction Propagation Behavior and a deep understanding of spring transactions

Preface

Spring defines seven types of transaction propagation behaviors in the TransactionDefinition interface. Transaction Propagation Behavior is a unique enhancement feature of the Spring framework. It does not belong to the actual provider database behavior of transactions. This is a powerful toolbox provided by Spring. Using transaction propagation lines can facilitate our development work. However, people have many misunderstandings about it. You must have heard the rumor that "service method transactions are best not nested. To use the tool correctly, you must first understand the tool. This article gives a detailed introduction to the seven transaction propagation behaviors and shows the main sample code.

Basic Concepts

1. What is transaction Propagation?

The transaction Propagation Behavior is used to describe how the method modified by a transaction Propagation Behavior is nested into another method.

Use pseudocode to describe:

 public void methodA(){ methodB(); //doSomething }  @Transaction(Propagation=XXX) public void methodB(){ //doSomething }

CodemethodA()Method nested callmethodB()Method,methodB()The transaction Propagation Behavior@Transaction(Propagation=XXX)Set. Note thatmethodA()The transaction is not started. The modification method of a transaction Propagation Behavior is not required to be called in the peripheral method of the transaction.

2. Seven transaction propagation behaviors in Spring

Transaction Propagation Behavior Type Description
PROPAGATION_REQUIRED If no transaction exists, create a transaction. If a transaction already exists, add it to the transaction. This is the most common choice.
PROPAGATION_SUPPORTS Supports the current transaction. If no transaction exists, it is executed in non-transaction mode.
PROPAGATION_MANDATORY Use the current transaction. If no transaction exists, an exception is thrown.
PROPAGATION_REQUIRES_NEW Create a new transaction. If a transaction exists, the current transaction is suspended.
PROPAGATION_NOT_SUPPORTED The operation is performed in non-transaction mode. If a transaction exists, the current transaction is suspended.
PROPAGATION_NEVER It is executed in non-transaction mode. If a transaction exists, an exception is thrown.
PROPAGATION_NESTED If a transaction exists, it is executed in the nested transaction. If no transaction exists, perform a similar operation as PROPAGATION_REQUIRED.

The definition is very simple and easy to understand. Next we will go to the Code testing section to verify that our understanding is correct.

Code Verification

The code in this article is presented at two layers in the traditional three-layer structure, namely the Service and Dao layers. Spring is responsible for dependency injection and annotation-based transaction management, and the DAO layer is implemented by Mybatis, you can also use any method you like, such as Hibernate, JPA, and JDBCTemplate. The database uses a MySQL database. You can also use any database that supports transactions without affecting the verification results.

First, create two tables in the database:

User1

CREATE TABLE `user1` ( `id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, `name` VARCHAR(45) NOT NULL DEFAULT '', PRIMARY KEY(`id`))ENGINE = InnoDB;

User2

CREATE TABLE `user2` ( `id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, `name` VARCHAR(45) NOT NULL DEFAULT '', PRIMARY KEY(`id`))ENGINE = InnoDB;

Then write the corresponding Bean and DAO Layer Code:

User1

Public class User1 {private Integer id; private String name; // The get and set methods are omitted ...}

User2

Public class User2 {private Integer id; private String name; // The get and set methods are omitted ...}

User1Mapper

Public interface User1Mapper {int insert (User1 record); User1 selectByPrimaryKey (Integer id); // other methods are omitted ...}

User2Mapper

Public interface User2Mapper {int insert (User2 record); User2 selectByPrimaryKey (Integer id); // other methods are omitted ...}

Finally, the Code for specific verification is implemented by the service layer. We will list the details below.

1. PROPAGATION_REQUIRED

Add the corresponding methods for User1Service and User2ServicePropagation.REQUIREDAttribute.

User1Service method:

@ Servicepublic class User1ServiceImpl implements User1Service {// omit other... @ Override @ Transactional (propagation = Propagation. REQUIRED) public void addRequired (User1 user) {user1Mapper. insert (user );}}

User2Service method:

@ Servicepublic class User2ServiceImpl implements User2Service {// omit other... @ Override @ Transactional (propagation = Propagation. REQUIRED) public void addRequired (User2 user) {user2Mapper. insert (user) ;}@ Override @ Transactional (propagation = Propagation. REQUIRED) public void addRequiredException (User2 user) {user2Mapper. insert (user); throw new RuntimeException ();}}

1.1 scenario 1

Transactions are not enabled in the peripheral method of this scenario.

Verification Method 1:

@ Override public void notransaction_exception_required_required () {User1 user1 = new User1 (); user1.setName ("zhangsan"); user1Service. addRequired (user1); User2 user2 = new User2 (); user2.setName (""); user2Service. addRequired (user2); throw new RuntimeException ();}

Verification Method 2:

@ Override public void notransaction_required_required_exception () {User1 user1 = new User1 (); user1.setName ("zhangsan"); user1Service. addRequired (user1); User2 user2 = new User2 (); user2.setName (""); user2Service. addRequiredException (user2 );}

Perform the verification methods respectively. Results:

Verification Method serial number database Result Analysis

Verification Method serial number Database results Result Analysis
1 "John" and "Li Si" are all inserted. The transaction is not enabled for the peripheral method, and the "Zhang San" and "Li Si" methods are inserted to run independently in their own transactions, an exception in the peripheral method does not affect the internal insertion of transactions that are independent of the "Zhang San" and "Li Si" methods.
2 "John" is inserted, and "Li Si" is not inserted. There are no transactions in the peripheral methods, and the insertion of the "Zhang San" and "Li Si" methods all run independently in their own transactions, therefore, inserting the "Li Si" method will throw an exception and only roll back the inserted "Li Si" method. inserting the "Zhang San" method will not be affected.

Conclusion: With these two methods, we have proved Propagation when transactions are not enabled in the peripheral method. the internal Methods Modified by REQUIRED will start their own transactions, and the opened transactions are independent of each other and do not interfere with each other.

1.2 scenario 2

The peripheral method enables transactions, which is a scenario with high usage.

Verification Method 1:

@ Override @ Transactional (propagation = Propagation. REQUIRED) public void transaction_exception_required_required () {User1 user1 = new User1 (); user1.setName ("zhangsan"); user1Service. addRequired (user1); User2 user2 = new User2 (); user2.setName (""); user2Service. addRequired (user2); throw new RuntimeException ();}

Verification Method 2:

@ Override @ Transactional (propagation = Propagation. REQUIRED) public void transaction_required_required_exception () {User1 user1 = new User1 (); user1.setName ("zhangsan"); user1Service. addRequired (user1); User2 user2 = new User2 (); user2.setName (""); user2Service. addRequiredException (user2 );}

Verification Method 3:

@ Transactional @ Override public void transaction_required_required_exception_try () {User1 user1 = new User1 (); user1.setName ("zhangsan"); user1Service. addRequired (user1); User2 user2 = new User2 (); user2.setName (""); try {user2Service. addRequiredException (user2);} catch (Exception e) {System. out. println ("method rollback ");}}

Perform the verification methods respectively. Results:

Verification Method serial number Database results Result Analysis
1 Neither "Zhang San" nor "Li Si" are inserted. The external method starts the transaction, the internal method joins the external method transaction, the external method rolls back, the internal method also needs to roll back.
2 Neither "Zhang San" nor "Li Si" are inserted. When the peripheral method starts a transaction, the internal method is added to the peripheral method transaction, and the internal method throws an exception rollback, the peripheral method perceives an exception and causes the overall transaction rollback.
3 Neither "Zhang San" nor "Li Si" are inserted. When the peripheral method starts a transaction, the internal method is added to the peripheral method transaction, and the internal method throws an exception rollback. Even if the catch method is not perceived by the peripheral method, the entire transaction is still rolled back.

Conclusion: The above test results show that the transaction is started in the peripheral method.Propagation.REQUIREDThe modified internal method is added to the transaction of the peripheral method.Propagation.REQUIREDBoth the modified internal and peripheral Methods belong to the same transaction. As long as one method is rolled back, the entire transaction is rolled back.

2. PROPAGATION_REQUIRES_NEW

Add the corresponding methods for User1Service and User2ServicePropagation.REQUIRES_NEWAttribute.

User1Service method:

@ Servicepublic class User1ServiceImpl implements User1Service {// omit other... @ Override @ Transactional (propagation = Propagation. REQUIRES_NEW) public void addRequiresNew (User1 user) {user1Mapper. insert (user) ;}@ Override @ Transactional (propagation = Propagation. REQUIRED) public void addRequired (User1 user) {user1Mapper. insert (user );}}

User2Service method:

@ Servicepublic class User2ServiceImpl implements User2Service {// omit other... @ Override @ Transactional (propagation = Propagation. REQUIRES_NEW) public void addRequiresNew (User2 user) {user2Mapper. insert (user) ;}@ Override @ Transactional (propagation = Propagation. REQUIRES_NEW) public void addRequiresNewException (User2 user) {user2Mapper. insert (user); throw new RuntimeException ();}}

2.1 scenario 1

The transaction is not enabled for the peripheral method.

Verification Method 1:

@ Override public void notransaction_exception_requiresNew_requiresNew () {User1 user1 = new User1 (); user1.setName ("James"); user1Service. addRequiresNew (user1); User2 user2 = new User2 (); user2.setName (""); user2Service. addRequiresNew (user2); throw new RuntimeException ();}

Verification Method 2:

@ Override public void notransaction_requiresNew_requiresNew_exception () {User1 user1 = new User1 (); user1.setName ("zhangsan"); user1Service. addRequiresNew (user1); User2 user2 = new User2 (); user2.setName (""); user2Service. addRequiresNewException (user2 );}

Perform the verification methods respectively. Results:

Verification Method serial number Database results Result Analysis
1 Insert "Michael" and "Li Si. There are no transactions in the peripheral methods, and the insertion of the "Zhang San" and "Li Si" Methods runs independently in their own transactions. The exception rollback thrown by the peripheral methods does not affect the internal methods.
2 "Zhang San" inserted, "Li Si" NOT INSERTED The peripheral method does not enable transactions. Insert the "Zhang San" method and insert 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: The two methods prove that the transaction is not started in the peripheral method.Propagation.REQUIRES_NEWThe modified internal method will start your own transactions, and the opened transactions are independent of each other and do not interfere with each other.

2.2 scenario 2

Start the transaction in the peripheral method.

Verification Method 1:

@ Override @ Transactional (propagation = Propagation. REQUIRED) public void transaction_exception_required_requiresNew_requiresNew () {User1 user1 = new User1 (); user1.setName ("zhangsan"); user1Service. addRequired (user1); User2 user2 = new User2 (); user2.setName (""); user2Service. addRequiresNew (user2); User2 user3 = new User2 (); user3.setName ("Wang Wu"); user2Service. addRequiresNew (user3); throw new RuntimeException ();}

Verification Method 2:

@ Override @ Transactional (propagation = Propagation. REQUIRED) public void transaction_required_requiresNew_requiresNew_exception () {User1 user1 = new User1 (); user1.setName ("zhangsan"); user1Service. addRequired (user1); User2 user2 = new User2 (); user2.setName (""); user2Service. addRequiresNew (user2); User2 user3 = new User2 (); user3.setName ("Wang Wu"); user2Service. addRequiresNewException (user3 );}

Verification Method 3:

@ Override @ Transactional (propagation = Propagation. REQUIRED) public void transaction_required_requiresNew_requiresNew_exception_try () {User1 user1 = new User1 (); user1.setName ("zhangsan"); user1Service. addRequired (user1); User2 user2 = new User2 (); user2.setName (""); user2Service. addRequiresNew (user2); User2 user3 = new User2 (); user3.setName (""); try {user2Service. addRequiresNewException (user3);} catch (Exception e) {System. out. println ("rollback ");}}

Perform the verification methods respectively. Results:

Verification Method serial number Database results Result Analysis
1 "Zhang San" is not inserted, "Li Si" is inserted, and "Wang Wu" is inserted. The peripheral method starts the transaction, inserts the "Zhang San" method and the peripheral method into a transaction, inserts the "Li Si" method, and inserts the "Wang Wu" method into the independent new transaction, respectively, the peripheral method throws an exception and only rolls back the same transaction method as the peripheral method. Therefore, the "Michael" method is inserted for rollback.
2 "Zhang San" is not inserted, "Li Si" is inserted, and "Wang Wu" is not inserted. The peripheral method starts the transaction, inserts the "Zhang San" method and the peripheral method into a transaction, inserts the "Li Si" method, and inserts the "Wang Wu" method into the independent new transaction. Inserting the "Wang Wu" method throws an exception. First, the transaction that inserts the "Wang Wu" method is rolled back. The exception continues to be thrown and the transaction of the peripheral method is also rolled back, therefore, the Insert "Michael" method is also rolled back.
3 "Zhang San" is inserted, "Li Si" is inserted, and "Wang Wu" is not inserted. The peripheral method starts the transaction, inserts the "Zhang San" method and the peripheral method into a transaction, inserts the "Li Si" method, and inserts the "Wang Wu" method into the independent new transaction. Inserting the "Wang Wu" method throws an exception. First, the transaction that inserts the "Wang Wu" method is rolled back. The exception is caught and is not perceived by the peripheral method. The transaction of the peripheral method is not rolled back, therefore, the "Zhang San" method is successfully inserted.

Conclusion: when the transaction is started by the peripheral methodPropagation.REQUIRES_NEWThe modified internal method will still start independent transactions independently from external method transactions. Internal methods and external method transactions are independent from each other and do not interfere with each other.

3. PROPAGATION_NESTED

Add the corresponding methods for User1Service and User2ServicePropagation.NESTEDAttribute.

User1Service method:

@ Servicepublic class User1ServiceImpl implements User1Service {// omit other... @ Override @ Transactional (propagation = Propagation. NESTED) public void addNested (User1 user) {user1Mapper. insert (user );}}

User2Service method:

@ Servicepublic class User2ServiceImpl implements User2Service {// omit other... @ Override @ Transactional (propagation = Propagation. NESTED) public void addNested (User2 user) {user2Mapper. insert (user) ;}@ Override @ Transactional (propagation = Propagation. NESTED) public void addNestedException (User2 user) {user2Mapper. insert (user); throw new RuntimeException ();}}

3.1 scenario 1

Transactions are not enabled in the peripheral method of this scenario.

Verification Method 1:

@ Override public void notransaction_exception_nested_nested () {User1 user1 = new User1 (); user1.setName ("zhangsan"); user1Service. addNested (user1); User2 user2 = new User2 (); user2.setName ("Li Si"); user2Service. addNested (user2); throw new RuntimeException ();}

Verification Method 2:

@ Override public void notransaction_nested_nested_exception () {User1 user1 = new User1 (); user1.setName ("zhangsan"); user1Service. addNested (user1); User2 user2 = new User2 (); user2.setName ("Li Si"); user2Service. addNestedException (user2 );}

Perform the verification methods respectively. Results:

Verification Method serial number Database results Result Analysis
1 "John" and "Li Si" are all inserted. The transaction is not enabled for the peripheral method, and the "Zhang San" and "Li Si" methods are inserted to run independently in their own transactions, an exception in the peripheral method does not affect the internal insertion of transactions that are independent of the "Zhang San" and "Li Si" methods.
2 "John" is inserted, and "Li Si" is not inserted. There are no transactions in the peripheral methods, and the insertion of the "Zhang San" and "Li Si" methods all run independently in their own transactions, therefore, inserting the "Li Si" method will throw an exception and only roll back the inserted "Li Si" method. inserting the "Zhang San" method will not be affected.

Conclusion: The two methods prove that the transaction is not started in the peripheral method.Propagation.NESTEDAndPropagation.REQUIREDIn the same way, all the modified internal methods start their own transactions, and the opened transactions are independent of each other and do not interfere with each other.

3.2 Scenario 2

Start the transaction in the peripheral method.

Verification Method 1:

@ Transactional @ Override public void transaction_exception_nested_nested () {User1 user1 = new User1 (); user1.setName ("James"); user1Service. addNested (user1); User2 user2 = new User2 (); user2.setName ("Li Si"); user2Service. addNested (user2); throw new RuntimeException ();}

Verification Method 2:

@ Transactional @ Override public void transaction_nested_nested_exception () {User1 user1 = new User1 (); user1.setName ("James"); user1Service. addNested (user1); User2 user2 = new User2 (); user2.setName ("Li Si"); user2Service. addNestedException (user2 );}

Verification Method 3:

@ Transactional @ Override public void transaction_nested_nested_exception_try () {User1 user1 = new User1 (); user1.setName ("zhangsan"); user1Service. addNested (user1); User2 user2 = new User2 (); user2.setName (""); try {user2Service. addNestedException (user2);} catch (Exception e) {System. out. println ("method rollback ");}}

Perform the verification methods respectively. Results:

Verification Method serial number Database results Result Analysis
1 Neither "Zhang San" nor "Li Si" are inserted. The external method starts the transaction. The internal transaction is the sub-transaction of the peripheral transaction. The external method rolls back, and the internal method also rolls back.
2 Neither "Zhang San" nor "Li Si" are inserted. When the peripheral method starts a transaction, the internal transaction is a sub-transaction of the peripheral transaction. The internal method throws an exception rollback, and the peripheral method senses an exception and causes the overall transaction rollback.
3 "Zhang San" and "Li Si" are not inserted. When the peripheral method starts a transaction, the internal transaction is the sub-transaction of the peripheral transaction. if an exception is thrown by inserting the "Zhang San" internal method, the sub-transaction can be rolled back independently.

Conclusion: The above test results show that the transaction is started in the peripheral method.Propagation.NESTEDThe modified internal method is a sub-transaction of an external transaction. When the peripheral primary transaction is rolled back, the sub-transaction must be rolled back, internal sub-transactions can be rolled back independently without affecting peripheral primary transactions and other sub-transactions.

4. Similarities and Differences between REQUIRED, REQUIRES_NEW, and NESTED

From the comparison between "1.2 scenario 2" and "3.2 Scenario 2", we can see that:

Both the NESTED and REQUIRED methods are peripheral method transactions. If the peripheral method throws an exception, the transactions of the two methods will be rolled back. However, REQUIRED is a peripheral method transaction, so it is the same as the peripheral transaction. Once the REQUIRED transaction throws an exception and is rolled back, the peripheral method transaction will also be rolled back. NESTED is a sub-transaction of the peripheral method and has a separate storage point. Therefore, the NESTED method throws an exception and is rolled back without affecting the transaction of the peripheral method.

From the comparison between "2.2 scenario 2" and "3.2 Scenario 2", we can see that:

Both NESTED and REQUIRES_NEW can roll back internal method transactions without affecting peripheral method transactions. However, because NESTED is a NESTED transaction, after the peripheral method is rolled back, the sub-transaction as the peripheral method transaction will also be rolled back. REQUIRES_NEW is implemented by starting a new transaction. The internal transaction and the peripheral transaction are two transactions, and the rollback of the peripheral transaction will not affect the internal transaction.

5. Other Transaction propagation Behaviors

Given the length of the article, tests of other transaction propagation behaviors are not described here. Interested readers can find the corresponding test code and result explanations in the source code. Door: https://github.com/TmTse/tran...

Simulation case

After introducing so many transaction propagation behaviors, how can we apply them in actual work? Here is an example:

Suppose we have a registration method, which calls the method of adding points, if we want to add points, the registration process will not be affected (that is, the registration method cannot be rolled back if the add points fail to be rolled back), we will write as follows:

@ Service public class UserServiceImpl implements UserService {@ Transactional public void register (User user) {try {membershipPointService. addPoint (Point point);} catch (Exception e) {// omitting ...} // omitting ...} // omitting ...}

We also stipulate that registration failure should be affectedaddPoint()Method (the Registration Method rollback method also needs to be rolled back), thenaddPoint()The method needs to be implemented as follows:

@ Service public class MembershipPointServiceImpl implements MembershipPointService {@ Transactional (propagation = Propagation. NESTED) public void addPoint (Point point) {try {recordService. addRecord (Record record);} catch (Exception e) {// omitting ...} // omitting ...} // omitting ...}

We noticed thataddPoint()Is also calledaddRecord()This method is used to record logs. Its implementation is as follows:

@ Service public class RecordServiceImpl implements RecordService {@ Transactional (propagation = Propagation. NOT_SUPPORTED) public void addRecord (Record record) {// omitting ...}

We noticed thataddRecord()Methodpropagation = Propagation.NOT_SUPPORTED Because it does not matter how accurate the log is, you can add one or more logs, soaddRecord()Method itself and peripheralsaddPoint()Method throws an exception.addRecord()Method rollback, andaddRecord()Method throws an exception and does not affect the perimeter.addPoint()Method execution.

Through this example, we believe that everyone has a more intuitive understanding of the use of transaction propagation behaviors. The combination of various attributes can indeed make our business more flexible and diverse.

Conclusion

Based on the above introduction, I believe that you have a deeper understanding of Spring transaction Propagation Behavior and hope everyone can help with their daily development work.

Summary

The above is all the content of this article. I hope the content of this article has some reference and learning value for everyone's learning or work. If you have any questions, please leave a message to us, thank you for your support.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.