【问题标题】:JPA Version Entity mergeJPA 版本实体合并
【发布时间】:2016-09-17 18:19:37
【问题描述】:

我知道已经有一些关于这个主题的问题,但我认为这个问题有所不同。

假设我有这个课程:

@Entity
public class foo{
  @Id
  @GeneratedValue
  private long id;

  @Version
  private long version;

  private String description;
  ...
}

我创建了一些对象并使用 JPA add() 将它们保存到数据库中。

稍后,我使用 JPA all() 从存储库中获取所有内容; 从该列表中,我选择一个对象并更改描述。

然后我想使用 JPA merge() 更新存储库中的那个对象(参见代码)。

这里的问题是它在我第一次尝试更改描述时起作用(版本值现在是 2)。 第二次,一个 OptimisticLockException 被抛出,表明该对象同时被改变了。

我正在使用 H2,在嵌入式模式下拥有 DB。

合并代码:

//First: persist is tried, if the object already exists, an exception is raised and then this code is executed
try {
     tx = em.getTransaction();
     tx.begin();
     entity = em.merge(entity);
     tx.commit();
   } catch (PersistenceException pex) {
              //Do stuff
            }

哪里出了问题?

谢谢。

编辑(更多代码)

//Foo b 是通过使用 JPA all() 从 db 中获取所有对象获得的,然后从该列表中选择一个对象

b.changeDescription("新的东西!");

//调用更新方法(合并代码已贴)

【问题讨论】:

  • 你试过用 em.persist 代替 merge 吗? merge 方法在更新后继续跟踪对象。根据您的配置方式,更新可能已经进入数据库。
  • 发布更多代码。相关的是您如何加载和修改实体以及如何调用您发布的代码。
  • @AlanHay 我用更多代表我的情况的代码编辑了原始帖子
  • @fhofmann 我编辑了回答您问题的“合并代码”。

标签: java jpa h2


【解决方案1】:

Here are 一个帖子完美地解释了何时抛出OptimisticLockException

另外,为了将来参考,您可以让 JPA 在更新实体时避免这种内存中验证,但希望在此事务结束时使用 EntityManager 上的 detach 方法在 DB 端进行更改:

em.detach(employee);

【讨论】:

    【解决方案2】:

    我假设您正在从不同的客户端或不同的线程更改列表中的元素。这就是导致OptimisticLockException 的原因。

    一个线程在它自己的EntityManager 中读取Foo 对象并在读取时获得@Version

    // select and update AnyEntity
    EntityManager em1 = emf.createEntityManager();
    EntityTransaction tx1 = em1.getTransaction();
    tx1.begin();
    AnyEntity firstEntity = em1.createQuery("select a from AnyEntity a", AnyEntity.class).getSingleResult();
    firstEntity.setName("name1");
    em1.merge(firstEntity);
    

    在第一个客户端将其更改提交到数据库之前,另一个客户端同时读取和更新Foo 对象:

    // select and update AnyEntity from a different EntityManager from a different thread or client
    EntityManager em2 = emf.createEntityManager();
    EntityTransaction tx2 = em2.getTransaction();
    tx2.begin();
    AnyEntity secondEntity = em2.createQuery("select a from AnyEntity a", AnyEntity.class).getSingleResult();
    secondEntity.setName("name2");
    em2.merge(secondEntity);
    

    现在第一个客户端将其更改提交到数据库:

    // commit first change while second change still pending
    tx1.commit();
    em1.close();
    

    第二个客户端在更新其更改时会收到OptimisticLockException

    // OptimisticLockException thrown here means that a change happened while AnyEntity was still "checked out"
    try {
        tx2.commit();
        em2.close();
    } catch (RollbackException ex ) {
        Throwable cause = ex.getCause();
        if (cause != null && cause instanceof OptimisticLockException) {
            System.out.println("Someone already changed AnyEntity.");
        } else {
            throw ex;
        }
    }
    

    参考:Java - JPA - @Version annotation

    【讨论】:

      【解决方案3】:

      您是否正确初始化了版本字段?

      如果不是,它不应该与null 一起使用,请尝试为其添加默认值:

      @Version
      private Long version = 0L;
      

      【讨论】:

        猜你喜欢
        • 2013-11-13
        • 2016-02-10
        • 1970-01-01
        • 1970-01-01
        • 2013-10-13
        • 2018-03-21
        • 1970-01-01
        • 2012-06-17
        • 2012-12-26
        相关资源
        最近更新 更多