【问题标题】:Reading object a second time using JPA doesn't update it使用 JPA 再次读取对象不会更新它
【发布时间】:2014-09-04 19:38:06
【问题描述】:

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

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

获取实体的相关代码:

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

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

我正在使用带有默认 EclipseLink ORM 的 GlassFish Server Open Source Edition 4.0。

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

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

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


完整代码如下

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

【问题讨论】:

    标签: java jpa glassfish eclipselink


    【解决方案1】:

    默认情况下,EclipseLink 启用共享对象缓存来缓存对象 从数据库中读取以避免重复的数据库访问。如果 数据库直接通过 JDBC 或其他应用程序更改 或服务器,共享缓存中的对象将是陈旧的。

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

    可以通过将以下内容添加到您的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 中有很多关于缓存的附加信息:

    另请参阅:

    【讨论】:

    • 我现在最终使用了第一个解决方案。由于大部分数据库很可能只能由我的 Web 应用程序修改,因此我稍后会重新访问。到目前为止,我还没有遇到需要添加query-results-cacherefresh 属性的情况。您提供的链接是很好的资源!谢谢!
    猜你喜欢
    • 1970-01-01
    • 2014-10-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-17
    • 1970-01-01
    • 1970-01-01
    • 2021-01-10
    相关资源
    最近更新 更多