JPA版本实体合并

时间:2016-05-20 13:01:28

标签: java jpa h2

我知道这个问题已经存在一些问题,但我认为这个问题不同。

假设我有这门课程:

@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在嵌入模式下有数据库。

合并代码:

//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
            }

哪里可能出错?

谢谢。

编辑(更多代码)

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

b.changeDescription(“Something new!”);

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

3 个答案:

答案 0 :(得分:1)

我认为您正在从不同的客户端或不同的线程更改列表中的元素。这是导致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

答案 1 :(得分:0)

Here are时发布一个帖子,该文章在抛出OptimisticLockException时完全解释。

另外,为了将来参考,您可以让JPA在更新实体时避免对实体进行内存中验证,但是想要在此事务结束时使用' detach'更改数据库端。 EntityManager上的方法:

em.detach(employee);

答案 2 :(得分:0)

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

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

@Version
private Long version = 0L;