Eight affairs in actual Spring

1, Preparation before test

Data table structure:

1. StuService

package com.ybqdren.service;

import com.ybqdren.pojo.Stu;

/**
 * Wen(Joan) Zhao <withzhaowen@126.com>
 * 2021/9/15
 */
public interface StuService {
    public Stu getStuInfo(int id);

    public void saveStu();

    public void saveStu(int id);

    public void updateStu(int id);

    public void deleteStu(int id);

    public void saveParent();

    public void saveChildren();

}


2. StuServiceImpl

package com.ybqdren.service.impl;

import com.ybqdren.mapper.StuMapper;
import com.ybqdren.pojo.Stu;
import com.ybqdren.service.StuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;


/**
 * Wen(Joan) Zhao <withzhaowen@126.com>
 * 2021/9/15
 */

@Service
public class StuServiceImpl implements StuService {
    @Autowired
    private StuMapper stuMapper;

    //@Transactional(propagation = Propagation.SUPPORTS)
    @Override
    public Stu getStuInfo(int id) {
        return (Stu) stuMapper.selectByPrimaryKey(id);
    }

    //@Transactional(propagation = Propagation.REQUIRED)
    @Override
    public void saveStu() {
        Stu stu = new Stu();
        stu.setName("jack");
        stu.setAge(18);
        stuMapper.insert(stu);

    }

   // @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public void saveStu(int id) {

    }

    //@Transactional(propagation = Propagation.REQUIRED)
    @Override
    public void updateStu(int id) {
        Stu stu = new Stu();
        stu.setId(id);
        stu.setName("lucy");
        stu.setAge(20);
        stuMapper.updateByPrimaryKey(stu);
    }

    //@Transactional(propagation = Propagation.REQUIRED)
    @Override
    public void deleteStu(int id) {
        stuMapper.deleteByPrimaryKey(id);
    }

    @Override
    public void saveParent() {
        Stu stu = new Stu();
        stu.setName("parent");
        stu.setAge(19);
        stuMapper.insert(stu);
    }

    @Override
    public void saveChildren() {
        saveChild1();
    }

    public void saveChild1(){
        Stu stu1 = new Stu();
        stu1.setName("child-1");
        stu1.setAge(11);
        stuMapper.insert(stu1);
    }

    public void saveChild2(){
        Stu stu1 = new Stu();
        stu1.setName("child-2");
        stu1.setAge(11);
        stuMapper.insert(stu1);
    }
}


3. TestTransService

package com.ybqdren.service;

public interface TestTransService {
    public void testPropagationTrans();
}


4. TestTransServiceImpl

package com.ybqdren.service.impl;

import com.ybqdren.service.StuService;
import com.ybqdren.service.TestTransService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service
public class TestTransServiceImpl implements TestTransService {

    @Autowired
    private StuService stuService;

    // @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public void testPropagationTrans() {
        stuService.saveParent();
    }
}


5. TransTest

import com.ybqdren.Application;
import com.ybqdren.service.StuService;
import com.ybqdren.service.TestTransService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

/**
 * Wen(Joan) Zhao <withzhaowen@126.com>
 * 2021/9/19
 */

@SpringBootTest(classes = Application.class)
public class TransTest {
    @Autowired
    private StuService stuService;

    @Autowired
    private TestTransService testTransService;

    @Test
    public void myTest() {
        testTransService.testPropagationTrans();
    }
}


2, Do not use transactions

1. StuServiceImpl

package com.ybqdren.service.impl;

import com.ybqdren.mapper.StuMapper;
import com.ybqdren.pojo.Stu;
import com.ybqdren.service.StuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;


/**
 * Wen(Joan) Zhao <withzhaowen@126.com>
 * 2021/9/15
 */

@Service
public class StuServiceImpl implements StuService {
    @Autowired
    private StuMapper stuMapper;

 // .....
    
    @Override
    public void saveParent() {
        Stu stu = new Stu();
        stu.setName("parent");
        stu.setAge(19);
        stuMapper.insert(stu);
    }

    @Override
    public void saveChildren() {
        saveChild1();
        // Add a code that will definitely report an exception between the two operations of adding data
        int a = 1/0;
        saveChild2();
    }

    public void saveChild1(){
        Stu stu1 = new Stu();
        stu1.setName("child-1");
        stu1.setAge(11);
        stuMapper.insert(stu1);
    }

    public void saveChild2(){
        Stu stu1 = new Stu();
        stu1.setName("child-2");
        stu1.setAge(11);
        stuMapper.insert(stu1);
    }
}


2. TestTransServiceImpl

package com.ybqdren.service.impl;

