• Hello, everyone, here is the public number: Java Xiaojie to refueling
  • We all know that Spring has seven kinds of transaction propagation behaviors, and they are often asked in interviews, but they all look too similar to each other, so today we will use this article to thoroughly understand them!
  • There are a lot of code argumentation in the article, it is recommended to collect in the computer end to eat
  • Don’t say much. Just drive

Spring’s seven transaction propagation behaviors

The following transaction propagation attributes are transaction annotations typed on method B

  • Propagation.REQUIRED: Default transaction Propagation behavior of spring, where method A calls method B. If A has A transaction, then method B is added to the transaction in method A. Otherwise, method B opens A new transaction itself

  • Propagation.SUPPORTS: method A calls method b. If method A has A transaction, then method B is added to the transaction in method A. Otherwise, method B executes in A non-transactional manner

  • Propagation.MANDATORY: Method A must call method b. If A has no transaction, method B will throw an exception

  • Propagation.REQUIRES_NEW: Method A calls method B. If A has transactions, then method B suspends the transactions of method A and method B restarts A new transaction itself

  • Propaganda. NOT_SUPPORTED: method A calls method B. If A has A transaction, then B suspends the transaction in method A. Otherwise, method B executes in A non-transactional way

  • Propagation.NEVER: No transaction is supported. Method A calls method B. If A has transaction, method B will throw an exception

  • Propagandized: Same propagandized.REQUIRED, but this propagandized property can also save a state node, preventing all NESTED transactions from being rolled back

Now that we’ve looked at some of the explanations for each propagation attribute, with the rest of the explanation in mind, let’s look at the actual code

In actual combat

Propagation.REQUIRED

  • Spring’s default transaction propagation property, method A calls method B. If method A has A transaction, then method B joins the transaction in method A, otherwise method B starts A new transaction itself

A interface

@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
    @Override
    public Integer updateTest(Test updateVO) {
        System.out.println("A updateTest method");
        int i = testMapper.updateByPrimaryKey(updateVO);
        Test test =new Test("Xiao jie".24);
        // Call the insert Ttest method of the B interface
        BTestService.insertTest(test);
        return i;
    }
Copy the code

Let’s look at interface B

 @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
    @Override
    public Integer insertTest(Test updateVO) {
        System.out.println("B insertTest method");
        int i = testMapper.insert(updateVO);
        // Throw an exception
        int a = 1 / 0 ;
        return i;
    }
Copy the code

So if I write my code like this, what data is going to end up in this database?

@PostMapping("/update")
    public Object updateTest(@RequestBody Test updateVO){
        Integer flag = ATestService.updateTest(updateVO);
        return flag;
    }
Copy the code

Content of original database

Postman, let’s have a look

You can see the result of the console is that they share one transaction (sqlSession is the same)

In this case, the content of the database does not change, indicating that both interfaces A and B are rolled back

A common interview question arises at this point: If an exception thrown by method B is caught by method A try catch, will the operation of method A be rolled back?

The answer is: it does

Looking at the test code, we added code to method A to catch the exception thrown by method B

@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
    @Override
    public Integer updateTest(Test updateVO) {
        System.out.println("A updateTest method");
        int i = testMapper.updateByPrimaryKey(updateVO);
        Test test =new Test("Xiao jie".24);
          try {
               // Call the insert Ttest method transaction method
               BTestService.insertTest(test);
           }catch (Exception e){
               System.out.println("Method A recovered the anomaly."+e.getMessage());
           }
        return i;
    }
Copy the code

One more postman, and the console outputs the following test results

When we look at the database, the data hasn’t changed

Then again, if A does not catch and method B catches the exception itself, will the transaction be rolled back?

The answer is: no

Change the code of interface B

@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
    @Override
    public Integer insertTest(Test updateVO) {
        System.out.println("B insertTest method");
        int i = 0;

        try {
             i = testMapper.insert(updateVO);
            // Throw an exception
            int a = 1 / 0 ;
        }catch (Exception e){
            System.out.println("Method B recovered the anomaly."+e.getMessage());
        }

        return i;
    }
