likes
comments
collection
share

为什么使用@Transactional时灵时不灵?

作者站长头像
站长
· 阅读数 15

同步线程操作

同步示例1

当前类里加上@Transactional注解,如果出现异常,插入操作回滚

@Transactional(rollbackFor = RuntimeException.class)
public int outInsert() {
    AnimalPO po = new AnimalPO();

    po.setName("示范点");
    po.setAge(11);
    int insert = animalMapper.insert(po);
	//手动抛出异常
    int a = 10/ 0;

    return insert;
}
同步示例2

当前类里添加编程事务,如果出现异常,事务会回滚

public int outInsert() {
    AnimalPO po = new AnimalPO();

    po.setName("示范点");
    po.setAge(11);
    int insert = 0;
	
	//开启事务
    TransactionStatus begin = transactionConfig.begin();
    try {
        insert = animalMapper.insert(po);
		//提交事务
        transactionConfig.commit(begin);
        int a = 10/ 0;
    } catch (Exception e) {
        transactionConfig.rollback(begin);
      log.info("回滚操作",e);
    }
    return insert;

}
同步示例3

外层service方法里使用注解事务,然后通过内层service插入数据,如果内层service出现异常,那么外层和内层都会回滚。

//外层service
  @Transactional(rollbackFor = RuntimeException.class)
    public int outInsert() {
		
		//内层sevice插入数据
 	 insert = lionService.intimalInsert();

        return insert;

    }
    //内层service
    public int intimalInsert() {
        log.info("内层测试开始");
        AnimalPO po = new AnimalPO();
        po.setName("777");
        po.setAge(11);
           int  insert = animalMapper.insert(po);
		int a = 10/ 0;
        return insert;
    }
同步示例4
  1. 外层服务添加事务注解,然后先插入数据
  2. 调用内层服务插入数据,编程事务和注解事务会管理内层服务,但是编程事务优先于注解事务。
  3. 此时外层服务手动抛出异常,内层服务回滚数据,外层服务回滚数据
  4. 外层服务注解事务生效,编程事务生效,注解事务包裹住了编程事务
  5. 手动异常出现在已完成内层service之后,此时内层serivce已完成自己的操作。但是数据库并没有内层插入的数据,因为外层注解事务还没跑完。
  6. 如果把外层编程式事务放入内层执行,效果和上面的执行是一样的
    @Transactional(rollbackFor = RuntimeException.class)
    public int outInsert() {

        AnimalPO po = new AnimalPO();
        po.setName("示范点99999");
        po.setAge(11);
        animalMapper.insert(po);

        int insert = 0;
        TransactionStatus begin = transactionConfig.begin();
        try {
			 //内层sevice插入数据
            insert = lionService.intimalInsert();
            transactionConfig.commit(begin);
        } catch (Exception e) {
            log.info("进入编程事务catch操作");
            transactionConfig.rollback(begin);
        }
		//抛出异常
        int a = 10 / 0;
        return insert;

    }
    //内层service
    public int intimalInsert() {
        log.info("内层测试开始");
        AnimalPO po = new AnimalPO();
        po.setName("777");
        po.setAge(11);
        int  insert = animalMapper.insert(po);
        return insert;
    }
  1. 如果把异常放在这里执行,效果和6一样,但是错误会多出一个Transaction rolled back because it has been marked as rollback-only,它表示事务多次回滚的调用。
    @Transactional(rollbackFor = RuntimeException.class)
    public int outInsert() {

        AnimalPO po = new AnimalPO();
        po.setName("示范点99999");
        po.setAge(11);
        animalMapper.insert(po);

        int insert = 0;
        TransactionStatus begin = transactionConfig.begin();
        try {
            insert = lionService.intimalInsert();
            //放在这里
            int a = 10 / 0;
            transactionConfig.commit(begin);
        } catch (Exception e) {
            log.info("进入编程事务catch操作");
            transactionConfig.rollback(begin);
        }

        return insert;

    }
同步示例5

外层serivce开启注解事务,并插入数据,插入操作被try-catch包裹,此时手动异常,数据正常插入成功,事务不起效果,因为异常被catch吃掉了,原因catch没有抛出异常,如果抛出异常,会被事务捕获,并处理回滚.

  @Transactional(rollbackFor = RuntimeException.class)
    public int outInsert() {

        AnimalPO po = new AnimalPO();
        po.setName("示范点99999");
        po.setAge(11);
        
        try {
            animalMapper.insert(po);
            //手动异常
            int a = 10 / 0;
        } catch (Exception e) {
            log.info("catch操作");
        }
        
        return 1;
    }
同步示例6

同步操作中,本类方法调用,调用的方法加了事务注解,被调用的方法没加,并且是private修饰的,那么数据会被事务管理,如果被调用的类插入数据失败,会导致事务回滚。


   @Transactional(rollbackFor = RuntimeException.class)
    public int outInsert()  {
            this.des(22);
        return 1;
    }

    private  int  des(int a){

            AnimalPO po = new AnimalPO();
            po.setName("示范点" + a);
            po.setAge(11);
            animalMapper.insert(po);
            int b = 10/0;
        return 1;

    }



异步线程操作

异步示例1