import com.ybqdren.service.StuService;
import com.ybqdren.service.TestTransService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service
public class TestTransServiceImpl implements TestTransService {

    @Autowired
    private StuService stuService;

    // @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public void testPropagationTrans() {
        stuService.saveParent();

        // newly added
        stuService.saveChildren();
    }
}


3. Database after operation execution

1) Report exception




2) Database (before and after)


You can see that saveChildren saves only one data, and child-2 is not inserted

After an exception occurs, child-2 cannot be saved to the database and is not rolled back


3, Business

1. REQUIRED

1) Introduction

  1. Use the current transaction. If there is no transaction, create a new transaction. The sub method must run in a transaction;
  2. If there is a transaction, join the transaction as a whole.

2) Add in parent method

Start the transaction in TestTransServiceImpl

    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public void testPropagationTrans() {
        stuService.saveParent();

        stuService.saveChildren();
    }

After execution

As you can see, no data is inserted into the database here
Because transaction propagation is REQUIRED here, although neither saveParent nor saveChild methods have transactions, they are also propagated to REQUIRED transactions
Therefore, when the parent method uses the REQUIRED transaction, the transactions in the two child methods will be rolled back after the exception occurs in the child method


3) Add in a single sub method

Clear transactions on parent method
//    @Transactional(propagation = Propagation.REQUIRED)    @Override    public void testPropagationTrans() {        stuService.saveParent();        stuService.saveChildren();    }

Add a transaction on the saveChild method
    @Override    public void saveParent() {        Stu stu = new Stu();        stu.setName("parent");        stu.setAge(19);        stuMapper.insert(stu);    }    @Transactional(propagation = Propagation.REQUIRED)    @Override    public void saveChildren() {        saveChild1();        int a = 1/0;        saveChild2();    }public void saveChild1(){    Stu stu1 = new Stu();    stu1.setName("child-1");    stu1.setAge(11);    stuMapper.insert(stu1);}public void saveChild2(){    Stu stu1 = new Stu();    stu1.setName("child-2");    stu1.setAge(11);    stuMapper.insert(stu1);}

After execution


You can see that the data executed by the saveParent method is added to the database, but saveChild does not.
That is, when there is no transaction in the current method body, he goes back and recreates a transaction


In other words, the saveChildren method must exist in a transaction to run, so it rolls back after an exception, while the saveParent has no transaction.


2. SUPPORTS

1) Introduction

If there is a transaction currently, the transaction is used; If there are currently no transactions, no transactions are used.

2) The outer layer has no transactions, while the sub method has

@Overridepublic void testPropagationTrans() {    stuService.saveParent();    stuService.saveChildren();}

@Overridepublic void saveParent() {    Stu stu = new Stu();    stu.setName("parent");    stu.setAge(19);    stuMapper.insert(stu);}@Transactional(propagation = Propagation.SUPPORTS)@Overridepublic void saveChildren() {    saveChild1();    int a = 1/0;    saveChild2();}public void saveChild1(){    Stu stu1 = new Stu();    stu1.setName("child-1");    stu1.setAge(11);    stuMapper.insert(stu1);}public void saveChild2(){    Stu stu1 = new Stu();    stu1.setName("child-2");    stu1.setAge(11);    stuMapper.insert(stu1);}

After execution





3) The outer layer uses transactions, and one of the inner layer's sub methods also uses transactions

@Transactional(propagation = Propagation.REQUIRED)@Overridepublic void testPropagationTrans() {    stuService.saveParent();    stuService.saveChildren();}

@Overridepublic void saveParent() {    Stu stu = new Stu();    stu.setName("parent");    stu.setAge(19);    stuMapper.insert(stu);}@Transactional(propagation = Propagation.SUPPORTS)@Overridepublic void saveChildren() {    saveChild1();    int a = 1/0;    saveChild2();}public void saveChild1(){    Stu stu1 = new Stu();    stu1.setName("child-1");    stu1.setAge(11);    stuMapper.insert(stu1);}public void saveChild2(){    Stu stu1 = new Stu();    stu1.setName("child-2");    stu1.setAge(11);    stuMapper.insert(stu1);}

After execution



Empty before and after


3. MANDATORY

1) Introduction

The propagation property enforces the existence of a transaction, and throws an exception if it does not exist.

2) The outer layer has no transactions. One of the sub methods has transactions and the other method has no transactions

    @Override    public void testPropagationTrans() {        stuService.saveParent();        stuService.saveChildren();    }

