UserA 和 UserB 分别同时更改 objectA.filedA objectA.filedB。因为他们没有改变同一个领域,人们可能会认为没有重叠。真的吗?或者 pm.makePersistnace() 的实现实际上覆盖了整个对象......很高兴知道......
2 回答
这是你想象中发生的事情吗?
- Alice 检索对象。{ a = 1, b = "Foo" }
- Bob 检索对象。{ a = 1, b = "Foo" }
- Alice 通过改变 b 来修改对象。{ a = 1, b = "酒吧" }
- Bob 通过更改 a 来修改对象。{ a = 2, b = "Foo" }
- Alice 保留了她的对象副本。{ a = 1, b = "酒吧" }
- Bob 保留他的对象副本。{ a = 2, b = "Foo" }
Bob 的对象副本将覆盖数据存储中的副本,因为他保留了他的整个 object,而不仅仅是他的更改字段集。或者,一般来说,无论它们中的哪一个最后持久化,它们的整个对象都持久化在数据存储中。
您可以通过在事务中运行每个 get-set-and-persist 操作来解决此问题。App Engine 事务不会锁定整个对象以防止在本地检索或修改,它们只是阻止其他用户持久保存。所以:
- Alice 在事务中检索对象。{ a = 1, b = "Foo" }
- Bob 在事务中检索对象。{ a = 1, b = "Foo" }
- Alice 通过改变 b 来修改对象。{ a = 1, b = "酒吧" }
- Bob 通过更改 a 来修改对象。{ a = 2, b = "Foo" }
- Alice试图持久化该对象,但不能,因为 Bob 在一个事务中打开了它。将引发异常,Alice 将通过结束她的事务并重试来捕获该异常......
- Bob 将对象持久化,没有任何问题,因为 Alice 已经完成了他的交易 { a = 2, b = "Foo" }
- Alice 通过再次检索来重试她的交易。{ a = 2, b = "Foo" }
- Alice 通过改变 b 来修改对象。{ a = 2, b = "酒吧" }
- Alice 持久化了该对象,它之所以有效,是因为没有其他人打开交易。{ a = 2, b = "酒吧" }
我不确定哪个用户会得到异常,但只要他们愿意在看到异常时重试,他们最终都可以对对象进行更改并持久化。
这称为乐观锁定。
感谢您的回答。遗憾的是 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);
}
}