在我们的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包装到我的特定于应用程序的异常中,我该怎么办?
请建议。
答案 0 :(得分:2)
这里的问题是代码中没有抛出异常,但是当容器提交事务时(参见this answer)。为避免使用bean管理的事务,您可以:
EntityManager.flush()
,@AroundInvoke
注释的EJB拦截器,它开始,提交和回滚事务(类似于BMT的方法)或@TransactionAttribute(REQUIRES_NEW)
。另请参阅this thread,其中详细说明了REQUIRES_NEW
方法的一些细节。
答案 1 :(得分:1)
在catch块中添加entityManager.flush()
,或者在@Transactional带注释的方法周围移动catch块。
原因是实体经理通常可以自由决定何时将数据写入数据库(即发生异常时的点)。通常,写入数据库已完成
entityManager.flush()
或所以解决方案是:确保实体管理器将(非法)数据写入try-block中的数据库。