Spring事务传播

1.事务传播特性

目录

1.事务传播特性

2.实测

2.1 建表

2.2 创建 项目

2.3测试结论

3.REQUIRED,REQUIRES_NEW,NESTED异同


2.实测

教员说过,实践是检验真理的唯一标准。

2.1 建表

mysql数据库

CREATE TABLE `student` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(45) NOT NULL DEFAULT '',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

CREATE TABLE `teacher` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(45) NOT NULL DEFAULT '',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

2.2 创建 项目

TestService 调用 StudentService 和 TeacherService 中的代码。 验证事务的传播特性。
在单元测试中,调用 TestService 中的方法。

关键代码:

package com.dawei.transaction.service;

import com.dawei.transaction.mapper.StudentDao;
import com.dawei.transaction.pojo.Student;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

/**
 * @author da wei
 * @description
 * @create 2021/7/15 11:07
 */
@Service
public class StudentServiceImpl implements StudentService {
    @Autowired
    StudentDao studentDao;

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void addRequired(Student student) {
        studentDao.insert(student);
    }


    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void addRequiresNew(Student student) {
        studentDao.insert(student);
    }

    @Override
    @Transactional(propagation = Propagation.NESTED)
    public void addNested(Student student) {
        studentDao.insert(student);
    }

}
package com.dawei.transaction.service;

import com.dawei.transaction.mapper.TeacherDao;
import com.dawei.transaction.pojo.Teacher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

/**
 * @author da wei
 */
@Service
public class TeacherServiceImpl implements TeacherService {
    @Autowired
    TeacherDao teacherDao;

    /**
     * 虽然 @Transactional 注解可以作用于接口、接口方法、类以及类方法上,
     * 但是 Spring 建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。
     * 代理模式有JDK代理 和 cglib代理
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void addRequired(Teacher teacher) {
        teacherDao.insert(teacher);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void addRequiredException(Teacher teacher) {
        teacherDao.insert(teacher);
        //此处会抛出一个运行时异常
        int i = 1 / 0;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void addRequiresNew(Teacher teacher) {
        teacherDao.insert(teacher);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void addRequiresNewException(Teacher teacher) {
        teacherDao.insert(teacher);
        //此处会抛出一个运行时异常
        int i = 1 / 0;
    }

    @Override
    @Transactional(propagation = Propagation.NESTED)
    public void addNested(Teacher teacher) {
        teacherDao.insert(teacher);
    }

    @Override
    @Transactional(propagation = Propagation.NESTED)
    public void addNestedException(Teacher teacher) {
        teacherDao.insert(teacher);
        int i = 1 / 0;
    }
}

2.3测试结论

请查看注释。

package com.dawei.transaction.test;

import com.dawei.transaction.pojo.Student;
import com.dawei.transaction.pojo.Teacher;
import com.dawei.transaction.service.StudentService;
import com.dawei.transaction.service.TeacherService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

/**
 * @author da wei
 */
@Service
public class TestServiceImpl implements TestService {

    @Autowired
    StudentService studentService;
    @Autowired
    TeacherService teacherService;

    /*-----------------------------1.  测试   propagation = Propagation.REQUIRED---------------------------------------------------------------------------------*/

    /**
     * 1.1  外围没有开启事务
     */

    //1.1.1  在单元测试中调用该方法。 测试结果: 外部未开启事务,两个添加方法在自己的事务中独立
//运行。外部的异常不影响内部两个方法的插入。
    @Override
    public void noTransactionExceptionRequiredRequired() {
        Student student = new Student();
        student.setName("学生1.1.1");
        studentService.addRequired(student);

        Teacher teacher = new Teacher();
        teacher.setName("教师1.1.1");
        teacherService.addRequired(teacher);
        int i = 1 / 0;
    }

    //1.1.2  在单元测试中调用该方法。 测试结果:学生2 成功插入到数据库。  外围方法没有事务,两个
//插入的方法独自创建自己的事务,教师添加遇到异常 导致 回滚,学生添加成功。
    @Override
    public void noTransactionRequiredRequiredException() {
        Student student = new Student();
        student.setName("学生1.1.2");
        studentService.addRequired(student);

        Teacher teacher = new Teacher();
        teacher.setName("教师1.1.2");
        teacherService.addRequiredException(teacher);
    }
    //结论:通过上面两个方法我们证明了在外围方法未开启事务的情况下Propagation.REQUIRED修饰的内
//部方法会新开启自己的事务,且开启的事务相互独立,互不干扰。

