4

我有一个 JPA 实体类用户,其用户名 @ID 且没有父实体组。我需要确保当两个并行事务尝试使用相同的用户名持久化一个用户时,只有一个被提交,另一个被回滚。

例子:

User bob = new User("bob");
EntityTransaction transaction = em.getTransaction();

try {
  transaction.begin();
  User u = em.find(User.class, bob.id());
  if (u == null) {
    em.persist(bob);
  } else {
    // identical user already existed before the transaction
    throw new UserExistsException(); 
  }
  transaction.commit();
} catch (RollbackException e) {
    // identical user was created during the transaction
    throw new UserExistsException();
}

根据 Datastore 文档,事务遵循乐观锁定方法:

“当事务开始时,App Engine 通过检查事务中使用的实体组的最后更新时间来使用乐观并发控制。在为实体组提交事务时,App Engine 再次检查在事务中使用的实体组的最后更新时间“ (https://developers.google.com/appengine/docs/java/datastore/transactions

这在持久化事务之前不存在的新(根)实体时是否有效?就我而言,App Engine 是否会检查另一笔交易是否同时保留了具有相同 ID 的用户?如果是这样,我是否需要一个显式的 @Version 字段来达到这个目的?

4

1 回答 1

1

对这个问题进行一些早就应该结束的事情:答案是“是”,上面的代码应该可以按预期工作。简而言之,乐观并发控制机制将使用(根)实体的种类“用户”和给定的标识符“鲍勃”来比较两个事务中使用的(新)实体组。Datastore 文档现在还明确解决了创建案例:

当两个或更多事务尝试同时更改同一个实体组(更新现有实体或创建新实体)时,第一个提交的事务将成功,所有其他事务将在提交时失败。

在这种情况下,使用 JPA,您会得到一个 RollbackException。低级 API 将引发 ConcurrentModificationException。如果您使用 Objectify(我强烈推荐),失败的事务将自动重试。因此,您应该确保在事务中首先检查实体是否存在,除非您想在第二次尝试时覆盖它。

于 2015-02-26T20:08:42.617 回答