Spring Data JPA + Hibernate:从未到达Transactional方法中的catch块

时间:2014-07-14 12:53:13

标签: spring jpa try-catch spring-data-jpa transactional

在我们的Spring Data JPA + Hibernate应用程序中,有多种方法在同一个事务中有多个JPA操作 - 下面是其中一个spring服务方法的代码/配置。

@Transactional(rollbackFor=MyAppException.class)
public void updateCustomerprofile(String customerKey) throws MyAppException{
    try{
    CustomerId customerIdObj = customerIdRepository.findOne(customerKey);
    customerIdObj.setCreatedUser("<loggedin-user>");
     // more logic here
    customerIdObj = customerIdRepository.save(customerIdObj);
    }catch(DataAccessException dae){
        //this catch block is never reached even if there is an exception while saving because of JPA flusheing at the end of transaction
        throw new MyAppException(dae);
    }

}

据观察,即使在保存记录时抛出异常,执行也永远不会到达catch块 - 这是因为JPA在事务结束时刷新。

这个捕获块到底应该放在哪里?

我们是否应该在Web层(bean)上捕获DataAcccessException?

如果是这样,我们是不是将数据层依赖关系带到了web层?

如果我必须将DataAccessException包装到我的特定于应用程序的异常中,我该怎么办?

请建议。

2 个答案:

答案 0 :(得分:2)

这里的问题是代码中没有抛出异常,但是当容器提交事务时(参见this answer)。为避免使用bean管理的事务,您可以:

  • 通过EntityManager.flush()
  • 同步持久性上下文
  • 使用带有@AroundInvoke注释的EJB拦截器,它开始,提交和回滚事务(类似于BMT的方法)或
  • 将应该抛出异常的代码包装在@TransactionAttribute(REQUIRES_NEW)
  • 的方法中

另请参阅this thread,其中详细说明了REQUIRES_NEW方法的一些细节。

答案 1 :(得分:1)

在catch块中添加entityManager.flush(),或者在@Transactional带注释的方法周围移动catch块。

原因是实体经理通常可以自由决定何时将数据写入数据库(即发生异常时的点)。通常,写入数据库已完成

  • 在&#34;选择&#34;之前查询,
  • 当显式调用entityManager.flush()
  • 提交交易时。

所以解决方案是:确保实体管理器将(非法)数据写入try-block中的数据库。