【问题标题】:Optimistic locking while deleting entities in JPA在 JPA 中删除实体时的乐观锁定
【发布时间】:2015-07-17 12:31:27
【问题描述】:

使用 CMT 的 EJB 中的事务方法,用于删除提供的实体:

public boolean delete(Entity entity) {
    Entity managedEntity = entityManager.find(Entity.class, entity.getId());

    if (managedEntity == null) {
        throw new EntityNotFoundException();
    }

    entityManager.remove(managedEntity);
    return !entityManager.contains(managedEntity);
}

由关联的客户端提供的实体是一个分离的实体。 entityManager.remove() 操作会产生一个DELETE DML 语句,如下所示,

DELETE FROM db.entity WHERE ((id = ?) AND (version = ?))

如果数据库中的行版本与实体中的行版本冲突,附加检查AND (version = ?) 应该会导致javax.persistence.OptimisticLockException 被抛出。

这不会抛出javax.persistence.OptimisticLockException,即使提供的实体将在另一个会话中由另一个用户同时修改,因为它最后一次被客户端(网络或其他)读取,因为find()方法获得在另一个事务中具有更新行版本的实体(旧的/陈旧的行版本(由@javax.persistence.Version 标记)仅包含在作为方法参数提供的分离实体中)。

EntityManager#merge(T entity) 不同,EntityManager#remove(Object entity) 不接受分离的实体。将分离的实体传递给 remove() 是一个错误。

java.lang.IllegalArgumentException: Entity must be managed to call remove: com.example.Entity[ id=1 ], try merging the detached and try the remove again.

在删除like之前直接合并实体,

entityManager.remove(entityManager.contains(entity) ? entity : entityManager.merge(entity));

如果实体有关联,则会导致其他问题。

它只留下了乐观锁定的一种替代方法,即手动检查行版本,然后手动抛出javax.persistence.OptimisticLockException,这不是人们可能期望的方法。

长话短说:如果要删除的实体被另一个会话背后修改,如何在删除实体时抛出javax.persistence.OptimisticLockException?上述方法不适用于乐观锁定。

【问题讨论】:

  • 合并有什么问题?如果合并了您不想合并的引用实体,则应考虑删除所涉及的级联设置,以便仅合并要删除的实体。否则,请创建您自己的使用 ID 和版本而不是使用 Find 的查询。
  • 合并操作会留下EclipseLink 中this 问题中提到的问题。这需要移除级联,但并不总是可行的。在许多其他情况下是必要的。

标签: hibernate jpa eclipselink optimistic-locking


【解决方案1】:

如果您想避免merge 的分类,您可以改为执行“软合并” - 只需从分离实例中获取版本并将其设置为托管实例:

Entity managedEntity = entityManager.find(Entity.class, entity.getId());
if (managedEntity == null) {
    throw new EntityNotFoundException();
}
managedEntity.setVersion(entity.getVersion());

当然,为了不重复自己,根据您的 DAO、存储库等的层次结构,您可以创建一个通用的 softMerge 方法,该方法将加载托管实体,从分离的实例中获取版本并将其设置为管理的。

【讨论】:

  • @javax.persistence.Version 字段的 setter 方法声明为 protected,因为它应该是为了防止对版本字段的潜在无意更改。
  • 是的,但您现在想明确更改它。 :) 作为替代,通用softMerge 方法可以使用反射来设置它,以便它适用于所有实体。由于softMerge 将成为框架的一部分,因此可以使用反射。
猜你喜欢
  • 2017-10-02
  • 1970-01-01
  • 1970-01-01
  • 2012-11-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多