java - 使用 JPA 第二次读取对象不会更新它

标签 java jpa glassfish eclipselink

我有一个小型网络应用程序;下面是其简化的可运行版本。

在每个 http 请求中,我都会从数据库中读取一个对象,每次都相同 并用它来显示页面的内容。发布到后的第一个请求 Glassfish ,一切都很好。但是,如果我更改数据库中的值, 此更改不会反射(reflect)在下一个请求中。

与获取实体相关的代码:

// @WebServlet MyServlet
@Inject
private MyEJB ejb;

// MyServlet.doGet()
MyEntity e = ejb.getResource(1);
System.out.printf("%s: %s%n", e, e.getValue());

// @Stateless MyEJB
@PersistenceContext
private EntityManager em;

// MyEJB.getResource()
TypedQuery<MyEntity> q = em.createNamedQuery("MyEntity.selectById", MyEntity.class);
q.setParameter("id", id);
MyEntity e = q.getSingleResult();
return e;

在请求之间将值更改为 asdf!doGet() 中的日志输出:

Information: pkg.model.MyEntity@7bc0b80e: asdf
Information: pkg.model.MyEntity@5543a940: asdf

显然,实体在请求之间被缓存,尽管不同的对象是 回。在查询之前或之后在 getResource刷新实体管理器 没有什么区别,我不明白。 刷新实体确实如此 工作,但不是递归的,并且不涵盖肯定会出现的其他需求 在原来的应用程序中。我在尝试其他事情时遇到的异常证实了 EntityManager 是由 JTA 管理的,正如我所预期的那样。

我使用 GlassFish Server Open Source Edition 4.0 和默认的 EclipseLink ORM。

我认为三个选项可以满足我的需求:

  • Glassfish 控制台或某些 WEB-INF/META-INF 文件或类似文件中的配置
  • 在实际处理每个请求之前我可以执行一些代码,确保 已缓存的对象与数据库是最新的
  • 执行此操作的不同注入(inject)方式,或者将缓存限制为 每个请求基础

我怎样才能实现其中之一,或者它是我实际需要的不同东西?


完整代码如下

package pkg.model;

import javax.persistence.Basic;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;

@Entity
@Table(schema = "MySchema")
@NamedQueries({
    @NamedQuery(name = "MyEntity.selectById",
            query = "SELECT e FROM MyEntity e WHERE e.id = :id")
})
public class MyEntity {
    private Long   id;
    private String value;

    @Id
    @GeneratedValue
    public Long getId() {
        return id;
    }

    @SuppressWarnings("unused")
    private void setId(Long id) {
        this.id = id;
    }

    @Basic
    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }
}

package pkg;

import java.io.IOException;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import javax.servlet.ServletException;
import pkg.model.MyEntity;

@Stateless
public class MyEJB {
    @PersistenceContext
    private EntityManager em;

    public MyEntity getResource(long id) throws ServletException, IOException {
        TypedQuery<MyEntity> q = em.createNamedQuery("MyEntity.selectById", MyEntity.class);
        q.setParameter("id", id);
        MyEntity e = q.getSingleResult();
        return e;
    }
}

package pkg;

import java.io.IOException;
import java.io.PrintWriter;
import javax.inject.Inject;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import pkg.model.MyEntity;

@WebServlet(name = "MyServlet", urlPatterns = "/")
public class MyServlet extends HttpServlet {
    @Inject
    private MyEJB ejb;

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        MyEntity e = ejb.getResource(1);
        System.out.printf("%s: %s%n", e, e.getValue());
        resp.setContentType("text/html;charset=UTF-8");
        try (PrintWriter out = resp.getWriter();) {
            out.print(e.getValue());
        }
    }
}

此外,还有一个空的 beans.xml 和一个普通的 web.xml(只是一个 welcome-file-list)

最佳答案

By default EclipseLink enables a shared object cache to cache objects read from the database to avoid repeated database access. If the database is changed directly through JDBC, or by another application or server, the objects in the shared cache will be stale.

有不同的选项可以禁用缓存或强制刷新。

可以通过将以下内容添加到 persistence.xml 来禁用普通共享缓存:

<property name="eclipselink.cache.shared.default" value="false"/>

当使用 NamedQueries 时,就像您一样,您可能还需要通过添加以下内容来禁用查询缓存:

<property name="eclipselink.query-results-cache" value="false"/>
<property name="eclipselink.refresh" value="true"/>

另一种更详细的禁用缓存的方法是通过查询提示。这可以通过以下方式完成:

TypedQuery<MyEntity> q = em.createNamedQuery("MyEntity.selectById", MyEntity.class);
q.setHint(QueryHints.CACHE_USAGE, CacheUsage.DoNotCheckCache);
// or 
q.setHint("javax.persistence.cache.storeMode", "REFRESH");

EclipseLink wiki 中有很多有关缓存的附加信息:

另请参阅:

关于java - 使用 JPA 第二次读取对象不会更新它,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24744133/

相关文章:

Java JPA - 同步数据库和实体

Glassfish 和 java :comp/BeanManager

jsf - 正确设置 HTTP header 以强制缓存 JS、CSS 和 PNG 文件

java - MySQLSyntaxErrorException 尽管一切看起来都很好(Hibernate)

java - MySQL 或 Hibernate 缓存问题接收旧数据

java - 为什么在一个 case block 中定义的变量在其他 case block 中可用?

jpa - JDODetachedFieldAccessException : You have just attempted to access field "attachment" yet this field was not detached when you detached the object

tomcat - 本地主机在 Tomcat 启动后未被解析,而是显示奇怪的 GlassFish 服务器消息

java - OSGi - 获取接口(interface)的实现

java - 如何从货币代码中获取国家代码/名称