0

UserA 和 UserB 分别同时更改 objectA.filedA objectA.filedB。因为他们没有改变同一个领域,人们可能会认为没有重叠。真的吗?或者 pm.makePersistnace() 的实现实际上覆盖了整个对象......很高兴知道......

4

2 回答 2

1

这是你想象中发生的事情吗?

  1. Alice 检索对象。{ a = 1, b = "Foo" }
  2. Bob 检索对象。{ a = 1, b = "Foo" }
  3. Alice 通过改变 b 来修改对象。{ a = 1, b = "酒吧" }
  4. Bob 通过更改 a 来修改对象。{ a = 2, b = "Foo" }
  5. Alice 保留了她的对象副本。{ a = 1, b = "酒吧" }
  6. Bob 保留他的对象副本。{ a = 2, b = "Foo" }

Bob 的对象副本将覆盖数据存储中的副本,因为他保留了他的整个 object,而不仅仅是他的更改字段集。或者,一般来说,无论它们中的哪一个最后持久化,它们的整个对象都持久化在数据存储中。

您可以通过在事务中运行每个 get-set-and-persist 操作来解决此问题。App Engine 事务不会锁定整个对象以防止在本地检索或修改,它们只是阻止其他用户持久保存。所以:

  1. Alice 在事务中检索对象。{ a = 1, b = "Foo" }
  2. Bob 在事务中检索对象。{ a = 1, b = "Foo" }
  3. Alice 通过改变 b 来修改对象。{ a = 1, b = "酒吧" }
  4. Bob 通过更改 a 来修改对象。{ a = 2, b = "Foo" }
  5. Alice试图持久化该对象,但不能,因为 Bob 在一个事务中打开了它。将引发异常,Alice 将通过结束她的事务并重试来捕获该异常......
  6. Bob 将对象持久化,没有任何问题,因为 Alice 已经完成了他的交易 { a = 2, b = "Foo" }
  7. Alice 通过再次检索来重试她的交易。{ a = 2, b = "Foo" }
  8. Alice 通过改变 b 来修改对象。{ a = 2, b = "酒吧" }
  9. Alice 持久化了该对象,它之所以有效,是因为没有其他人打开交易。{ a = 2, b = "酒吧" }

我不确定哪个用户会得到异常,但只要他们愿意在看到异常时重试,他们最终都可以对对象进行更改并持久化。

这称为乐观锁定

于 2010-04-07T15:37:59.990 回答
0

感谢您的回答。遗憾的是 makePersistence() 实现是将整个对象写入数据存储区,而不仅仅是写入已更改的字段。这一事实实际上迫使 GAE 中的任何共享对象更新都使用事务作为规则。此外 - 在这种情况下,您必须实施“重试机制”,因为事务中可能会发生异常。

所以......更新GAE中的任何共享对象应该总是有这些额外的:

  • 在事务中执行
  • 实施重试机制

Google 在其网站中的大多数示例实际上都没有考虑到这一点。好像他们假设大多数应用程序不会使用共享对象

例如(http://code.google.com/appengine/docs/java/datastore/creatinggettinganddeletingdata.html):

public void updateEmployeeTitle(User user, String newTitle) {
    PersistenceManager pm = PMF.get().getPersistenceManager();
    try {
        Employee e = pm.getObjectById(Employee.class, user.getEmail());
        if (titleChangeIsAuthorized(e, newTitle) {
            e.setTitle(newTitle);
        } else {
            throw new UnauthorizedTitleChangeException(e, newTitle);
        }
    } finally {
        pm.close();
    }
}

或者:

public void updateEmployeeTitle(Employee e, String newTitle) {
    if (titleChangeIsAuthorized(e, newTitle) {
        e.setTitle(newTitle);
        PersistenceManager pm = PMF.get().getPersistenceManager();
        try {
            pm.makePersistent(e);
        } finally {
            pm.close();
        }
    } else {
        throw new UnauthorizedTitleChangeException(e, newTitle);
    }
}
于 2010-04-07T16:40:01.733 回答