4

我正在使用 EclipseLink 的查询结果缓存:

 @NamedQuery(name = "User.findAll",
    query = "SELECT s FROM ...",
    hints= {
            @QueryHint(name=QueryHints.QUERY_RESULTS_CACHE, value=HintValues.TRUE)
            ...

我首先使用名为“CacheManager”的无状态bean手动清除缓存,该bean具有以下方法:

public void invalidateCacheForQuery(String namedQueryName) {
    log.info("CacheManagerService: clearing cache for named query " + namedQueryName);
     ((JpaCache) em.getEntityManagerFactory().getCache())
         .clearQueryCache(namedQueryName);
}

每当有可能使查询结果过时的修改(添加/删除)时,我都会调用此方法。调用是在修改后直接进行的,例如

em.merge(u);
cacheMan.invalidateCacheForQuery("..."); // for each cached query

(我在这里偷工减料,这是我所做的事情的本质)。

上面的解决方案效果很好——如果没有清除缓存,我的 JUnit 测试将失败,如果存在上述“invalidateCacheForQuery”调用,它们不会失败(如果不存在则失败)。

然后我试图通过定义一个拦截器来让我的生活更轻松:

@EJB CacheManager cacheMan;

@AroundInvoke
public Object clearQueryCacheAfterwards(InvocationContext ctx)
        throws Exception {

    // this does something like em.persist that might make cached query results
    // invalid:
    final Object result = ctx.proceed(); 

    // hera are calls cacheMan.invalidateCacheForQuery for all of the relevant named
    // queries. Yes, this is done after ctx.proceed().

    return result;
}

注意:我保持上面的代码简洁:拦截器注释接受一个类对象作为它的参数,我使用类对象来查找它的所有 NamedQueries 的名称,这些名称有一个 QueryHint 指示它们已被缓存。这只是为了解释我如何找到要清除的命名查询。

拦截器方法不起作用,我不知道为什么。我知道invalidateCacheForQuery(String namedQueryName)当我得到正确的日志消息时,也会使用拦截器方法(使用正确的命名查询名称)调用该方法。我还从拦截器中获取日志消息。因此拦截器机制正在工作,例如,它不是一个简单的问题,比如 beans.xml 中缺少定义。但是,我知道缓存清除不起作用,因为如果我根本没有清除缓存,我的 JUnit 测试会以同样的方式失败。

查看日志,这些连续的行让我确定拦截器正在做它应该做的事情:

ClearQueryCacheInterceptor: clearing query cache for named query User.findAll 
CacheManagerService: clearing cache for named query User.findAll

我还尝试@PersistenceContext(unitName="...") EntiytyManager em;在拦截器中使用并通过它清除缓存,而不使用 CacheManager EJB。

因此,回顾一下冗长的解释:我有两种方法调用相同的方法,使 EclipseLink 的查询结果缓存无效。我通过日志记录验证了这两种方法都设法使用相同的参数(NamedQuery 名称)调用该方法。内联“在 em.persist() 之后”方式有效,但在拦截器中执行基本完全相同的事情却没有。感觉就像我正在使用拦截器清除 EclipseLink 缓存的另一个实例,但我不知道这怎么可能。

4

1 回答 1

1

您的问题是您在使用 Interceptor 方法时正在处理两个不同的 PersistenceContexts。

看起来您的拦截器配置在实体级别上工作,它不共享 EJB 容器的 PersistenceContext。尝试将拦截器移动到您的服务层(将其应用于持久/修改数据的 EJB)。它不像您尝试做的那样自动,但应该可以工作。

于 2014-01-28T10:51:39.620 回答