    /**
     * 1.2 外围方法开启事务,这个是使用率比较高的场景。
     */

    //1.2.1 在单元测试中调用该方法。  测试结果: 学生和教师均保存失败。 外围方法开启事务,内部方
//法加入外围方法事务,外围方法回滚,内部方法也要回滚。
    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void transactionExceptionRequiredRequired() {
        Student student = new Student();
        student.setName("学生1.2.1");
        studentService.addRequired(student);

        Teacher teacher = new Teacher();
        teacher.setName("教师1.2.1");
        teacherService.addRequired(teacher);
        int i = 1 / 0;

    }

    //1.2.2 测试结果:全部插入失败。  外围方法开启事务,内部方法加入外围方法事务,内部方法抛出异
//常回滚,外围方法感知异常致使整体事务回滚。
    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void transactionRequiredRequiredException() {
        Student student = new Student();
        student.setName("学生1.2.2");
        studentService.addRequired(student);

        Teacher teacher = new Teacher();
        teacher.setName("教师1.2.2");
        teacherService.addRequiredException(teacher);

    }

    //1.2.3  全部保存失败。 外围方法开启事务,内部方法加入外围方法事务,内部方法抛出异常回滚,即
//使方法被catch不被外围方法感知,整个事务依然回滚。
    //这里我特地测试了1.2.1中的方法,捕获 1/0 异常, 发现两个均能插入成功。  与上面对比,即 捕获
//普通方法 是不会使事务中止的。
    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void transactionRequiredRequiredExceptionTry() {
        Student student = new Student();
        student.setName("学生1.2.3");
        studentService.addRequired(student);

        Teacher teacher = new Teacher();
        teacher.setName("教师1.2.3");
        try {
            teacherService.addRequiredException(teacher);
        } catch (Exception e) {
            System.out.println("出现异常");
        }
    }
    //结论:以上试验结果我们证明在外围方法开启事务的情况下Propagation.REQUIRED修饰的内部方法会
//加入到外围方法的事务中,所有Propagation.REQUIRED修饰的内部方法和外围方法均属于同一事务,只要
//一个方法回滚,整个事务均回滚。

    /*-----------------------------2.  测试   propagation = Propagation.REQUIRES_NEW--------------------------------------------------------------------------------*/

    /**
     * 2.1 外围方法没有开启事务。
     */

    //2.1.1 测试结果 都保存成功。 外围方法没有事务,插入学生、教师方法都在自己的事务中独立运行,
//外围方法抛出异常回滚不会影响内部方法。
    @Override
    public void noTransactionExceptionRequiresNewRequiresNew() {
        Student student = new Student();
        student.setName("学生2.1.1");
        studentService.addRequiresNew(student);

        Teacher teacher = new Teacher();
        teacher.setName("教师2.1.1");
        teacherService.addRequiresNew(teacher);
        int i = 1 / 0;
    }

    //2.1.2 仅有学生保存成功。外围方法没有开启事务,插入学生方法和插入教师方法分别开启自己的事
//务,插入教师方法抛出异常回滚,其他事务不受影响。
    @Override
    public void noTransactionRequiresNewRequiresNewException() {
        Student student = new Student();
        student.setName("学生2.1.2");
        studentService.addRequiresNew(student);

        Teacher teacher = new Teacher();
        teacher.setName("教师2.1.2");
        teacherService.addRequiresNewException(teacher);
    }
    //结论:通过这两个方法我们证明了在外围方法未开启事务的情况下Propagation.REQUIRES_NEW修饰的
//内部方法会新开启自己的事务,且开启的事务相互独立,互不干扰。

    /**
     * 2.2 外围方法开启事务。
     */

    //2.2.1 学生插入失败,教师都插入成功。 外围方法开启事务,插入 学生2.2.1 方法和外围方法一个事
//务,插入 教师2.2.1-1方法、插入教师2.2.1-2方法分别在独立的新建事务中,外围方法抛出异常只回滚和
//外围方法同一事务的方法,故插入学生2.2.1的方法回滚。
    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void transactionExceptionRequiredRequiresNewRequiresNew() {
        Student student = new Student();
        student.setName("学生2.2.1");
        studentService.addRequired(student);

        Teacher teacher1 = new Teacher();
        teacher1.setName("教师2.2.1-1");
        teacherService.addRequiresNew(teacher1);

        Teacher teacher2 = new Teacher();
        teacher2.setName("教师2.2.1-2");
        teacherService.addRequiresNew(teacher2);
        int i = 1 / 0;
    }

