如何从数据库中正确刷新陈旧实体?

时间:2013-08-14 09:44:18

标签: java jpa entitymanager

在我的Web应用程序中,我经常需要在单个请求的范围内保持从数据库中读取实体,并在稍后的另一个请求中刷新它们。目前我有一个辅助方法

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

每个方法都会在其他任何方法之前刷新它正在处理的实体。如果实体已经刷新,则没有任何反应。

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

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

  1. 不是拨打mergerefresh,而是获取实体的ID并致电

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

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

  2. 长期保留ID,并在每次请求期间读取新鲜实体。从设计的角度来看,这似乎更正确(陈旧的实体无论如何都带有无效的信息),但同样需要手头有T的类。
  3. 更新:我将实体保留在请求中的原因仅仅是为了方便起见。我特别不需要确保在此期间不修改实体 - 无论如何我会在下一个请求时刷新它。

3 个答案:

答案 0 :(得分:1)

你的#1应该有用。

object.getClass()适用于该课程,您可以使用emf.getPersistenceUnitUtil().getIdentifier(object)

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

答案 1 :(得分:0)

我认为你在描述optimistic locking

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

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

答案 2 :(得分:0)

如果您尝试从数据库刷新实体,那么您可能做错了。

您的实体不应超过要求的时间。您的JPA提供程序应该处理缓存,因此重新调用相同的查询并再次检索同一个实体应该不是一件容易的事。因此,将您的实体存储在@RequestScoped,@ ConversationScoped和类似范围内。

另一方面,如果您确实需要实体更长的时间,您应该考虑锁定。正如之前的海报所解释的那样,您可以这样做:

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

在此处详细了解锁定和JPA:http://city81.blogspot.com/2011/03/pessimistic-and-optimistic-locking-in.html