JPA:仅当结果集不为空时才缓存查询
JPA: cache queries only if resultset is not empty
我在 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;
我希望实现的目标是仅在查询结果为非空时缓存查询结果。
我敢肯定,因为我在跟踪级别通过休眠日志记录检查了它,所以无论如何都会缓存空结果集。
如有任何建议,我们将不胜感激。
此致!
我通过编写 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 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;
我希望实现的目标是仅在查询结果为非空时缓存查询结果。
我敢肯定,因为我在跟踪级别通过休眠日志记录检查了它,所以无论如何都会缓存空结果集。
如有任何建议,我们将不胜感激。
此致!
我通过编写 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;
}
}
}