hibernate - 与不存在的 id 合并

标签 hibernate jpa

众所周知,EntityManager.merge() 的引入是为了将对分离实体所做的更改合并到附加实体中。这表明了一种模式“获取一个实体,将其浅拷贝返回给某个客户端,让客户端进行一些更改并向我们发送更新的实例,然后将更新的实例合并回持久性上下文中”,对吧?

但是,如果在该客户端正在使用其独立副本时实例被第三方删除了怎么办?当我们尝试将其合并回去时,人们会期望其行为类似于 SQL 更新:抛出一些“实体不存在”之类的异常。人们可能会感到失望:在这种情况下,JPA(至少在 Hibernate 实现中)将简单地忽略提供的 ID,并创建一个新实体而不是更新现有实体。

我该如何处理这个问题?当客户端只是尝试更新已删除的实体时,我当然不想创建新实体。

我想我可以在合并之前通过 id 找到实体(如果找不到则抛出),并依靠“可重复读取”来防止实体删除(如果有)对当前事务可见,确保 merge() 进行更新现有实体。然而,这有点难看,特别是考虑到 merge() 可以级联到嵌套实体,我也必须 find() 这些实体。我还尝试引入 @version 希望它会有所帮助,并且如果我提供非空基本版本,JPA 会检测到创建新实体是不正确的,但这似乎也被忽略了。

附注我突然想到,我可以允许 JPA 创建一个新实体,然后根据客户端提供的 ID 检查其 ID。比如说,如果使用一个序列来生成 ID,则新 ID 将与旧 ID 不同,在这种情况下,我可以抛出异常并回滚事务。但这仍然是额外的检查,因此这个解决方案并不比使用 find() 更好。

最佳答案

合并后可以检查key是否相同。
如果相同,则说明合并对象相同,如果不同,则说明分离实体已被删除。

    EntityManager em = entityManagerFactory.createEntityManager();
    em.getTransaction().begin();
    EntityClass mergedEntity = em.merge( detachedEntity );
    if( mergedEntity.getId() != detachedEntity.getId() ){
        System.out.println("Entity has been deleted !!!  Do rollback.");
        em.getTransaction().rollback();
    }else{
        System.out.println("Entity has been merged !!! Do commit ");
        em.getTransaction().commit();
    }

如果启用 SQL 跟踪,您将看到在执行合并操作时:
mergedEntity = em.merge( detachedEntity );,Hibernate 在幕后正在执行以下操作:

  • 从实体中选择...,其中 id = id_of_detached_entity
  • 如果上述查询找到一行,则 Hibernate 为合并的实体对象分配相同的 Id
  • 如果上面的查询没有返回任何内容,Hibernate 将从序列中获取下一个 Id 值,并将该值分配给合并的实体对象,因此合并的对象具有与旧的分离对象不同的 Id。

关于hibernate - 与不存在的 id 合并,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50997657/

相关文章:

java - 如何在非主键上使用 hibernate 注释设置单向一对一关系?

java - 当仅设置 id 而不是首先从数据库获取对象时,一对多不会在没有引用的情况下更新

java - 使用@Transactional时EntityManager.persist()不插入数据

java - 事务中的 org.hibernate.LazyInitializationException

java - Hibernate工具Pojo Generation默认使用Set

Java Spring,如何检索记录并使用新的唯一键发布重复项?

java - Hibernate 为@ManyToOne JPA 注释属性创建 N+1 查询

java - 提取公共(public)列(审核字段)[Java][JPA]

java - Hibernate:无法级联删除子级的单向父级

java - 如何防止 Hibernate 使用 0 作为 ID?