JPA:仅当结果集不为空时才缓存查询
Posted
技术标签:
【中文标题】JPA:仅当结果集不为空时才缓存查询【英文标题】:JPA: cache queries only if resultset is not empty 【发布时间】:2021-12-06 10:24:57 【问题描述】:我正在使用 JPA 2.1 + Hibernate + EHCache。
这是我的命名查询(查询代码不相关):
List<MyEntity> list = getEntityManager()
.createNamedQuery("my-query-id", MyEntity.class))
.setHint(QueryHints.CACHEABLE, true)
.setHint(QueryHints.CACHE_REGION, "my-query-region")
.setParameter("my-query-param", "my-param-value")
.setMaxResults(1)
.getResultList();
if (list.isEmpty())
log.warn("No data found.");
return null;
return list;
我希望实现的目标是仅在查询结果非空时才缓存查询结果。
我敢肯定,因为我在跟踪级别通过休眠日志记录检查了它,所以无论如何都会缓存空结果集。
任何建议都将不胜感激。
问候!
【问题讨论】:
我认为你可以直接驱逐缓存docs.jboss.org/hibernate/orm/4.3/javadocs/org/hibernate/… 不幸的是,我认为这个解决方案不适合我,因为在那种情况下,我应该只驱逐与特定查询参数值相关的空条目。 但是如果你使用缓存区域,你可以通过evictQueryRegion(String regionName)
来驱逐这个区域
我明白,但我们假设我使用param-value-1
执行查询,它会得到一个非空的结果集。然后我用param-value-2
再次执行它,得到一个空的结果集。因此,我在同一个区域中有一个非空结果集和一个空结果集。如果我驱逐整个区域,我也会丢失非空结果集。对吗?
你能补充一点为什么你不想缓存空结果吗?
【参考方案1】:
我通过编写 EHCache 装饰器找到了解决方案,如下所示:
EHCache XML 配置片段
<cache name="my-queries-region"
maxEntriesLocalHeap="50000"
eternal="false"
timeToLiveSeconds="14400">
<persistence strategy="none"/>
<!-- https://www.ehcache.org/ehcache.xml -->
<cacheDecoratorFactory
class="com.example.JpaCacheDecoratorNotEmptyQueryFactory" />
</cache>
装饰器工厂实现
package com.example;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.constructs.CacheDecoratorFactory;
import java.util.Properties;
public class JpaCacheDecoratorNotEmptyQueryFactory extends CacheDecoratorFactory
@Override
public Ehcache createDecoratedEhcache(Ehcache cache, Properties properties)
return new JpaCacheDecoratorNotEmptyQueryDecorator(cache);
@Override
public Ehcache createDefaultDecoratedEhcache(Ehcache cache, Properties properties)
return new JpaCacheDecoratorNotEmptyQueryDecorator(cache);
装饰器实现
package com.example;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import net.sf.ehcache.CacheException;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Element;
import net.sf.ehcache.constructs.EhcacheDecoratorAdapter;
import org.hibernate.cache.internal.QueryResultsCacheImpl;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.List;
@Slf4j
public class JpaCacheDecoratorNotEmptyQueryDecorator extends EhcacheDecoratorAdapter
private final Field resultsField;
@SneakyThrows
@SuppressWarnings("rawtypes")
protected boolean canCache(Element element)
boolean cacheable = true;
Object value = element.getObjectValue();
if (value instanceof QueryResultsCacheImpl.CacheItem)
List results = (List)resultsField.get(value);
cacheable = !results.isEmpty();
if (!cacheable)
if (log.isDebugEnabled())
log.debug("Query not cacheable due to empty result set.");
return cacheable;
protected boolean canCache(Collection<Element> elements)
for (Element element: elements)
if (!canCache(element))
return false;
return true;
@SneakyThrows
public JpaCacheDecoratorNotEmptyQueryDecorator(Ehcache underlyingCache)
super(underlyingCache);
resultsField = QueryResultsCacheImpl
.CacheItem
.class
.getDeclaredField("results");
resultsField.setAccessible(true);
@Override
public void put(Element element, boolean doNotNotifyCacheReplicators)
throws IllegalArgumentException,
IllegalStateException,
CacheException
if (canCache(element))
super.put(element, doNotNotifyCacheReplicators);
@Override
public void put(Element element)
throws IllegalArgumentException,
IllegalStateException,
CacheException
if (canCache(element))
super.put(element);
@Override
public void putAll(Collection<Element> elements)
throws IllegalArgumentException,
IllegalStateException,
CacheException
if (canCache(elements))
super.putAll(elements);
@Override
public void putQuiet(Element element)
throws IllegalArgumentException,
IllegalStateException,
CacheException
if (canCache(element))
super.putQuiet(element);
@Override
public void putWithWriter(Element element)
throws IllegalArgumentException,
IllegalStateException,
CacheException
if (canCache(element))
super.putWithWriter(element);
@Override
public Element putIfAbsent(Element element)
throws NullPointerException
if (canCache(element))
return super.putIfAbsent(element);
else
return null;
@Override
public Element putIfAbsent(Element element, boolean doNotNotifyCacheReplicators)
throws NullPointerException
if (canCache(element))
return super.putIfAbsent(element, doNotNotifyCacheReplicators);
else
return null;
【讨论】:
以上是关于JPA:仅当结果集不为空时才缓存查询的主要内容,如果未能解决你的问题,请参考以下文章