Java 如何正确锁定和重新加载实体

Java 如何正确锁定和重新加载实体,java,mysql,hibernate,spring,jakarta-ee,Java,Mysql,Hibernate,Spring,Jakarta Ee,在我的web应用程序中,我有几个线程可能同时访问相同的数据,因此我决定使用Hibernate实现乐观(版本控制)和悲观锁定 目前,我使用以下模式锁定实体并对其执行写入操作(使用Springs事务管理器和带有@Transactional的事务划分): 然而,有时当@Transactional刷新时,我会得到StaleObjectException,它告诉我一个ChildEntity已经被并发修改,现在有一个错误的版本 我猜我没有正确地刷新实体及其子项,因此我正在处理过时的数据。有人能指出如何做到这

在我的web应用程序中,我有几个线程可能同时访问相同的数据,因此我决定使用Hibernate实现乐观(版本控制)和悲观锁定

目前,我使用以下模式锁定实体并对其执行写入操作(使用Springs事务管理器和带有@Transactional的事务划分):

然而,有时当@Transactional刷新时,我会得到
StaleObjectException
,它告诉我一个ChildEntity已经被并发修改,现在有一个错误的版本

我猜我没有正确地刷新
实体及其子项,因此我正在处理过时的数据。有人能指出如何做到这一点吗?我的一些想法包括清除持久性上下文(会话)或再次调用
session.lock(entity,LockMode.READ)
,但我不确定这里的内容是否正确

谢谢你的帮助

为什么要让“锁定模式.升级”和乐观锁定一起生活?看起来像是有争议的事情

Hibernate从不锁定内存中的对象,始终使用数据库的锁定机制。此外,“如果数据库不支持请求的锁定模式,Hibernate将使用适当的替代模式,而不是引发异常。这确保了应用程序的可移植性。”。这意味着,如果您的数据库不支持SELECT。。。对于更新,您很可能会得到这些异常


另一个可能的原因是您没有为孩子们使用“org.hibernate.annotations.CascadeType.LOCK”。

您可能想看看这个hibernate问题:


简而言之:如果给定实体已预加载,则Hibernat在成功锁定后不会执行选择。您需要在收到锁后为自己的实体调用refresh。

我使用的是MySQL InnoDB,它支持“选择…进行更新”,因此我认为这里没有问题。此外,我也在使用乐观锁定,以便在同时进行修改时获得“通知”。很抱歉,没有注意到您在儿童方面有问题(粗体:)。我已经更新了答案…在我的代码中,我总是锁定实体,即使我只想更新实体而不是子实体。因此,还应该获得锁。实际上,我主要担心的是,我没有仅使用刷新来处理最新的实体/子实体。对不起,我很困惑。我相信您使用的是最新版本的entity,但在儿童方面存在问题。我认为出现这个问题是因为没有为它们定义未锁定的子对象(org.hibernate.annotations.CascadeType.LOCK)。是这样还是我遗漏了什么?如果不锁定子项,则无法保证您使用的是最新版本的子项。另一方面,主实体必须是最新的,因为在调用refresh()之前显式锁定了它
@Transactional
public void doSomething(entity) {
    session.lock(entity, LockMode.UPGRADE);
    session.refresh(entity);

    // I change the entity itself as well as entites in a relationship.
    entity.setBar(...);
    for(Child childEntity : entity.getChildren()) {
        childEntity.setFoo(...);
    }
}