spring 事务的传播级别

测试环境:

数据库

StudnetMapper接口,TeacherMapper同理。

package com.cherish.transaction.mapper;

import com.cherish.transaction.entity.Student;
import org.apache.ibatis.annotations.Insert;

public interface StudentMapper {
    @Insert("insert into student(name,age) values(#{name},#{age})")
    int addStudent(Student student);
}

StudnetService接口,TeacherService同理。

package com.cherish.transaction.service;

import com.cherish.transaction.entity.Student;

public interface StudentService {
     int insertStudent(Student student);
}

StudentService 实现类,TeacherService同理

package com.cherish.transaction.service.impl;

import com.cherish.transaction.entity.Student;
import com.cherish.transaction.entity.Teacher;
import com.cherish.transaction.mapper.StudentMapper;
import com.cherish.transaction.mapper.TeacherMapper;
import com.cherish.transaction.service.StudentService;
import com.cherish.transaction.service.TeacherService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class StudentServiceImpl implements StudentService {

    @Autowired
    private StudentMapper studentMapper;

    @Override
    public int insertStudent(Student student) {
        System.out.println("TeacherServiceImpl insertTeacher 开始执行"+student);;
        int i = studentMapper.addStudent(student);
        System.out.println("TeacherServiceImpl insertTeacher 执行结束");
        return i;
    }
}

设计DoAllService调用者两个service

package com.cherish.transaction.service;

import com.cherish.transaction.entity.Student;
import com.cherish.transaction.entity.Teacher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class DoAllService {

    @Autowired
    private TeacherService teacherService;

    @Autowired
    private StudentService studentService;

    public void doAll(){
        Teacher teacher = new Teacher("王老师");
        int i = teacherService.insertTeacher(teacher);
        System.out.println("中间");
        Student student = new Student("小王");
        int i1 = studentService.insertStudent(student);
    }
}

1、在三个service上的三个方法打上如下注解

@Transactional(propagation= Propagation.REQUIRED)、

1、修改

启动了老师服务的事务,抛出异常后,回滚了老师插入的操作,但是学生service的插入学生操作未执行(因为中间未打印),抛出异常后,没有执行下去

反着修改,在学生中抛出异常,老师中正常插入操作(规避抛出异常不执行,因为先执行了正常的老师操作操作)

结果:

结果:学生和老师都没插入

所以:在平时使用中,某个controller中需要使用多个service做DML操作,可以实现一个中间service(例子中的doAll),在中间service中去注入A、B等等业务service(各个业务service也是存在自己的事物的),在这种场景下,就能将两个业务service控制在同一个service中

@Transactional(propagation= Propagation.REQUIRED)
public void doAll(){
    Teacher teacher = new Teacher("王老师");
    int i = teacherService.insertTeacher(teacher);
    System.out.println("中间");
    Student student = new Student("小王");
    int i1 = studentService.insertStudent(student);
}

2.修改studentService方法上的注解为

@Transactional(propagation= Propagation.REQUIRES_NEW)

再次测试的结果是

两个操作都未成功

原因:学生服务插入操作由于是

@Transactional(propagation= Propagation.REQUIRES_NEW)

Propagation.REQUIRES_NEW挂起原有事物,新建一个事务,这两个数据库连接也是不同的,此时学生服务操作失败是必然的,而老师服务也被回滚是因为,老师服务和doAll服务在同一个事务内,学生抛出的运行时异常在doAll中被发现,所以出发了该事务的回滚,所有老师操作被回滚,也失效。

3、在doAll中捕获学生服务的异常

结果老师插入成功了,学生操作还是失效

原因:在前面的例子中,只是将学生抛出的异常给捕获处理了,在doAll和老师服务这个事务中,并未发现异常,所有操作都是正常的,这样就不会出现回滚,而学生服务依然是在自己新建的事务中回滚。

 

4、将学生服务修改为

@Transactional(propagation= Propagation.NESTED)

因为学生服务是作为doAll服务的子事务(嵌套事务),在学生服务中回滚是回滚到还原点处,也就是插入老师后,而插入老师没有抛出异常,所以是正常执行,插入成功。而插入学生依然是失败,其中这里在doAll方法中依然是需要try catch学生服务的。

对比 

@Transactional(propagation= Propagation.REQUIRED_NEW)和@Transactional(propagation= Propagation.NESTED)

一个是新的独立事务,一个是嵌套的子事务,功能类似

下面为网络上复制的一段,仅供参考 

在 SPRING 中一共定义了七种事务传播属性

  1. PROPAGATION_REQUIRED -- 支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。

  2. PROPAGATION_SUPPORTS -- 支持当前事务,如果当前没有事务,就以非事务方式执行。

  3. PROPAGATION_MANDATORY -- 支持当前事务,如果当前没有事务,就抛出异常。

  4. PROPAGATION_REQUIRES_NEW -- 新建事务,如果当前存在事务,把当前事务挂起。

  5. PROPAGATION_NOT_SUPPORTED -- 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。

  6. PROPAGATION_NEVER -- 以非事务方式执行,如果当前存在事务,则抛出异常。

  7. PROPAGATION_NESTED -- 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。

