我的服务类中有一个方法,它对一个实体执行一些更改,之后它执行一些关键代码(与不同微服务的通信),这些代码可能会返回附加到异常的错误信息。如果发生错误,则应还原更改。为了达到这个目的,我想我会使用@Transactional。
@Service
public class MyService {
@Autowired
private EntityRepository repository;
@Transactional(rollbackFor = CriticalCodeException.class, isolation = Isolation.READ_COMMITTED, propagation = Propagation.NESTED)
public Entity foo(entity) {
/* perform changes ... */
entity.setBar(...)
try {
/* critical code */
} catch (CriticalCodeException e) {
e.setEntityId(entity.getId());
throw e;
}
}
public attachError(CriticalCodeException e) {
// reload entity which should be rolled back but is not
Entity reloaded = repository.findOne(e.getEntityId);
reloaded.setError(e.getError); // attach error
repository.save(reloaded) // saves changes which should be rolled back
}
}
由于CriticalCodeException是一个RuntimeException,也可能发生在另一个上下文中,我创建了一个指定的ExceptionHandler,它应该捕获它并将错误附加到我的实体。
@ExceptionHandler(CriticalCodeException .class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ResponseBody
public ErrorResponse processError(CriticalCodeException e) {
entityService.attachError(e);
return e.getError();
}
然而,当我附上错误时,我也不会挽救过去的改变。因此,当我从嵌套的@Transactional方法抛出Exception之后重新加载实体时,我希望它处于初始状态。嗯,事实并非如此。如果我没有附加错误(因此省略我的save()调用),则更改不会持久化。所以我认为在我的异常处理之后最终会执行回滚。
在回滚完成后,有人能告诉我如何在初始状态下访问我的实体吗?
我正在使用JPA和Hibernate的Spring Boot Data。我已尝试使用@EnableTransactionManagement等各种配置并设置不同的bean组合。然而,经过大量的研究,我非常肯定@Transactional实际上正在工作(从上面描述的最终回滚中可以看出),并且无需进一步配置即可工作。我做错了什么?
修改
事实上,这些变化已经回滚,但我的实体经理没有被重置,只有M. Deinum指出。在我的持久性设置中,对实体的更改仅通过调用repository.save(entity)
来保留。我最终做的是省略事务方法,而是实现repository.reload(entity)
,它通过实体管理器刷新我的实体。通过这种方式,我可以摆脱以前所做的更改,这些更改甚至不会在第一时间持续存在。