使用 JPA 时避免来自 DB 的陈旧数据?

sle*_*ske 5 database spring caching jpa eclipselink

我面临一个类似于Invalidating JPA EntityManager session 中描述的问题:

问题:获取陈旧数据

我们正在 SQL 数据库上运行 JPQL 查询,该数据库也由不同的应用程序同时更改。我们使用的是在Tomcat下运行的JSF+Spring+EclipseLink。

执行 JPQL 查询的类是一个单例 Spring bean,并使用注入的EntityManager

@Component
public class DataRepository{
    @PersistenceContext
    private EntityManager entityManager;
    public List<MyDTO> getStuff(long id) {
        String jpqlQuery ="SELECT new MyDTO([...])";
        TypedQuery<MyDTO> query = entityManager.createQuery(jpqlQuery,MyDTO.class);
        return query.getResultList();
    }
[...]
Run Code Online (Sandbox Code Playgroud)

(代码释义)。

问题是这段代码看不到直接对数据库执行的更改。这些更改仅在 Tomcat 实例重新启动时才可见。

我们尝试过的

我们假设此行为是由与 关联的一级缓存引起的EntityManager,如链接问题中所述。我们找到了两个解决方案:

  • 调用entityManager.clear()调用之前createQuery(这是建议的链接的问题)
  • 注入一个EntityManagerFactor(使用@PersistenceUnit),然后EntityManager为每个查询创建并关闭一个新的

两种解决方案都能满足我们的需求——我们获得了新的数据。

问题:

  • 这两种解决方案是否正确?哪一个更好?
  • 特别是,我们是否可以安全地调用entityManager.clear()注入的 EntityManager,或者这是否会以某种方式影响也使用注入的 EntityManager 的其他代码(在相同或不同的类中)?
  • 有没有不同的、更好的方法?我们能不能以某种方式声明我们想要刷新缓存,或者我们想要一个新的EntityManager

我认为这一定是一个相当普遍的问题(因为它发生在多个应用程序共享一个数据库时),所以我认为必须有一个简单的解决方案......

sle*_*ske 5

看起来我们已经解决了问题。

其实有两个问题:

  1. 在调试问题时,在某些情况下,我们没有为每个查询使用新的 EntityManager。当使用和重用同一个 EntityManager 时,实体一旦被检索到显然永远不会被刷新(除非使用 显式刷新EntityManager.refresh())。这显然是因为一旦一个实体被加载,它就会被存储在 EntityManager 的持久化上下文(又名一级缓存)中。这样做是必要的,因为 JPA 规范要求对同一实体的后续查询返回相同的对象实例。换句话说:只要您使用相同的 EntityManager,除非您明确刷新,否则您将获得陈旧数据。

  2. 当我们没有使用新的EntityManager对于每个查询,事情总是很。但是,我们在 EclipseLink 中遇到了一个错误,其中为某些 JPQL 查询(涉及构造函数表达式和 JOIN FETCH)返回陈旧数据。

精简版

如果您想始终从从程序外部修改的数据库中获取新数据,那么

  • 为每个需要新数据的查询使用新的 EntityManager
  • 禁用二级缓存(<shared-cache-mode>NONE</shared-cache-mode>在persistence.xml中)


Mar*_*tör 2

  1. 创建 newEntityManager是正确的,另一个则不正确(见下文)。

  2. EntityManager#clear()除了单线程系统之外,调用在所有系统中都是非常危险的。

    ...导致所有托管实体变得分离...

    因此,如果一个线程与附加实体一起工作,而“您的”线程清除实体管理器,则会产生严重的副作用。

  3. 嗯,很难说。如果在应用程序外部修改的实体数量很少,我将直接使用数据源和 aJdbcTemplate来执行相应的操作。

  • 该链接意味着您处于事务中,否则代理每次操作都会使用新的 EM。如果这是正确的,您可以在事务之外执行读取以获得更好的性能。另请注意 EclipseLink 有一个共享的二级缓存;验证您正在使用隔离的缓存设置 (2认同)