下面为,一些源码上的debug

进入doLogin方法,实现类DataSourceTransactionManager中的doLogin方法如下

 

 

以上的截图没有其他目的,就是给自己以后复习提个醒,这些类、方法、位置需要留意。同时发现打上事务注解后,代理对象带上了CGLIB的标识,应该就是通过CGLIB方式生成代理对象,对事务切面处理。

下面是一个特殊例子:

package com.cherish.transaction.service.impl;

import com.cherish.transaction.entity.Student;
import com.cherish.transaction.entity.Teacher;
import com.cherish.transaction.mapper.StudentMapper;
import com.cherish.transaction.mapper.TeacherMapper;
import com.cherish.transaction.service.StudentService;
import com.cherish.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;

@Service
public class StudentServiceImpl implements StudentService {


    @Autowired
    private StudentMapper studentMapper;
    @Autowired


    @Transactional(propagation= Propagation.REQUIRED)
    @Override
    public int insertStudent(Student student) {
        System.out.println(this);
        int i1 = this.addStudent(student);
        System.out.println("TeacherServiceImpl insertTeacher 开始执行"+student);;
        int i = studentMapper.addStudent(student);
        System.out.println("TeacherServiceImpl insertTeacher 执行结束");
        int exception = 1/0;
        return i;
    }


    @Transactional(propagation= Propagation.REQUIRES_NEW)
    public int addStudent(Student student){
        System.out.println("TeacherServiceImpl addStudent 开始执行"+student);;
        int i = studentMapper.addStudent(student);
        System.out.println("TeacherServiceImpl addStudent 执行结束");
        return i;
    }
}
package com.cherish.transaction;

import com.cherish.transaction.entity.Student;
import com.cherish.transaction.entity.Teacher;
import com.cherish.transaction.service.DoAllService;
import com.cherish.transaction.service.StudentService;
import com.cherish.transaction.service.TeacherService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class TestTransaction02 {


    @Autowired
    private StudentService studentService;


    @Test
    public void test01(){
        Student student = new Student("lys");
        studentService.insertStudent(student);
    }

}

结果两个数据插入没成功

this对象输出为

原因:

@Transactional(propagation= Propagation.REQUIRES_NEW)
public int addStudent(Student student){

未生效,根据前面分析,如果上面Propagation.REQUIRES_NEW生效了,则是会出现,insert生效,add不生效,数据库插入一条lys的数据。

修改方法


    @Override
    @Transactional(propagation= Propagation.REQUIRED)
    public int insertStudent(Student student) {
        System.out.println("this "+this);
        StudentService studentService = applicationContext.getBean(StudentService.class);
        System.out.println("studentService "+studentService);
        int i1 = studentService.addStudent(student);
        System.out.println("TeacherServiceImpl insertTeacher 开始执行"+student);;
        int i = studentMapper.addStudent(student);
        System.out.println("TeacherServiceImpl insertTeacher 执行结束");
        int exception = 1/0;
        return i;
    }

结果:插入一条lys数据成功

输出结果为

方法二:

获取当前代理对象

报错:

解决:

@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)

运行结果:

添加

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

执行结果:

插入数据成功,因为

@Transactional(propagation= Propagation.REQUIRES_NEW)
public int addStudent(Student student){

是自己的新事物,不会因为insertStudent方法中抛出异常给被动回滚掉

反证:注释该注解

//    @Transactional(propagation= Propagation.REQUIRES_NEW)
    public int addStudent(Student student){

执行结果:

两条记录都为生效,

public int addStudent(Student student){方法调用和将方法内的代码写入addStudent方法中无异,所以必然是同一个事务内,这样在出现除0异常后,也就一起回滚了,最总两个操作都失效。

总结两种方法

 


    @Override
    @Transactional(propagation= Propagation.REQUIRED)
    public int insertStudent(Student student) {
        System.out.println("this "+this);
        // 失效
        //        int i1 = this.addStudent(student);
        //        System.out.println("this "+this);
        // 失效
//        解决方法一  注:applicationContext是注入的容器上下文对象
//        StudentService studentService = applicationContext.getBean(StudentService.class);
//        System.out.println("studentService "+studentService);
//        解决方法二 
//        注: 1、需要在spirng boot启动类上需要配置上述的EnableAspectJAutoProxy  2、需要导入spring-boot-starter-aop 包
        StudentService studentService = (StudentService)AopContext.currentProxy();
        int i1 = studentService.addStudent(student);

        System.out.println("中间");
        System.out.println("TeacherServiceImpl insertTeacher 开始执行"+student);;
        int i = studentMapper.addStudent(student);
        System.out.println("TeacherServiceImpl insertTeacher 执行结束");
        int exception = 1/0;
        return i;
    }

以上是自己对spring事务的进一步学习的记录,给自己做个笔记,同时些许能帮助部分同学理解Spring事务

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值