GAE更新同一实体的不同字段

发布于 2024-08-28 04:28:40 字数 147 浏览 8 评论 0原文

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

UserA and UserB are changing objectA.filedA objectA.filedB respectively and at the same time.
Because they are not changing the same field one might think that there are no overlaps.
Is that true?
or the implementation of pm.makePersistnace() actually override the whole object...
good to know...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

囚我心虐我身 2024-09-04 04:28:40

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

  1. 爱丽丝取回了物体。 { a = 1, b = "Foo" }
  2. Bob 检索该对象。 { a = 1, b = "Foo" }
  3. Alice 通过更改 b 来修改对象。 { a = 1, b = "Bar" }
  4. Bob 通过更改 a 来修改对象。 { a = 2, b = "Foo" }
  5. Alice 保留了该对象的副本。 { a = 1, b = "Bar" }
  6. Bob 保留他的对象副本。 { a = 2, b = "Foo" }

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

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

  1. Alice 在交易中检索对象。 { a = 1, b = "Foo" }
  2. Bob 在事务中检索对象。 { a = 1, b = "Foo" }
  3. Alice 通过更改 b 来修改对象。 { a = 1, b = "Bar" }
  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 = "Bar" }
  9. Alice 保留该对象,并且它有效,因为没有其他人打开交易。 { a = 2, b = "Bar" }

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

这称为乐观锁定

Is this what you're envisioning happening?

  1. Alice retrieves the object. { a = 1, b = "Foo" }
  2. Bob retrieves the object. { a = 1, b = "Foo" }
  3. Alice modifies the object, by changing b. { a = 1, b = "Bar" }
  4. Bob modifies the object, by changing a. { a = 2, b = "Foo" }
  5. Alice persists her copy of the object. { a = 1, b = "Bar" }
  6. Bob persists his copy of the object. { a = 2, b = "Foo" }

Bob's copy of the object will overwrite the copy in the datastore, because he's persisting his whole object, not just his set of changed fields. Or, in general, whichever of them persists last has their whole object persisted in the datastore.

You could fix this by running each of their get-set-and-persist operations in a transaction. App Engine transactions don't lock the whole object from being retrieved or modified locally, they just prevent other users from persisting. So:

  1. Alice retrieves the object in a transaction. { a = 1, b = "Foo" }
  2. Bob retrieves the object in a transaction. { a = 1, b = "Foo" }
  3. Alice modifies the object, by changing b. { a = 1, b = "Bar" }
  4. Bob modifies the object, by changing a. { a = 2, b = "Foo" }
  5. Alice tries to persist the object, but can't, because Bob has it open in a transaction. An exception will be thrown, which Alice will catch by ending her transcation and retrying...
  6. Bob persists the object, with no problems, because Alice has finished his transaction { a = 2, b = "Foo" }
  7. Alice retries her transaction by retrieving again. { a = 2, b = "Foo" }
  8. Alice modifies the object, by changing b. { a = 2, b = "Bar" }
  9. Alice persists the object, and it works because nobody else has a transaction open. { a = 2, b = "Bar" }

I'm not absolutely sure which user will get the exception, but as long as they're willing to retry when they see it, they'll both be able to make their changes to the object and persist them, eventually.

This is called Optimistic Locking.

请别遗忘我 2024-09-04 04:28:40

感谢您的回答。
遗憾的是 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);
    }
}

Thanks for your answer.
Pity that the makePersistence() implementation is to write the WHOLE object to the datastore and not only to the fields that were changed.
This fact is actually forcing ANY shared object update in GAE to use a transaction as a rule.
Further more - in such cases you must implement the "retry mechanism" as an exception in the transaction may occur.

So... updating any shared object in GAE should ALWAYS have these extras:

  • do it within a transaction
  • implement a retry mechanism

Most of Google's examples in their site are actually not taking that into account. As if they're assuming that most apps won't used shared objects

For example (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();
    }
}

OR:

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);
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文