Copy the code

Also remove the catch exception for method A

@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
    @Override
    public Integer updateTest(Test updateVO) {
        System.out.println("A updateTest method");
        int i = testMapper.updateByPrimaryKey(updateVO);
        Test test =new Test("Xiao jie".24);
        // Call the insert Ttest method transaction method
        BTestService.insertTest(test);
        return i;
    }
Copy the code

The result at this point is

The data in the database is

Interface A and interface B are in effect and are operating on the database

What’s the difference between method A and method B?
  • The difference is, if method A catches an exception,Method B’s transaction annotations are aware of the exception and roll back;
  • And method B captured by itself,The transaction annotation of method B will not be aware of the exception and will not be rolled back

Once you understand the above example, it is easy to analyze whether the various exception/propagation properties are rolled back or not.

Propagation.SUPPORTS

  • Method A calls method B. If method A has A transaction, then method B is added to the transaction in method A. Otherwise, method B executes itself in A non-transactional manner

We change the transaction Propagation attribute of interface B to Propagation.SUPPORTS

@Transactional(rollbackFor = Exception.class,propagation = Propagation.SUPPORTS)
    @Override
    public Integer insertTest(Test updateVO) {
        System.out.println("B insertTest method");
        int i = testMapper.insert(updateVO);
        // Throw an exception
        int a = 1 / 0 ;
        return i;
    }
Copy the code

A method of

@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
    @Override
    public Integer updateTest(Test updateVO) {
        System.out.println("A updateTest method");
        int i = testMapper.updateByPrimaryKey(updateVO);
        Test test =new Test("Xiao jie".24);
        // Call the insert Ttest method transaction method
        BTestService.insertTest(test);
        return i;
    }
Copy the code

The test results were

The database value hasn’t changed either, so both operations are rolled back so let’s take A look at this after we remove the transaction annotation for method A

 @Override
    public Integer updateTest(Test updateVO) {
        System.out.println("A updateTest method");
        int i = testMapper.updateByPrimaryKey(updateVO);
        Test test =new Test("Xiao jie".24);
        // Call the insert Ttest method transaction method
        BTestService.insertTest(test);
        return i;
    }
Copy the code

The test results are yes

The database value is

Thus, neither operation is rolled back, and method B is a non-transactional operation

Propagation.MANDATORY

Method A calls method B. If method A has no transaction, then method B will throw an exception

Interface A:

@Override
    public Integer updateTest(Test updateVO) {
        System.out.println("A updateTest method");
        int i = testMapper.updateByPrimaryKey(updateVO);
        Test test =new Test("Xiao jie".24);
        // Call the insert Ttest method transaction method
        BTestService.insertTest(test);
        return i;
    }
Copy the code

B Interfaces are as follows:

@Transactional(rollbackFor = Exception.class,propagation = Propagation.MANDATORY)
    @Override
    public Integer insertTest(Test updateVO) {
        System.out.println("B insertTest method");
        int i = testMapper.insert(updateVO);
        // Throw an exception
        int a = 1 / 0 ;
        return i;
    }
Copy the code

The value of the database has not changed, so it can be seen that the transaction annotation of method B isPropagation.MANDATORYWhen method A has no transaction, an error is reported.

Propagation.REQUIRES_NEW

  • Method A calls method B. If method A has A transaction, method B suspends method A’s transaction and method B restarts A new transaction itself

A method of

@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
    @Override
    public Integer updateTest(Test updateVO) {
        System.out.println("A updateTest method");
        int i = testMapper.updateByPrimaryKey(updateVO);
        Test test =new Test("Xiao jie".24);
        // Call the insert Ttest method transaction method
        BTestService.insertTest(test);
        return i;
    }
Copy the code

Method B

@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW)
    @Override
    public Integer insertTest(Test updateVO) {
        System.out.println("B insertTest method");
        int i = testMapper.insert(updateVO);
        // Throw an exception
        int a = 1 / 0 ;
        return i;
    }
Copy the code

