我的情况:一个公共方法在开始时试图更新实体,最后,它试图查询更新后的实体并通过SNS发送消息。
第一个问题是,如果我调用DeviceDao.save()
,我可以获得更新的实体,但是如果我调用UpdateClass
中的自定义更新方法updateAsset
,我就不能获得更新的实体。
第二个问题是代码仍然会传递到messagePublisher,即使UpdateClass.doSth()
抛出org.hibernate.exception.ConstraintViolationException(我对资产ID有一个唯一约束),我也可以在“MessagePublisher.doRealthing()”中获得更新的实体,但最终,更新会回滚。
我的问题是,为什么我会得到那两个问题?对于第二个问题,除了提前查询资产Id,我如何才能避免它?
这是我的密码
public interface ExampleDeviceDao extends JpaRepository<Device, UUID>, JpaSpecificationExecutor<Device> {
@Modifying
@Query("UPDATE device a SET a.asset = ?1 WHERE a.device = ?2")
int updateAsset(UUID asset, UUID device);
}
我的公共服务和方法:
第一个问题
2条答案
按热度按时间uqjltbpv1#
问题可能是您的一级缓存(也称为持久性上下文)包含您正在执行DML语句的实体(
update device ...
)。Hibernate并不知道DML语句将更改持久性上下文中实体的状态,也无法对此执行任何明智的操作。因此,更改将在数据库上执行,但是持久性上下文中的实体仍然具有旧的状态,唯一明智的做法是刷新该实体(如果需要的话),或者通过调用EntityManager.detach()
将其从持久性上下文中删除。就我对你的第二个问题的理解,你的发布者代码发布了SNS消息,即使数据库事务失败。在这种情况下,你总是会有一个可能的问题,因为SNS客户端不是事务的一部分,也不可能是。
您可以通过以下方式在事务完成后发送消息
@Transactional
方法返回后调用发布服务器如果JVM在事务完成后关闭,但尚未发送SNS消息,或者SNS消息发送由于某种原因失败,则可能会错过发送某些消息。
为了解决这些问题,你通常会使用某种具有重试机制的作业队列。如果你对此感兴趣,你可以看看Blaze-Notify,它是一个工具包,可以用来有效地实现这一点。与其立即发送SNS消息,不如持久化一个代表消息的实体,该消息随后将被异步处理并利用重试机制发送。
q3aa05252#
第一期请参见@ChristianBeikov answer。
第二个问题和第一个问题的原因一样,你有
1.您在内存中更改设备(持久上下文)。
1.您将设备保存到数据库中。实际上,Hibernate此时什么也不做。您可以启用SQL日志记录,并看到什么也没有发生。
1.您从数据库中获取了
Device
。Hibernate在这里也不对数据库做任何操作。它只是从内存中获取您在(1)步骤中所做的更改。请使用SQL日志进行验证。1.已更改的
Device
被发送到SNS。@Transactional
方法结束。从方法返回后,Hibernate将从点(1)到数据库的所有更改刷新(请参阅SQL日志),并发生验证异常错误。但SNS消息已随新更改一起发送。数据库具有旧更改,因为事务已回滚。可能的解决方案
您可以在发布SNS消息之前刷新数据库更改