2.20 详解事务的传播-1 - 图1
进入 Transactional 注解,默认使用的属性是 REQUIRED,表示当前方法是一定要使用事务,如果当前事务不存在,则创建新的事务,如果当前方法本身有事务,则不创建新的事务。
Propagation 一共有七个属性
image.png
下面通过示例详细介绍七个属性的作用
foodie-dev-service 中 StuService 接口添加两个方法

  1. package com.imooc.service;
  2. import com.imooc.pojo.Stu;
  3. /**
  4. * @author 92578
  5. * @since 1.0
  6. */
  7. public interface StuService {
  8. public Stu getStuInfo(int id);
  9. public void saveStu();
  10. public void updateStu(int id);
  11. public void deleteStu(int id);
  12. public void saveParent();
  13. public void saveChildren();
  14. }

foodie-dev-service 中 StuServiceImpl 实现对应的方法

  1. package com.imooc.service.impl;
  2. import com.imooc.mapper.StuMapper;
  3. import com.imooc.pojo.Stu;
  4. import com.imooc.service.StuService;
  5. import org.springframework.beans.factory.annotation.Autowired;
  6. import org.springframework.stereotype.Service;
  7. import org.springframework.transaction.annotation.Propagation;
  8. import org.springframework.transaction.annotation.Transactional;
  9. /**
  10. * @author 92578
  11. * @since 1.0
  12. */
  13. @Service
  14. public class StuServiceImpl implements StuService {
  15. @Autowired
  16. private StuMapper stuMapper;
  17. @Transactional(propagation = Propagation.SUPPORTS)
  18. @Override
  19. public Stu getStuInfo(int id) {
  20. return stuMapper.selectByPrimaryKey(id);
  21. }
  22. @Transactional(propagation = Propagation.REQUIRED)
  23. @Override
  24. public void saveStu() {
  25. Stu stu = new Stu();
  26. stu.setName("jack");
  27. stu.setAge(19);
  28. stuMapper.insert(stu);
  29. }
  30. @Transactional(propagation = Propagation.REQUIRED)
  31. @Override
  32. public void updateStu(int id) {
  33. Stu stu = new Stu();
  34. stu.setId(id);
  35. stu.setName("lucy");
  36. stu.setAge(20);
  37. stuMapper.updateByPrimaryKey(stu);
  38. }
  39. @Transactional(propagation = Propagation.REQUIRED)
  40. @Override
  41. public void deleteStu(int id) {
  42. stuMapper.deleteByPrimaryKey(id);
  43. }
  44. // 测试事务 required 属性
  45. @Override
  46. public void saveParent() {
  47. Stu stu = new Stu();
  48. stu.setName("parent");
  49. stu.setAge(19);
  50. stuMapper.insert(stu);
  51. }
  52. @Override
  53. public void saveChildren() {
  54. saveChild1();
  55. int a = 1 / 0;
  56. saveChild2();
  57. }
  58. public void saveChild1() {
  59. Stu stu = new Stu();
  60. stu.setName("child-1");
  61. stu.setAge(11);
  62. stuMapper.insert(stu);
  63. }
  64. public void saveChild2() {
  65. Stu stu = new Stu();
  66. stu.setName("child-2");
  67. stu.setAge(22);
  68. stuMapper.insert(stu);
  69. }
  70. }

foodie-dev-service 中 创建 TestTransService 接口

package com.imooc.service;

public interface TestTransService {
    public void testPropagationTrans();
}

foodie-dev-service 中 创建 TestTransServiceImpl 实现类

package com.imooc.service.impl;

import com.imooc.service.StuService;
import com.imooc.service.TestTransService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * Created by 92578 on 2020/8/15 0:49
 **/
@Service
public class TestTransServiceImpl implements TestTransService {

    @Autowired
    private StuService stuService;

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

在 foodie-dev-api 的 pom 中添加 JUnit 包

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

在 foodie-dev-api 的 src/test/java/com/test 包下创建 TransTest 类

package com.test;

import com.imooc.Application;
import com.imooc.service.StuService;
import com.imooc.service.TestTransService;
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;

/**
 * Created by 92578 on 2020/8/15 1:10
 **/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class TransTest {

    @Autowired
    public StuService stuService;

    @Autowired
    private TestTransService testTransService;

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

确保 Stu 表中没有数据
image.png
运行测试后提示出错
image.png
数据库中插入了两条记录
image.png

2.20.1 Propagation.REQUIRED

在 TestTransServiceImpl 类中的 testPropagationTrans 方法启用事务

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

清除原有数据,重新运行测试方法,发现表中没有再插入数据
image.png
通过上面示例可以发现,事务会进行传播,我们在测试的父方法上面开启事务,调用的子方法 saveParent 和 saveChildren 虽然没有添加事务,但是事务会传递到子方法中。
在 StuServiceImpl 类中的 saveChildren 方法上添加事务

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

在 TestTransServiceImpl 类中的 testPropagationTrans 方法关闭事务

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

重新运行测试方法,发现数据库中插入一条记录
image.png
由于父级方法并没有事务,因此 saveParent 方法执行后会保存到数据库中,saveChildren 方法开启了事务,因此进行回滚
【总结】
Propagation.REQUIRED:使用当前的事务,如果当前没有事务,则自己新建一个事务,子方法是必须运行在一个事务中的;如果当前存在事务,则加入这个事务,成为一个整体。(适用于增加、修改、删除)
举例:领导没饭吃,我有钱,我会自己买了自己吃;领导有的吃,会分给你一起吃。

2.20.2 Propagation.SUPPORTS

在 StuServiceImpl 类中的 saveChildren 方法上添加事务

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

清空数据,执行测试方法,发现数据库中插入两条记录
image.png
在 TestTransServiceImpl 类中的 testPropagationTrans 方法启用事务

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

清空数据,执行测试方法,发现数据库中没有记录
image.png

【总结】
Propagation.SUPPORTS:如果当前有事务,则使用事务;如果当前没有事务,则不使用事务。(适用于查询)
举例:领导没饭吃,我也没饭吃;领导有饭吃,我也有饭吃。

2.20.3 Propagation.MANDATORY

image.png
在 TestTransServiceImpl 类中的 testPropagationTrans 方法关闭事务

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

在 StuServiceImpl 类中的 saveChildren 方法上添加事务

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

执行测试方法,提示出错,没有找到一个存在的事务
image.png
在 TestTransServiceImpl 类中的 testPropagationTrans 方法启用事务

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

执行测试方法,提示出错
image.png
【总结】
Propagation.MANDATORY:该传播属性强制必须存在一个事务,如果不存在,则抛出异常。
举例:领导必须管饭,不管饭没饭吃,我就不乐意了,就不干了(抛出异常)