@Overridepublic void saveParent() {    Stu stu = new Stu();    stu.setName("parent");    stu.setAge(19);    stuMapper.insert(stu);}@Transactional(propagation = Propagation.MANDATORY)@Overridepublic void saveChildren() {    saveChild1();    int a = 1/0;    saveChild2();}public void saveChild1(){    Stu stu1 = new Stu();    stu1.setName("child-1");    stu1.setAge(11);    stuMapper.insert(stu1);}public void saveChild2(){    Stu stu1 = new Stu();    stu1.setName("child-2");    stu1.setAge(11);    stuMapper.insert(stu1);}

After execution

Console error




database






3) There are transactions in the outer layer and sub methods. One method has transactions and the other does not

    @Transactional(propagation = Propagation.REQUIRED)    @Override    public void testPropagationTrans() {        stuService.saveParent();        stuService.saveChildren();    }

@Overridepublic void saveParent() {    Stu stu = new Stu();    stu.setName("parent");    stu.setAge(19);    stuMapper.insert(stu);}@Transactional(propagation = Propagation.MANDATORY)@Overridepublic void saveChildren() {    saveChild1();    int a = 1/0;    saveChild2();}public void saveChild1(){    Stu stu1 = new Stu();    stu1.setName("child-1");    stu1.setAge(11);    stuMapper.insert(stu1);}public void saveChild2(){    Stu stu1 = new Stu();    stu1.setName("child-2");    stu1.setAge(11);    stuMapper.insert(stu1);}

After execution




database






4. REQUIRES_NEW

1) Introduction

  1. If there is a current transaction, suspend the transaction and create a new transaction for your own use;
  2. If there is no current transaction, the same as REQUIRED.

2) The outer layer has no transactions, the child method has transactions, and the parent method has no transactions

    @Override    public void testPropagationTrans() {        stuService.saveParent();        stuService.saveChildren();    }
@Overridepublic void saveParent() {    Stu stu = new Stu();    stu.setName("parent");    stu.setAge(19);    stuMapper.insert(stu);}@Transactional(propagation = Propagation.REQUIRES_NEW)@Overridepublic void saveChildren() {    saveChild1();    int a = 1/0;    saveChild2();}public void saveChild1(){    Stu stu1 = new Stu();    stu1.setName("child-1");    stu1.setAge(11);    stuMapper.insert(stu1);}public void saveChild2(){    Stu stu1 = new Stu();    stu1.setName("child-2");    stu1.setAge(11);    stuMapper.insert(stu1);}

After execution




database






analysis

    @Override    public void testPropagationTrans() {        stuService.saveParent();        stuService.saveChildren();    }

saveParent has no transactions, while saveChildren has transactions, so the latter will be distinguished from the former. The latter will roll back when an error occurs, but the former will be normally stored in the database.


3) The outer layer has transactions, the child method has transactions, and the parent method has no transactions

    @Transactional(propagation = Propagation.REQUIRED)    @Override    public void testPropagationTrans() {        stuService.saveParent();        stuService.saveChildren();    }
@Overridepublic void saveParent() {    Stu stu = new Stu();    stu.setName("parent");    stu.setAge(19);    stuMapper.insert(stu);}@Transactional(propagation = Propagation.REQUIRES_NEW)@Overridepublic void saveChildren() {    saveChild1();    int a = 1/0;    saveChild2();}public void saveChild1(){    Stu stu1 = new Stu();    stu1.setName("child-1");    stu1.setAge(11);    stuMapper.insert(stu1);}public void saveChild2(){    Stu stu1 = new Stu();    stu1.setName("child-2");    stu1.setAge(11);    stuMapper.insert(stu1);}

After execution




database



Both front and back are empty.


analysis

@Transactional(propagation = Propagation.REQUIRES_NEW)@Overridepublic void saveChildren() {    saveChild1();    int a = 1/0;    saveChild2();}

After the saveChildren method is executed, a byzero exception will be generated, and this exception will be passed to its calling method:

@Transactional(propagation = Propagation.REQUIRED)@Overridepublic void testPropagationTrans() {    stuService.saveParent();    stuService.saveChildren();}

Because the current method has a transaction REQUIRED and becomes a whole, it will affect the saveParent called together in the current method, resulting in the rollback of saveParent.


4) An exception is also generated when using transactions in the outer layer. The child method has transactions and the parent method has no transactions

