Spring 声明式事务的几个使用的坑

1.demo地址

  1. github地址

2. 事务不生效

  1. spring 默认通过动态代理的方式实现 AOP,对目标方法进行增强,private 方法无法被代理到,spring 自然也无法动态增强事务处理逻辑
    /**
     * 一、事务不生效 1、spring 默认通过动态代理的方式实现 AOP,对目标方法进行增强,private 方法无法被代理到,
     * spring 自然也无法动态增强事务处理逻辑
     *
     * @param name
     * @return
     */
    public int createUserWrong1(String name) {
        try {
            this.createUserPrivate(new UserEntity(name));
        } catch (Exception ex) {
            log.error("create user failed becauser {} ", ex.getMessage());
        }
        return userRepository.findByName(name).size();
    }
	// 这里不生效,不会被代理到
    @Transactional
    private void createUserPrivate(UserEntity userEntity) {
        userRepository.save(userEntity);
        if (userEntity.getName().contains("test")) {
            throw new RuntimeException("invalid username");
        }
    }
  1. 必须通过代理过的类从外部调用目标方法才能生效
    /**
     * 一、事务不生效 2、必须通过代理过的类从外部调用目标方法才能生效
     *
     * @param name
     * @return
     */
    public int createUserWrong2(String name) {
        try {
        	// 通过this调用不生效,不会白代理到
            this.createUserPublic(new UserEntity(name));
        } catch (Exception ex) {
            log.error("create user failed becauser {} ", ex.getMessage());
        }
        return userRepository.findByName(name).size();
    }
    @Transactional
    public void createUserPublic(UserEntity userEntity) {
        userRepository.save(userEntity);
        if (userEntity.getName().contains("test")) {
            throw new RuntimeException("invalid username");
        }
    }
  1. spring 通过 cglb 通过继承方式实现,private 在子类中不可见,自然无法进行事务增强, this 代表对象自己,Spring 不可能注入 this,所以通过this方法访问的必然不是代理
    @Autowired
    private UserService self;

    /**
     * spring 通过 cglb 通过继承方式实现,private 在子类中不可见,自然无法进行事务增强
     * this 代表对象自己,Spring 不可能注入 this,所以通过this方法访问的必然不是代理
     *
     * @param name
     * @return
     */
    public int createUserRight(String name) {
        try {
            //this 代表对象自己,Spring 不可能注入 this,所以通过this方法访问的必然不是代理
//            this.createUserPublic(new UserEntity(name));
            //spring 中 CGLIB 通过继承方式实现,private 在子类中不可见,自然无法进行事务增强
            self.createUserPublic(new UserEntity(name));
        } catch (Exception ex) {
            log.error("create user failed becauser {} ", ex.getMessage());
        }
        return userRepository.findByName(name).size();
    }

    @Transactional
    public void createUserPublic(UserEntity userEntity) {
        userRepository.save(userEntity);
        if (userEntity.getName().contains("test")) {
            throw new RuntimeException("invalid username");
        }
    }

3. 事务异常不回滚

  1. 异常必须抛出被捕获,或者手动设置回滚 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
  /**
     * 二、事务异常不回滚 1、异常必须抛出被捕获,或者手动设置回滚
     *
     * @param userEntity
     */
    @Transactional
    public void createUserPublic1(UserEntity userEntity) {
        try {
            userRepository.save(userEntity);
            //原因: 这里抛出 RuntimeException 被内部捕获了,没有throw ,事务异常不会回滚
            throw new RuntimeException("error");
        } catch (Exception ex) {
            log.error("create user fail ", ex.getMessage());

            //解决:手动设置回滚,否则事务不生效
//            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
  1. 默认捕获 RuntimeException 和 Error ,如果捕获 CheckedException 需要通过注解设置 rollbackFor=Exception.class
  /**
     * 二、事务异常不回滚 2、默认捕获 RuntimeException 和 Error ,
     * 如果捕获 CheckedException 需要通过注解设置 rollbackFor=Exception.class
     *
     * @param userEntity
     * @throws IOException
     */
//    @Transactional(rollbackOn = Exception.class) // 解决:手动指定,可以回滚
    @Transactional//原因:受检查异常 默认不会回滚
    public void createUserPublic2(UserEntity userEntity) throws IOException {
        userRepository.save(userEntity);
        ortherTask();
    }

    private void ortherTask() throws IOException {
        Files.readAllLines(Paths.get("file-not-exist"));
    }

4. 事务传播属性配置

  1. 参考文章
    1. Spring 中的事务传播
    2. Spring事务管理(详解+实例)
    3. @Transactional 同一个类中无事务方法a()内部调用有事务方法b()的问题
  2. todo

5. controller调用多个service的事务方法

  1. 一般service方法是有事务的,把所有操作封装在一个service方法中是比较安全的。 如果在controller中调用多个service方法,只有查询的情况下是可以这样的。

6. 有问题可以留言大家一起探讨 。。。。。。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章