    //2.2.2 仅有 教师2.2.2-1 插入成功。外围方法开启事务,插入“学生2.2.2“方法和外围方法一个事
//务,插入“教师2.2.2-1”方法、插入“教师2.2.2-2”方法分别在独立的新建事务中。插入“教师2.2.2-2”方法
//抛出异常,首先插入 “教师2.2.2-2”方法的事务被回滚,异常继续抛出被外围方法感知,外围方法事务亦被
//回滚,故插入“学生2.2.2”方法也被回滚。
    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void transactionRequiredRequiresNewRequiresNewException() {
        Student student = new Student();
        student.setName("学生2.2.2");
        studentService.addRequired(student);

        Teacher teacher1 = new Teacher();
        teacher1.setName("教师2.2.2-1");
        teacherService.addRequiresNew(teacher1);

        Teacher teacher2 = new Teacher();
        teacher2.setName("教师2.2.2-2");
        teacherService.addRequiresNewException(teacher2);
    }

    //2.2.3 学生2.2.3 和 教师2.2.3-1 插入成功。
    //外围方法开启事务,插入“学生2.2.3”方法和外围方法一个事务,插入“教师2.2.3-1”方法、插入“教师
//2.2.3-2”方法分别在独立的新建事务中。插入“教师2.2.3-2”方法抛出异常,首先插入“教师2.2.3-2”方法
//的事务被回滚,异常被catch不会被外围方法感知,外围方法事务不回滚,故插入“学生2.2.3”方法插入成功。
    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void transactionRequiredRequiresNewRequiresNewExceptionTry() {
        Student student = new Student();
        student.setName("学生2.2.3");
        studentService.addRequired(student);

        Teacher teacher1 = new Teacher();
        teacher1.setName("教师2.2.3-1");
        teacherService.addRequiresNew(teacher1);

        Teacher teacher2 = new Teacher();
        teacher2.setName("教师2.2.3-2");
        try {
            teacherService.addRequiresNewException(teacher2);
        } catch (Exception e) {
            System.out.println("异常");
        }
    }
    //结论:在外围方法开启事务的情况下Propagation.REQUIRES_NEW修饰的内部方法依然会单独开启独立
//事务,且与外部方法事务也独立,内部方法之间、内部方法和外部方法事务均相互独立,互不干扰。

    /*-----------------------------3.  测试   propagation = Propagation.NESTED--------------------------------------------------------------------------------*/

    /**
     * 3.1 外围方法没有开启事务。
     */

    //3.1.1 两个都插入成功。 外围方法未开启事务,插入“学生3.1.1”、“教师3.1.1”方法在自己的事务中
//独立运行,外围方法异常不影响内部插入“学生3.1.1”、“教师3.1.1”方法独立的事务。
    @Override
    public void noTransactionExceptionNestedNested() {
        Student student = new Student();
        student.setName("学生3.1.1");
        studentService.addNested(student);

        Teacher teacher1 = new Teacher();
        teacher1.setName("教师3.1.1");
        teacherService.addNested(teacher1);
        int i = 1 / 0;
    }

    //3.1.2  仅学生3.1.2插入成功。 外围方法没有事务,插入“学生3.1.2”、“教师3.1.2”方法都在自己
//的事务中独立运行,所以插入“教师3.1.2”方法抛出异常只会回滚插入“教师3.1.2”方法,插入“学生3.1.2”
//方法不受影响。
    @Override
    public void noTransactionNestedNestedException() {
        Student student = new Student();
        student.setName("学生3.1.2");
        studentService.addNested(student);

        Teacher teacher1 = new Teacher();
        teacher1.setName("教师3.1.2");
        teacherService.addNestedException(teacher1);
    }
    //结论:通过这两个方法我们证明了在外围方法未开启事务的情况下Propagation.NESTED和
//Propagation.REQUIRED作用相同,修饰的内部方法都会新开启自己的事务,且开启的事务相互独立,互不干扰。

    /**
     * 3.2 外围方法开启事务
     */
    //3.2.1 都插入失败。 外围方法开启事务,内部事务为外围事务的子事务,外围方法回滚,内部方法也
//要回滚。
    @Override
    @Transactional
    public void transactionExceptionNestedNested() {
        Student student = new Student();
        student.setName("学生3.2.1");
        studentService.addNested(student);

        Teacher teacher1 = new Teacher();
        teacher1.setName("教师3.2.1");
        teacherService.addNested(teacher1);
        int i = 1 / 0;
    }

