5

我面临的问题类似于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();
    }
[...]

(代码释义)。

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

我们尝试过的

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

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

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

问题:

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

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

4

4 回答 4

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 中)
于 2013-01-22T15:33:36.723 回答
2
  1. 创建 newEntityManager是正确的,另一个不是(见下文)。

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

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

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

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

于 2013-01-07T19:36:30.610 回答
1

要禁用 EclipseLink 中的共享缓存,请参阅,

http://wiki.eclipse.org/EclipseLink/FAQ/How_to_disable_the_shared_cache%3F

于 2013-01-08T14:45:58.820 回答
0

这会变魔术 entityManager.getEntityManagerFactory().getCache().evict(MyDTO.class);

于 2017-04-11T11:43:21.527 回答