实战Spring中的八大事务

一、测试前的准备

数据表结构:
实战Spring中的八大事务

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();
    }
}


二、不使用事务

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();
        // 在两次添加数据的操作之间加入一个绝对会报异常的代码
        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();

        // 新增
        stuService.saveChildren();
    }
}


3. 操作执行后的数据库

1) 报异常

实战Spring中的八大事务


2)数据库(前后)

实战Spring中的八大事务
实战Spring中的八大事务

可以看到saveChildren只保存了一数据,child-2并没有插入进去
实战Spring中的八大事务
在发生异常后,child-2是无法保存到数据库的,也没有进行回滚​


三、事务

1. REQUIRED

1)简介

  1. 使用当前的事务,如果当前没有事务,则自己新建一个事务,子方法是必须运行在一个事务中的;
  2. 如果当前存在事务,则加入这个事务,成为一个整体。

2)在父方法中添加

在TestTransServiceImpl中开启事务

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

        stuService.saveChildren();
    }

执行后

实战Spring中的八大事务

可以看到,此处数据库中并没有插入数据
因为在此处进行了事务传播REQUIRED,虽然saveParent和saveChild方法都没有事务,但是它们也传播到了REQUIRED事务
所以当父方法使用REQUIRED事务时,子方法发生异常后,两个子方法中的事务都会进行回滚


3)在单个子方法中添加

清除父方法上的事务
//    @Transactional(propagation = Propagation.REQUIRED)    @Override    public void testPropagationTrans() {        stuService.saveParent();        stuService.saveChildren();    }

在saveChild方法上添加一个事务
    @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);}

执行后

实战Spring中的八大事务

实战Spring中的八大事务


可以看到saveParent方法执行的数据添加到了数据库,而saveChild没有。
也就是说,当前的整个方法方法体中不带有事务时,他回去重新创建一个事务
实战Spring中的八大事务

也就是说saveChildren方法必须要存在于某个事务中去运行,所以它发生异常后进行了回滚,而saveParent没有事务。


2. SUPPORTS

1)简介

如果当前有事务,则使用事务;如果当前没有事务,则不使用事务。

2)外层没有事务,而子方法有

@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);}

执行后

实战Spring中的八大事务
实战Spring中的八大事务


3)外层使用事务,而内层其中一个子方法也是用事务

@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);}

执行后

实战Spring中的八大事务

前后都为空


3. MANDATORY

1)简介

该传播属性强制必须存在一个事务,如果不存在,则抛出异常。

2)外层无事务,其中一个子方法有事务,而另一个方法没有事务

    @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);}

执行后

控制台报错

实战Spring中的八大事务


数据库

实战Spring中的八大事务

实战Spring中的八大事务


3)外层有事务,子方法一个方法有事务,另一个没有

    @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);}

执行后

实战Spring中的八大事务


数据库

实战Spring中的八大事务

实战Spring中的八大事务


4. REQUIRES_NEW

1)简介

  1. 如果当前有事务,则挂起该事务,并且自己创建一个新的事务给自己使用;
  2. 如果当前没有事务,则同 REQUIRED。

2)外层无事务,子方法有事务,而父方法没有事务

    @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);}

执行后

实战Spring中的八大事务


数据库

实战Spring中的八大事务

实战Spring中的八大事务


分析

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

saveParent没有事务,而saveChildren有事务,所以会将后者和前者操作区分开来,后者发生错误会进行回滚,但是前者会正常存储到数据库中。


3)外层有事务,子方法有事务,父方法没有事务

    @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);}

执行后

实战Spring中的八大事务


数据库

实战Spring中的八大事务

前后都为空。


分析

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

saveChildren方法执行后会产生1个byzero的异常,而这个异常会传递给其调用方法:

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

因为当前方法有事务REQUIRED,变成了一个整体,所以就会影响当前方法中一起被调用的saveParent,所以也导致saveParent也出现了回滚。


4)外层使用事务也产生异常,子方法有事务,父方法没有事务

@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);}

执行后

实战Spring中的八大事务


数据库

实战Spring中的八大事务

多了两条数据:
实战Spring中的八大事务


分析

因为子方法saveChildren中使用了新的事务,所以当前事务是被挂起的,所以当前方法的事务是不会影响到其子方法的:

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

但是saveParent中没有使用事务,所以会被回滚,所以数据库中会保存saveChildren中插入的值:
实战Spring中的八大事务


5)外层有REQUIRED事务,子方法有REQUIRED事务,父方法没有事务

子方法和外层都是用REQUIRED事务,也就是两个方法都使用同一个事务:

@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);}

执行后

没有插入值:
实战Spring中的八大事务


分析

两个方法都共用了同一个事务,所以不管在哪个方法中抛出异常,都会进行回滚。


5. NOT_SUPPORTED

1)简介

如果当前有事务,则把事务挂起,自己不使用事务去运行数据库操作。

2)外层无事务,子方法不使用事务,父方法没有事务

@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);}

执行后

实战Spring中的八大事务


数据库

实战Spring中的八大事务

虽然控制台有报异常,但是并没有进行回滚:
实战Spring中的八大事务


分析

外层没有事务,且子方法中也不使用事务,因此产生了异常也没有进行回滚。


3)外层有事务,子方法不使用事务,父方法没有事务

@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);}

执行后

实战Spring中的八大事务


数据库

实战Spring中的八大事务

实战Spring中的八大事务


分析

saveChildren方法中抛出了一个异常,所以只执行到了saveChild1():

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

而因为当前方法是不使用异常的,所以saveChild1是没有发生回滚的。
而调用方是本身有事务的:

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

所以从saveChildren中产生的异常,也会让此处同时被调用的saveParent方法被回滚。


6. NEVER

1)简介

如果当前有事务存在,则抛出异常。

2)外层有事务REQUIRED,而子方法有NEVER事务

@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);}

执行后

提示已经有事务存在的异常:
实战Spring中的八大事务


数据库

没有任何数据插入:
实战Spring中的八大事务


3)外层没有事务,子方法有NEVER事务

    @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);}

执行后

实战Spring中的八大事务


数据库

实战Spring中的八大事务

实战Spring中的八大事务


分析

没有事务,就没有相应的回滚。


7. NESTED

1)简介

  1. 如果当前有事务,则开启子事务(嵌套事务),嵌套事务是独立提交或者回滚;如果当前没有事务,则同 REQUIRED。
  2. 但是如果主事务提交,则会携带子事务一起提交。
  3. 如果主事务回滚,则子事务会一起回滚。相反,子事务异常,则父事务可以回滚或不回滚。

2)外层使用REQUIRED事务有异常,子方法使用NESTED事务无异常

@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);}

执行后

实战Spring中的八大事务


数据库

什么都没有:
实战Spring中的八大事务


分析

有父子嵌套事务存在话,在调用方法中发生的异常,那么在嵌套事务里面的事务也会被回滚。


3)外层使用REQUIRED事务无异常,子方法使用NESTED事务有异常

@Transactional(propagation = Propagation.REQUIRED)@Overridepublic void testPropagationTrans() {    stuService.saveParent();    try{        // save point 保存点: 对当前事务的异常进行catch之后,就不会影响同层次调用的其他方法 其他操作        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);}

执行后

实战Spring中的八大事务


数据库

实战Spring中的八大事务


分析

使用save point 将saveChildren方法中的异常隔离开,不会影响到当前调用方中其他的方法。


上一篇:C语言中结构体类型知识点


下一篇:字典