【问题标题】:JPA: cache queries only if resultset is not emptyJPA:仅当结果集不为空时才缓存查询
【发布时间】: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;

我希望实现的目标是仅在查询结果非空时才缓存查询结果。

我敢肯定,因为我在跟踪级别通过休眠日志记录检查了它,所以无论如何都会缓存空结果集。

任何建议都将不胜感激。

问候!

【问题讨论】:

  • 不幸的是,我认为这个解决方案不适合我,因为在那种情况下,我应该只驱逐与特定查询参数值相关的空条目。
  • 但是如果你使用缓存区域,你可以通过evictQueryRegion(String regionName)来驱逐这个区域
  • 我明白,但我们假设我使用param-value-1 执行查询,它会得到一个非空的结果集。然后我用param-value-2 再次执行它,得到一个空的结果集。因此,我在同一个区域中有一个非空结果集和一个空结果集。如果我驱逐整个区域,我也会丢失非空结果集。对吗?
  • 你能补充一点为什么你不想缓存空结果吗?

标签: java hibernate jpa caching ehcache


【解决方案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;
        }
    }
}

【讨论】:

    猜你喜欢
    • 2020-10-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-06
    • 2021-09-07
    • 2021-03-15
    • 2014-01-25
    相关资源
    最近更新 更多