    //3.2.2 都插入失败。外围方法开启事务,内部事务为外围事务的子事务,内部方法抛出异常回滚,且外
//围方法感知异常致使整体事务回滚。
    @Override
    @Transactional
    public void transactionNestedNestedException() {
        Student student = new Student();
        student.setName("学生3.2.2");
        studentService.addNested(student);

        Teacher teacher1 = new Teacher();
        teacher1.setName("教师3.2.2");
        teacherService.addNestedException(teacher1);
    }

    //3.2.3 仅学生3.2.3 插入成功。 外围方法开启事务,内部事务为外围事务的子事务,插入“教师
//3.2.3”内部方法抛出异常,可以单独对子事务回滚。
    @Override
    @Transactional
    public void transactionNestedNestedExceptionTry() {
        Student student = new Student();
        student.setName("学生3.2.3");
        studentService.addNested(student);

        Teacher teacher1 = new Teacher();
        teacher1.setName("教师3.2.3");
        try {
            teacherService.addNestedException(teacher1);
        } catch (Exception e) {
            System.out.println("exception");
        }
        //结论:以上试验结果我们证明在外围方法开启事务的情况下Propagation.NESTED修饰的内部方法
//属于外部事务的子事务,外围主事务回滚,子事务一定回滚,而内部子事务可以单独回滚而不影响外围主事
//务和其他子事务
    }



}

3.REQUIRED,REQUIRES_NEW,NESTED异同

由“1.2 ”和“3.2 ”对比,我们可知:NESTED和REQUIRED修饰的内部方法都属于外围方法事务,如果外围方法抛出异常,这两种方法的事务都会被回滚。但是REQUIRED是加入外围方法事务,所以和外围事务同属于一个事务,一旦REQUIRED事务抛出异常被回滚,外围方法事务也将被回滚。而NESTED是外围方法的子事务,有单独的保存点,所以NESTED方法抛出异常被回滚,不会影响到外围方法的事务。

由上面测试 “2.2 ”和“3.2 ”对比,我们可知:NESTED和REQUIRES_NEW都可以做到内部方法事务回滚而不影响外围方法事务。但是因为NESTED是嵌套事务,所以外围方法回滚之后,作为外围方法事务的子事务也会被回滚。而REQUIRES_NEW是通过开启新的事务实现的,内部事务和外围事务是两个事务,外围事务回滚不会影响内部事务。

完整源码地址:https://github.com/crossyourheart/TestTransactionPropagation.git
参考:https://blog.csdn.net/yuan520588/article/details/88919659

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring事务传播是指在多个具有事务控制的service相互调用时所形成的复杂事务边界控制。Spring提供了七种事务传播行为,分别是: 1. REQUIRED(默认):表示当前方法必须在一个具有事务的上下文中运行,如果客户端有事务在进行,那么被调用端将在该事务中运行,否则重新开启一个事务。如果被调用端发生异常,调用端和被调用端事务都将回滚。 2. SUPPORTS:表示当前方法可以在一个具有事务的上下文中运行,也可以在没有事务的上下文中运行。 3. MANDATORY:表示当前方法必须在一个具有事务的上下文中运行,如果没有事务则抛出异常。 4. REQUIRES_NEW:表示当前方法必须运行在它自己的事务中。一个新的事务将启动,如果有现有的事务在运行,则该方法被挂起,直到新的事务提交或回滚才恢复执行。 5. NOT_SUPPORTED:表示当前方法不应该运行在一个事务中,如果有事务在运行,则将该事务挂起,直到方法执行完毕。 6. NEVER:表示当前方法不应该运行在一个事务中,如果有事务在运行,则抛出异常。 7. NESTED:表示如果当前方法正有一个事务在运行中,则该方法应该运行在一个嵌套事务中。被嵌套的事务可以独立于封装事务进行提交或回滚。如果封装事务存在,并且外层事务回滚,那么内层事务必须回滚,反之,内层事务不影响外层事务。如果封装事务不存在,则与REQUIRED相同。 这些事务传播行为可以根据具体的业务需求来选择,以实现正确的事务控制。<span class="em">1</span><span class="em">2</span><span class="em">3</span><span class="em">4</span>

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值