外层serivce开启注解事务,并插入数据,内层service使用子线程插入数据,内层出现异常,内层插入操作会回滚,外层事务不受干扰,外层插入数据成功。

  //添加事务注解即可
    @Transactional(rollbackFor = RuntimeException.class)
    public int outInsert() {

        AnimalPO po = new AnimalPO();
        po.setName("示范点99999");
        po.setAge(11);
        animalMapper.insert(po);

         //创建线程
         Thread thread = new Thread(() -> {
             TransactionStatus begin = transactionConfig.begin();
             try {
                 lionService.intimalInsert();
                 //手动异常
                 int a = 10 / 0;
                 transactionConfig.commit(begin);
             } catch (Exception e) {
                 log.info("进入编程事务catch操作");
                 transactionConfig.rollback(begin);
             }
                 });
        thread.start();
        
        return 1;

    }
    //内层service
    public int intimalInsert() {
        log.info("内层测试开始");
        AnimalPO po = new AnimalPO();
        po.setName("777");
        po.setAge(11);
        int  insert = animalMapper.insert(po);
        return insert;
    }
异步示例2

外层serivce开启注解事务,并插入数据,内层service使用编程事务,且使用子线程插入数据,此时外层出现异常,外层插入操作会回滚,内层事务不受干扰,内层插入数据成功。

  @Transactional(rollbackFor = RuntimeException.class)
    public int outInsert() throws InterruptedException {

        AnimalPO po = new AnimalPO();
        po.setName("示范点99999");
        po.setAge(11);
        animalMapper.insert(po);

         //创建线程
         Thread thread = new Thread(() -> {
             TransactionStatus begin = transactionConfig.begin();
             try {
                 lionService.intimalInsert();
               
                 transactionConfig.commit(begin);
             } catch (Exception e) {
                 log.info("进入编程事务catch操作");
                 transactionConfig.rollback(begin);
                 throw new RuntimeException(e);
             }
                 });
        thread.start();
        
        //手动异常
        int a = 10 / 0;

        return 1;

    }
    //内层service
    public int intimalInsert() {
        log.info("内层测试开始");
        AnimalPO po = new AnimalPO();
        po.setName("777");
        po.setAge(11);
        int  insert = animalMapper.insert(po);
        return insert;
    }
异步示例3

外层serivce开启注解事务,并插入数据。内层没有事务,内层service使用子线程插入数据,此时外层出现异常,外层插入操作会回滚,内层事务不受干扰,内层插入数据成功。内层service不受外层注解事务的管理。

 @Transactional(rollbackFor = RuntimeException.class)
    public int outInsert() throws InterruptedException {

        AnimalPO po = new AnimalPO();
        po.setName("示范点99999");
        po.setAge(11);
        animalMapper.insert(po);

         //创建线程
         Thread thread = new Thread(() -> {
              lionService.intimalInsert();
                 });
        thread.start();

        //手动异常
        int a = 10 / 0;

        return 1;

    }
异步示例4

当前方法中,通过本类方法调用本类中带有事务的方法,不会触发注解事务,哪怕调用的方法本身没有事务,也不会触发。以下结果是同步操作和异步操作都插入数据成功

    public int outInsert()  {
         //创建线程
         Thread thread = new Thread(() -> {


             this.des(12);

                 });
        thread.start();

        this.dem(22);
        return 1;
    }

    @Transactional(rollbackFor = RuntimeException.class)
    public  int  dem(int a){
        AnimalPO po = new AnimalPO();
        po.setName("示范点" + a);
        po.setAge(11);
        animalMapper.insert(po);
		//手动异常
        int b = 10/0;
       return 1;

    }

可以更改为:


  public int outInsert()  {
        //
        //AnimalPO po = new AnimalPO();
        //po.setName("示范点99999");
        //po.setAge(11);
        //animalMapper.insert(po);
            //创建线程
            Thread thread = new Thread(() -> {


                this.des(12);

                    });
            thread.start();

            this.des(22);

        return 1;
    }

    private   int  des(int a){
		//手动开启事务
        TransactionStatus begin = transactionConfig.begin();
        try {
            AnimalPO po = new AnimalPO();
            po.setName("示范点" + a);
            po.setAge(11);
            animalMapper.insert(po);
            int b = 10/0;
			//提交
            transactionConfig.commit(begin);
        } catch (Exception e) {
			//回滚
            transactionConfig.rollback(begin);
        }
        return 1;

    }




总结

  • 同步操作
  1. 外层添加事务,内层也添加事务.外层事务会管理内层事务,无论哪层事务抛出异常,两边都会回滚数据。
  2. 外层添加事务,内层添加事务,内层插入手动异常,但是并没有写回滚操作,也没有抛出异常,此时内层和外层插入操作成功。
  3. 外层有事务,内层没有,内层出现异常时,内层插入操作被try-catch捕获,如果catch没有抛出异常,那么外层事务并不会回滚,因为异常已经被吃掉了。要被外层事务捕获,内层的catch必须抛出异常.
  4. 本类中,同步调用本类带有注解事务的方法,不会触发事务特性,必须使用编程事务才行。
  • 异步操作
  1. 外层事务和内层事务其实是两个独立的操作,无论哪一边出现异常,只会回滚自己的,并不会影响对方的操作。
  2. 本类中,异步调用本类带有注解事务的方法,不会触发事务特性,必须使用编程事务才行。
转载自:https://juejin.cn/post/7126108674098987016
评论
请登录