As a result,Two interfaces can be foundDefaultSqlSessionIf interface A has A transaction, interface B suspends it and restarts A new transaction

  • If A does not catch an exception, then both A and B methods are rolled back
  • Method A catches an exception, then method A does not roll back

Again, if an exception is caught inside a method, then the transaction annotations on the method are not aware of the exception and the operation on the method is not rolled back!

Propagation.NOT_SUPPORTED

Method A calls method B. If method A has A transaction, then method B suspends the transaction in method A, otherwise method B executes itself in A non-transactional manner

A interface

@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
    @Override
    public Integer updateTest(Test updateVO) {
        System.out.println("A updateTest method");
        int i = testMapper.updateByPrimaryKey(updateVO);
        Test test =new Test("Xiao jie".24);
        // Call the insert Ttest method transaction method
        BTestService.insertTest(test);
        return i;
    }
Copy the code

B interface

@Transactional(rollbackFor = Exception.class,propagation = Propagation.NOT_SUPPORTED)
    @Override
    public Integer insertTest(Test updateVO) {
        System.out.println("B insertTest method");
        int i = testMapper.insert(updateVO);
        // Throw an exception
        int a = 1 / 0 ;
        return i;
    }
Copy the code

The test results were

The result of the database is

We can see that interface B takes effect and does insert A piece of data, but interface A does not take effect and does not change data. This is because an exception is thrown in interface B. Since the transaction Propagation behavior of interface B is propagandis. NOT_SUPPORTED, the transaction of interface A will be suspended. Interface B operates in A non-transactional manner (so the error is not rolled back). If the exception is found in interface A, it will be rolled back, so the data will not be changed

Propagation.NEVER

  • Method A calls method B. If A has A transaction, then method B will throw an exception

A interface

@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
    @Override
    public Integer updateTest(Test updateVO) {
        System.out.println("A updateTest method");
        int i = testMapper.updateByPrimaryKey(updateVO);
        Test test =new Test("Xiao jie".24);
        // Call the insert Ttest method transaction method
        BTestService.insertTest(test);
        return i;
    }
Copy the code

B interface

@Transactional(rollbackFor = Exception.class,propagation = Propagation.NEVER)
    @Override
    public Integer insertTest(Test updateVO) {
        System.out.println("B insertTest method");
        int i = testMapper.insert(updateVO);
        // Throw an exception
        int a = 1 / 0 ;
        return i;
    }
Copy the code

As a result,

The database has not been changed, so it can be seen that when interface A calls interface B when there is A transaction, an error is directly reported

Propagation.NESTED

  • REQUIRED, but this Propagation property can also save a state node to avoid all nested transactions being rolled back

A interface

@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
    @Override
    public Integer updateTest(Test updateVO) {
        System.out.println("A updateTest method");
        int i = testMapper.updateByPrimaryKey(updateVO);
        Test test =new Test("Xiao jie".24);
          try {
               // Call the insert Ttest method transaction method
               BTestService.insertTest(test);
           }catch (Exception e){
               System.out.println("Method A recovered the anomaly."+e.getMessage());
           }
        return i;
    }
Copy the code

B interface

@Transactional(rollbackFor = Exception.class,propagation = Propagation.NESTED)
    @Override
    public Integer insertTest(Test updateVO) {
        System.out.println("B insertTest method");
        int i = testMapper.insert(updateVO);
        // Throw an exception
        int a = 1 / 0 ;
        return i;
    }
Copy the code

As a result,

The database changes are as follows

The operation on interface A is not rolled back, but the operation on interface B is rolled back because of the “savePoint” safety point. During the operation on interface B, the current state (the operation on interface A has finished) is saved to the safety point. If interface B fails, the rollback will only be rolled back to this safety point

Note: You need to try catch the exception of interface B in interface A

Here is the public number: Java Xiaojie to refueling, we will see next time

Good article

  • How is the network connected from the four-layer model
  • Wanted to talk to you about memory management for the operating system
  • Define a Java annotation of your own
  • Five thousand words. Yes, we have an HTTP.
  • The interviewer from JD.com asked me, “What about MySql,MVCC?”