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. 有問題可以留言大家一起探討 。。。。。。

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