我正在使用 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 缓存的另一个实例,但我不知道这怎么可能。