【问题标题】:How does JPA cache refreshes entities externally modified?JPA缓存如何刷新外部修改的实体?
【发布时间】:2014-12-02 22:48:52
【问题描述】:

我之前遇到过 JPA 的问题。 我有两个应用程序:第一个使用 Java/JPA (EclipseLink),第二个使用 PHP。这两个应用程序可以访问同一个数据库。 现在,我通过 Java 访问“Expedition”对象,然后通过 Web 服务调用 PHP 应用程序(应该修改共享数据库表“Expedition”中该对象的属性),然后通过访问该属性Java 应用程序。

问题是,对象似乎没有在 Java 应用程序中被修改,即使它在数据库中被修改。我在考虑缓存问题。

原代码(简体):

System.out.println(expedition.getInfosexpedition()); // null

// Calling the web-service (modification of the "expedition" object in the database)
this.ec.eXtractor(expedition);

System.out.println(expedition.getInfosexpedition()); // Still null, should not be

“Expedition”和“Infosexpedition”类的定义:

远征:

@Entity
@Table(name = "expedition")
@XmlRootElement
public class Expedition implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "idExpedition")
    private Integer idExpedition;
    @OneToOne(cascade = CascadeType.ALL, mappedBy = "idExpedition")
    @XmlTransient
    private Infosexpedition infosexpedition;

信息远征:

@Entity
@Table(name = "infosexpedition")
@XmlRootElement
public class Infosexpedition implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "idInfoExpedition")
    private Integer idInfoExpedition;
    @JoinColumn(name = "idExpedition", referencedColumnName = "idExpedition")
    @OneToOne(optional = false)
    @XmlTransient
    private Expedition idExpedition;

我已经能够通过这样做使原始代码工作:

System.out.println(expedition.getInfosexpedition()); // null

// Calling the web-service (modification of the "expedition" object in the database)
this.ec.eXtractor(expedition);

try
{
    // Getting explicitly the "infosExpedition" item through a simple named request
    Infosexpedition infos = this.ec.getFacade().getEm().createNamedQuery("Infosexpedition.findByIdExpedition", Infosexpedition.class)
           .setParameter("idExpedition", expedition)
           .setHint("eclipselink.refresh", "true")
           .setHint("eclipselink.cache-usage", "DoNotCheckCache")
           .setHint("eclipselink.read-only", "true") // This line did the trick
           .getSingleResult();
     expedition.setInfosexpedition(infos);
}
catch (NoResultException nre) {}

System.out.println(expedition.getInfosexpedition()); // Not null anymore, OK

我试图了解这里发生了什么,以及为什么我必须指定一个“只读”提示才能使这项工作发挥作用……在此之前,我尝试了几乎所有方法,从 evictAll() 调用到 @987654326 @/merge() 调用,但没有任何效果。

有人可以帮助我了解不同级别的缓存在这里是如何工作的吗?为什么我新创建的行是“只读的”?

非常感谢。

【问题讨论】:

  • 您尚未展示有关如何管理 EntityManager 的代码,但 JPA 中有 2 级缓存。第二个级别是 EMF 级别,而第一个级别是 EntityManager 本身,保留对每个托管对象的引用,以便它可以跟踪更改并保持身份。您可能只保留一个 EM,而不是在完成后释放它 - 它们的目的是根据需要获取,就像您进行交易一样。
  • 我正在使用 JTA 进行事务处理,每次创建/编辑/删除都是通过调用 MyClass.getEntityManager().persist()/merge()/remove() 进行的,如果我理解这一点,鉴于“远征”是我方法的参数(而且我不知道这个参数是如何获得的),如果this.getEntityManager()不是允许我检索我的“远征”对象的经理,我就不能写this.getEntityManager().refresh(expedition)之类的东西?
  • 我的观点不是围绕事务处理,而是围绕 EntityManager 生命周期。但我假设如果您使用的是 JTA,那么您可能也会使用容器管理的 EM。当您持久化/合并/查找一个实体时,它会将其加载到当前的 EntityManager 缓存中。该引用在关闭或清除之前不会被释放,如下面的答案中所述。也可以刷新。 em.refresh(expedition) 仅在远征是该 EntityManager 中的托管实体时才有效。如果不是,那么 expedition=em.find(expedition.getId());em.refresh(expedition) 可能会起作用
  • 但是你需要看看你的探险队来自哪里。你是怎么读到它的?
  • JTA:UserManaged 还是 ContainerManaged?您如何管理您的交易?

标签: java jakarta-ee caching jpa eclipselink


【解决方案1】:

您正在使用的设置试图绕过缓存。 ("eclipselink.read-only", "true") 使其绕过一级缓存,而 ("eclipselink.cache-usage", "DoNotCheckCache") 使查询转到数据库而不是从二级缓存。最后 ("eclipselink.refresh", "true") 刷新共享缓存中的数据,而不是返回预建对象。即使您对请求之间的对象进行了更改,您的外观也必须对两个请求使用相同的 EntityManager。如 cmets 中所述,EntityManager 旨在用作事务,以便您与事务期间所做的更改隔离。如果这对您不起作用,您应该在第一次调用后清除或释放 entityManager,以便可以接听 Web 服务修改后的调用。

如果此之外的应用程序将频繁更改数据,您可能需要查看禁用共享缓存,如下所述: https://wiki.eclipse.org/EclipseLink/FAQ/How_to_disable_the_shared_cache%3F

并且还实现了乐观锁定,以防止任一应用程序用过时的数据覆盖另一个应用程序,如下所述: https://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Mapping/Locking/Optimistic_Locking

【讨论】:

  • +1 用于禁用 l2 缓存,不知道它在 eclipse 链接上默认启用
  • 感谢您对这些设置的解释。我更好地理解了为什么我的 EntityManager 看不到对数据库所做的任何更改。
【解决方案2】:

您所说的缓存是一级缓存,即在时间 t 的数据库状态在内存中的投影。

此“缓存”与实体管理器本身具有相同的生命周期,并且通常不会刷新,直到您明确清除它(使用 myEntityManager.clear())(您不应该)或强制它刷新特定实体实例(使用myEntityManager.refresh(myEntityInstance),这是你应该走的路)

更详细的解释见Struggling to understand EntityManager proper useJpa entity lifecycle

【讨论】:

  • 我尝试使用myEntityManager.refresh(expedition)myEntityManager.detach(expedition)...myEntityManager.merge(expedition),两者都不起作用:(
  • refresh 完全符合您的要求,如果从底层数据库连接中可以看到更新,那么它将起作用。也许是事务隔离问题。如何获取 entityManager 实例?
  • 进程是一个 Cron 任务,每小时执行一次。这个 Cron 任务定义在一个类中,用 @Singleton 注释,我在其中注入了一个 ExpeditionController,它有一个属性 entityManager
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-09
  • 1970-01-01
  • 2011-08-06
  • 1970-01-01
  • 2019-08-24
  • 2012-08-31
相关资源
最近更新 更多