@Transactional(propagation = Propagation.REQUIRED)@Overridepublic void testPropagationTrans() {    stuService.saveParent();    stuService.saveChildren();        int a = 1/0;}
@Overridepublic void saveParent() {    Stu stu = new Stu();    stu.setName("parent");    stu.setAge(19);    stuMapper.insert(stu);}@Transactional(propagation = Propagation.REQUIRES_NEW)@Overridepublic void saveChildren() {    saveChild1();    saveChild2();}public void saveChild1(){    Stu stu1 = new Stu();    stu1.setName("child-1");    stu1.setAge(11);    stuMapper.insert(stu1);}public void saveChild2(){    Stu stu1 = new Stu();    stu1.setName("child-2");    stu1.setAge(11);    stuMapper.insert(stu1);}

After execution




database



Two more pieces of data:



analysis

Because a new transaction is used in the sub method saveChildren, the current transaction is suspended, so the transaction of the current method will not affect its sub methods:

@Transactional(propagation = Propagation.REQUIRED)@Overridepublic void testPropagationTrans() {    stuService.saveParent();    stuService.saveChildren();    int a = 1/0;}

However, there is no transaction in saveParent, so it will be rolled back, so the value inserted in saveChildren will be saved in the database:



5) The outer layer has REQUIRED transactions, the child method has REQUIRED transactions, and the parent method has no transactions

Both sub methods and outer layers use REQUIRED transactions, that is, both methods use the same transaction:

@Transactional(propagation = Propagation.REQUIRED)@Overridepublic void testPropagationTrans() {    stuService.saveParent();    stuService.saveChildren();    int a = 1/0;}
@Overridepublic void saveParent() {    Stu stu = new Stu();    stu.setName("parent");    stu.setAge(19);    stuMapper.insert(stu);}@Transactional(propagation = Propagation.REQUIRED)@Overridepublic void saveChildren() {    saveChild1();    //        int a = 1/0;    saveChild2();}public void saveChild1(){    Stu stu1 = new Stu();    stu1.setName("child-1");    stu1.setAge(11);    stuMapper.insert(stu1);}public void saveChild2(){    Stu stu1 = new Stu();    stu1.setName("child-2");    stu1.setAge(11);    stuMapper.insert(stu1);}

After execution

No value inserted:



analysis

Both methods share the same transaction, so no matter which method throws an exception, it will be rolled back.


5. NOT_SUPPORTED

1) Introduction

If there is a transaction, suspend the transaction and run the database operation without using the transaction.

2) The outer layer has no transactions, the child method does not use transactions, and the parent method has no transactions

@Overridepublic void testPropagationTrans() {    stuService.saveParent();    stuService.saveChildren();}
@Overridepublic void saveParent() {    Stu stu = new Stu();    stu.setName("parent");    stu.setAge(19);    stuMapper.insert(stu);}@Transactional(propagation = Propagation.NOT_SUPPORTED)@Overridepublic void saveChildren() {    saveChild1();    int a = 1/0;    saveChild2();}public void saveChild1(){    Stu stu1 = new Stu();    stu1.setName("child-1");    stu1.setAge(11);    stuMapper.insert(stu1);}public void saveChild2(){    Stu stu1 = new Stu();    stu1.setName("child-2");    stu1.setAge(11);    stuMapper.insert(stu1);}

After execution




database



Although the console reported an exception, it did not roll back:



analysis

There are no transactions in the outer layer and transactions are not used in the sub methods, so exceptions are generated and no rollback is performed.


3) The outer layer has transactions, the child method does not use transactions, and the parent method has no transactions

@Transactional(propagation = Propagation.REQUIRED)@Overridepublic void testPropagationTrans() {    stuService.saveParent();    stuService.saveChildren();}

@Overridepublic void saveParent() {    Stu stu = new Stu();    stu.setName("parent");    stu.setAge(19);    stuMapper.insert(stu);}@Transactional(propagation = Propagation.NOT_SUPPORTED)@Overridepublic void saveChildren() {    saveChild1();    int a = 1/0;    saveChild2();}public void saveChild1(){    Stu stu1 = new Stu();    stu1.setName("child-1");    stu1.setAge(11);    stuMapper.insert(stu1);}public void saveChild2(){    Stu stu1 = new Stu();    stu1.setName("child-2");    stu1.setAge(11);    stuMapper.insert(stu1);}

After execution




database






analysis

An exception was thrown in the saveChildren method, so only saveChild1() was executed:

@Transactional(propagation = Propagation.NOT_SUPPORTED)@Overridepublic void saveChildren() {    saveChild1();    int a = 1/0;    saveChild2();}

Because the current method does not use exceptions, saveChild1 is not rolled back.
The caller has its own transaction:

@Transactional(propagation = Propagation.REQUIRED)@Overridepublic void testPropagationTrans() {    stuService.saveParent();    stuService.saveChildren();}

Therefore, the exception generated from saveChildren will also cause the saveParent method called here to be rolled back.


