JPA 中删除实体时的乐观锁定

Tin*_*iny 5 hibernate jpa eclipselink optimistic-locking

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

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);
}
Run Code Online (Sandbox Code Playgroud)

关联的客户端提供的实体是分离的实体。该entityManager.remove()操作生成DELETE如下 DML 语句:

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);
}
Run Code Online (Sandbox Code Playgroud)

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

javax.persistence.OptimisticLockException然而,即使提供的实体要在另一个会话中由另一个用户同时修改,这也不会抛出异常,因为它是由客户端(Web 或其他方式)最后读取的,因为该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.
Run Code Online (Sandbox Code Playgroud)

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

entityManager.remove(entityManager.contains(entity) ? entity : entityManager.merge(entity));
Run Code Online (Sandbox Code Playgroud)

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

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

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

Dra*_*vic 0

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

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

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