有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java如何正确地从数据库中刷新过时的实体?

在我的web应用程序中,我经常需要在单个请求期间保持从数据库读取的实体,然后在另一个请求中刷新它们。目前我有一个助手方法

protected <T> T refresh(T t) {
    if (!entityManager.contains(t)) {
        t = merge(t);
        entityManager.refresh(t);
    }
    return t;
}

他们使用的每个方法都会先刷新正在处理的实体。如果实体已经刷新,则不会发生任何事情

这很有效,直到后来我意识到这个解决方案有一个重大问题:如果实体在数据库中被并发修改,那么对merge的调用就会失败显然merge无法“知道”t的内容将被立即调用refresh替换

这个问题的正确解决方案是什么?到目前为止,我有这些想法,但没有一个让我感到完全满意:

  1. 不要调用mergerefresh,而是获取实体的ID并调用

    t = find(theClassOfT, t.getId());
    

    这将解决问题,我会得到一个完全新鲜的实体,但它有一个主要缺点:我需要知道T的类及其ID。获取ID可以通过为我的所有实体提供一个顶级超级接口来完成,但是获取类是有问题的(因为JPA实现可能会对实体进行子类化,我担心调用t.getClass()可能会返回一些特定于实现的T子类)

  2. 长期只保留ID,并在每次请求期间读取最新的实体。从设计的角度来看,这似乎更为正确(过时的实体仍然携带无效信息),但同样需要手头有T

更新:我在请求中保留实体只是为了方便。我特别不需要确保实体在这段时间内没有被修改——反正我会在下一个请求时刷新它


共 (3) 个答案

  1. # 1 楼答案

    我想你在描述optimistic locking

    一种方法是锁定实体,直到客户端提交更改——这将降低tour应用程序的吞吐量

    或者使用乐观锁定——您可以希望对象不会更改,将版本标识符放在实体中。保存对象后,将根据数据库中的当前状态检查该数字,如果该数字不匹配,因为另一个客户端更改了该实体,则此并发更新将失败。一旦并发修改发生,应用程序必须能够解决该状态

  2. # 2 楼答案

    如果试图从数据库中刷新实体,可能是做错了什么

    您的实体的寿命不应超过要求。您的JPA提供者应该处理缓存,因此再次调用相同的查询并检索相同的实体应该不会太麻烦。因此,将实体存储在@RequestScoped、@ConversationScoped和类似的作用域中

    另一方面,如果您确实需要较长时间的实体,则应该考虑锁定。如前一张海报所述,您可以:

    • 悲观锁定,在提交之前锁定实体并阻止其他人更新它
    • 乐观锁定,您希望实体不会更新,但save()可能会失败

    阅读更多关于锁定和JPA的信息,例如:http://city81.blogspot.com/2011/03/pessimistic-and-optimistic-locking-in.html

  3. # 3 楼答案

    你的#1应该有用

    object.getClass()应该适用于类,对于可以使用的Id emf.getPersistenceUnitUtil().getIdentifier(object)

    此外,还可以将属性Map传递给find()操作,并可以包含刷新查询提示(“eclipselink.refresh”=“true”或"javax.persistence.cache.storeMode", "REFRESH"),这可以避免潜在的2次查询