6. NEVER

1) Introduction

Throw an exception if a transaction currently exists.

2) The outer layer has a transaction REQUIRED, while the sub method has a NEVER transaction

@Transactional(propagation = Propagation.REQUIRED)@Overridepublic void testPropagationTrans() {    stuService.saveParent();    stuService.saveChildren();}

@Overridepublic void saveParent() {    Stu stu = new Stu();    stu.setName("parent");    stu.setAge(19);    stuMapper.insert(stu);}@Transactional(propagation = Propagation.NEVER)@Overridepublic void saveChildren() {    saveChild1();    int a = 1/0;    saveChild2();}public void saveChild1(){    Stu stu1 = new Stu();    stu1.setName("child-1");    stu1.setAge(11);    stuMapper.insert(stu1);}public void saveChild2(){    Stu stu1 = new Stu();    stu1.setName("child-2");    stu1.setAge(11);    stuMapper.insert(stu1);}

After execution

An exception indicating that a transaction already exists:



database

No data inserted:



3) There is no transaction in the outer layer, and the sub method has NEVER transaction

    @Override    public void testPropagationTrans() {        stuService.saveParent();        stuService.saveChildren();    }
@Overridepublic void saveParent() {    Stu stu = new Stu();    stu.setName("parent");    stu.setAge(19);    stuMapper.insert(stu);}@Transactional(propagation = Propagation.NEVER)@Overridepublic void saveChildren() {    saveChild1();    int a = 1/0;    saveChild2();}public void saveChild1(){    Stu stu1 = new Stu();    stu1.setName("child-1");    stu1.setAge(11);    stuMapper.insert(stu1);}public void saveChild2(){    Stu stu1 = new Stu();    stu1.setName("child-2");    stu1.setAge(11);    stuMapper.insert(stu1);}

After execution




database






analysis

Without a transaction, there is no corresponding rollback.


7. NESTED

1) Introduction

  1. If there is a transaction currently, open the sub transaction (nested transaction), and the nested transaction is committed or rolled back independently; If there is no current transaction, the same as REQUIRED.
  2. However, if the primary transaction is committed, it will be committed with the secondary transaction.
  3. If the primary transaction is rolled back, the child transactions are rolled back together. Conversely, if the child transaction is abnormal, the parent transaction can be rolled back or not.

2) There is an exception when the outer layer uses the REQUIRED transaction, and there is no exception when the sub method uses the needed transaction

@Transactional(propagation = Propagation.REQUIRED)@Overridepublic void testPropagationTrans() {    stuService.saveParent();    stuService.saveChildren();    int a = 1/0;}
@Overridepublic void saveParent() {    Stu stu = new Stu();    stu.setName("parent");    stu.setAge(19);    stuMapper.insert(stu);}@Transactional(propagation = Propagation.NESTED)@Overridepublic void saveChildren() {    saveChild1();    saveChild2();}public void saveChild1(){    Stu stu1 = new Stu();    stu1.setName("child-1");    stu1.setAge(11);    stuMapper.insert(stu1);}public void saveChild2(){    Stu stu1 = new Stu();    stu1.setName("child-2");    stu1.setAge(11);    stuMapper.insert(stu1);}

After execution




database

Nothing there?



analysis

If there is a parent-child nested transaction and an exception occurs in the calling method, the transaction in the nested transaction will also be rolled back.


3) There is no exception when the outer layer uses the REQUIRED transaction, and there is an exception when the sub method uses the NESTED transaction

@Transactional(propagation = Propagation.REQUIRED)@Overridepublic void testPropagationTrans() {    stuService.saveParent();    try{        // save point: after catching the exception of the current transaction, it will not affect other methods called at the same level. Other operations stuservice. Savechildren();} catch (Exception e){        e.printStackTrace();    }}
@Overridepublic void saveParent() {    Stu stu = new Stu();    stu.setName("parent");    stu.setAge(19);    stuMapper.insert(stu);}@Transactional(propagation = Propagation.NESTED)@Overridepublic void saveChildren() {    saveChild1();    int a = 1/0;    saveChild2();}public void saveChild1(){    Stu stu1 = new Stu();    stu1.setName("child-1");    stu1.setAge(11);    stuMapper.insert(stu1);}public void saveChild2(){    Stu stu1 = new Stu();    stu1.setName("child-2");    stu1.setAge(11);    stuMapper.insert(stu1);}

After execution




database




analysis

Using save point to isolate exceptions in the saveChildren method does not affect other methods in the current caller.


Tags: Java

Posted on Wed, 01 Dec 2021 00:19:27 -0500 by stu215