简体   繁体   English

JPA缓存如何刷新外部修改的实体?

[英]How does JPA cache refreshes entities externally modified?

I faced an issue earlier with JPA. 我早些时候遇到过JPA问题。 I have two apps : the main one, using Java/JPA (EclipseLink), and a second one, using PHP. 我有两个应用程序:主要使用Java / JPA(EclipseLink),第二个使用PHP。 The two apps have access to the same database. 这两个应用程序可以访问同一个数据库。 Now, I'm accessing an "Expedition" object through Java, then calling the PHP app through a web-service (which is supposed to modify an attribute of this object in the shared database table "Expedition"), then accessing this attribute through the Java app. 现在,我通过Java访问“Expedition”对象,然后通过Web服务调用PHP应用程序(应该在共享数据库表“Expedition”中修改此对象的属性),然后通过Java应用程序。

Problem is, the object seems not to be modified in the Java app, even if it is modified in the database. 问题是,即使在数据库中修改了对象,该对象似乎也不会在Java应用程序中进行修改。 I'm thinking about a cache problem. 我正在考虑缓存问题。

The original code (simplified) : 原始代码(简化):

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

Definitions of the "Expedition" and "Infosexpedition" classes : “远征”和“Infosexpedition”类的定义:

Expedition : 远征:

@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;

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;

I've been able to make the original code work by doing this : 我已经能够通过这样做使原始代码工作:

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

I'm trying to understand what happens here, and why did I had to specify a "read-only" hint to make this work... Before that, I tried almost everything, from evictAll() calls to detach() / merge() calls, and nothing worked. 我试图理解这里发生了什么,为什么我必须指定一个“只读”提示才能使这个工作......在那之前,我尝试了几乎所有东西,从evictAll()调用到detach() / merge()调用,没有任何效果。

Can someone help me to understand how the different levels of cache worked here ? 有人可以帮助我理解不同级别的缓存如何在这里工作? And why is my newly created line "read-only" ? 为什么我新创建的行“只读”?

Thanks a lot. 非常感谢。

The settings you are using are attempting to bypass the cache. 您正在使用的设置正在尝试绕过缓存。 ("eclipselink.read-only", "true") causes it to bypass the first level cache, while the ("eclipselink.cache-usage", "DoNotCheckCache") makes the query go to the database instead of pulling data from the second level cache. (“eclipselink.read-only”,“true”)导致它绕过第一级缓存,而(“eclipselink.cache-usage”,“DoNotCheckCache”)使查询转到数据库而不是从数据库中提取数据二级缓存。 Finally ("eclipselink.refresh", "true") refreshes the data in the shared cache rather then return the prebuilt object. 最后(“eclipselink.refresh”,“true”)刷新共享缓存中的数据,而不是返回预构建的对象。 Your facade must be using the same EntityManager for both requests even though you have made changes to the objects between the requests. 即使您已对请求之间的对象进行了更改,您的外观也必须对两个请求使用相同的EntityManager。 As mentioned in the comments, an EntityManager is meant to be used as a transaction would, so that you are isolated from changes made during your transactions. 正如评论中所提到的,EntityManager旨在用作事务,因此您可以与事务期间所做的更改隔离开来。 If this doesn't work for you, you should clear or release the entityManager after the first call, so that the calls after the web-service modifications can be picked up. 如果这对您不起作用,则应在第一次调用后清除或释放entityManager,以便可以拾取Web服务修改后的调用。

If applications outside this one are going to be making data changes frequently, you might want to look at disabling the shared cache as described here: https://wiki.eclipse.org/EclipseLink/FAQ/How_to_disable_the_shared_cache%3F 如果这个以外的应用程序要经常更改数据,您可能需要查看如下所述禁用共享缓存: https//wiki.eclipse.org/EclipseLink/FAQ/How_to_disable_the_shared_cache%3F

And also implement optimistic locking to prevent either application from overwriting the other with stale data as described here: https://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Mapping/Locking/Optimistic_Locking 并且还实现乐观锁定以防止应用程序用如下所述的陈旧数据覆盖另一个: https//wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Mapping/Locking/Optimistic_Locking

What you call cache is the 1st level cache, id est the in memory projection of the database state at a time t. 你称之为缓存的是第一级缓存,即在时间t的数据库状态的内存投影中。

This "cache" has the same lifecycle that the entity manager itself and generally won't be refreshed until you explicitely clear it (using myEntityManager.clear() ) (you shouldn't) or force it to refreh a specific entity instance (using myEntityManager.refresh(myEntityInstance) , this is the way you should go) 这个“缓存”具有与实体管理器本身相同的生命周期,并且通常不会刷新,直到您明确地清除它(使用myEntityManager.clear() )(您不应该)或强制它重新刷新特定实体实例(使用myEntityManager.refresh(myEntityInstance) ,这是你应该去的方式)

See Struggling to understand EntityManager proper use and Jpa entity lifecycle for a more detailed explanation 有关更详细的说明,请参阅Struggling以了解EntityManager的正确使用Jpa实体生命周期

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM