【问题标题】:Spring Boot Transaction not rolling backSpring Boot 事务不回滚
【发布时间】:2020-03-11 08:36:09
【问题描述】:

我在 Spring Boot 中配置了一个项目注解。 pom 有依赖 spring-data-jpa 和 spring-tx。

与 Config 类或属性中的事务配置无关(因为理论上不需要)。

鉴于这项服务:

package com.appserver.motion.service;

@Component(value = "exerciseService")
@Service
@Data
public class ExerciseService {

@Autowired private ExerciseRepository exerciseRepository;
@Autowired private TagRepository tagRepository;
@Autowired private ExerciseTagJoinRepository exerciseTagJoinRepository;
@Autowired private EquipmentRepository equipmentRepository;
@Autowired private ExerciseEquipmentJoinRepository exerciseEquipmentJoinRepository;

private ExerciseCommand exerciseCommand = new ExerciseCommand();

private List<SelectItem> tags = new ArrayList<SelectItem>();
private List<Integer> selectedTags = new ArrayList<Integer>();

private List<SelectItem> equipments = new ArrayList<SelectItem>();
private List<Integer> selectedEquipments = new ArrayList<Integer>();

@Transactional
public int saveExercise() {
    try {
        int resultat = exerciseRepository.save(toEntity()).getExe_id();

        if (this.selectedTags!=null) {
            for (Integer selectedTag: this.selectedTags) {
                ExerciseTagJoinEntity etje = new ExerciseTagJoinEntity();
                etje.setExa_exe_id(new Integer(resultat));
                etje.setExa_tag_id(new Integer(selectedTag));
                exerciseTagJoinRepository.save(etje);
            }
        }


        if (this.selectedEquipments!=null) {
            for (Integer selectedEqu: this.selectedEquipments) {
                ExerciseEquipmentJoinEntity eeje = new ExerciseEquipmentJoinEntity();
                eeje.setEeq_exe_id(new Integer(resultat));
                eeje.setEeq_equ_id(new Integer(selectedEqu));
                exerciseEquipmentJoinRepository.save(eeje);
            }
        }

        FacesMessage facesMsg = new FacesMessage(FacesMessage.SEVERITY_INFO, "Info:", "Insertat correcte");
        FacesContext.getCurrentInstance().addMessage("Correcte:", facesMsg);

        return resultat;
    }catch (Exception ex) {
        FacesMessage facesMsg = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Error:", ex.getMessage());
        FacesContext.getCurrentInstance().addMessage("Error:", facesMsg);
        return -1;
    }
}

}

存储库都是 JpaRepository 的扩展,并带有 @Repository 注释。

问题在于事务不会在异常时回滚,无论是否带有 rollBackFor 属性

另一方面,事务似乎正在工作,因为直到保存方法结束,插入才在 DB 中刷新。

顺便说一下,我正在使用 PostgreSQL DB。

我错过了什么吗?

我一直在记录交易包,这就是我得到的:

2019-11-15 08:52:45.493 TRACE 9100 --- [nio-3000-exec-4] o.s.t.i.TransactionInterceptor :获取 [com.appserver.motion.service.ExerciseService.saveExercise] 的交易 2019-11-15 08:52:47.678 TRACE 9100 --- [nio-3000-exec-4] o.s.t.i.TransactionInterceptor :获取 [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save] 的交易 2019-11-15 08:52:48.015 TRACE 9100 --- [nio-3000-exec-4] o.s.t.i.TransactionInterceptor :为 [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save] 完成交易 2019-11-15 08:52:49.163 TRACE 9100 --- [nio-3000-exec-4] o.s.t.i.TransactionInterceptor:获取 [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save] 的交易 2019-11-15 08:52:49.168 TRACE 9100 --- [nio-3000-exec-4] o.s.t.i.TransactionInterceptor :为 [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save] 完成交易 2019-11-15 08:52:49.168 TRACE 9100 --- [nio-3000-exec-4] o.s.t.i.TransactionInterceptor:获取 [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save] 的交易 2019-11-15 08:52:49.171 TRACE 9100 --- [nio-3000-exec-4] o.s.t.i.TransactionInterceptor :为 [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save] 完成交易 2019-11-15 08:53:02.439 TRACE 9100 --- [nio-3000-exec-4] o.s.t.i.TransactionInterceptor :为 [com.appserver.motion.service.ExerciseService.saveExercise] 完成交易

【问题讨论】:

    标签: spring-boot jpa transactions spring-data-jpa spring-transactions


    【解决方案1】:

    您的@Transactional 方法不会引发异常,因为您会捕获并处理它,因此不会发生回滚。

    如果你想捕获异常——例如用于记录目的——那么你重新抛出异常。此外,只有从java.lang.RuntimeException 扩展的异常才会自动发生回滚。

    //if the runtime type of Exception is a checked exception then optional config
    @Transactional(rollbackFor = SomeNonRunTimeException.class))
    public int saveExercise() {
        try {
            ....
        }catch (Exception ex) {
            //do some logging
            throw ex; //otherwise no rollback
        }
    }
    

    另一方面,事务似乎正在工作,因为直到保存方法结束,插入才在 DB 中刷新。

    这与事务无关,但由于“后写”模式会延迟刷新对数据库的更改,直到尽可能晚 - 通常在事务提交时,即当您的 @Transactional 方法返回时

    http://learningviacode.blogspot.com/2012/02/write-behind-technique-in-hibernate.html

    这种模式会导致另一个问题 - 您的异常处理程序不会捕获在刷新时生成的异常,因为刷新发生在事务提交时,即在方法返回时 - 在您的 catch 块之外。如果你想捕获并处理这些异常,那么你需要在你的 catch 块中进行显式刷新:

    @Transactional(rollbackFor = SomeNonRunTimeException.class))
    public int saveExercise() {
        try {
            //explicit flush required if we want to ctach and handle SQL exceptions
            exerciseTagJoinRepository.saveAndFlush(etje);
        }catch (Exception ex) {
            //do some logging
            throw ex; //otherwise no rollback
        }
    }
    

    【讨论】:

    • 你是对的,当异常不是从RuntimeException扩展时,必须标记为rollbackFor并重新抛出(或不捕获)。 “冲洗”指示也很受欢迎。
    【解决方案2】:

    可能你需要从一个方法中抛出 RuntimeException 并且它应该被标记为

    @Transactional(rollbackFor=Exception.class)
    

    它应该回滚事务。

    在你的情况下,它应该是这样的

        @Transactional(rollbackFor=FacesMessageException.class)
    public int saveExercise() {
        try {
    
        }catch (Exception ex) {
            throw new FacesMessageException();
        }
    }
    

    我建议您最好抛出异常,例如 FacesMessageException(),而不是捕获异常,但在此之前您需要创建 FacesMessageException 类。

    目前它没有回滚,因为您在 catch 块中捕获了异常,但它应该被抛出

    【讨论】:

      猜你喜欢
      • 2018-11-05
      • 2016-10-07
      • 2017-11-07
      • 2018-09-03
      • 2020-02-06
      • 2017-03-03
      • 2012-03-10
      • 2018-07-15
      相关资源
      最近更新 更多