Как сообщить Spring кешу не кэшировать нулевое значение в аннотации @Cacheable

Есть ли способ указать, что если метод возвращает значение null, то не кэшируйте результат в аннотации @Cacheable для такого метода?

@Cacheable(value="defaultCache", key="#pk")
public Person findPerson(int pk) {
   return getSession.getPerson(pk);
}

Обновление: вот вопрос JIRA, представленный относительно кэширования нулевого значения в ноябре прошлого года, который еще не решен: [# SPR-8871] Условие @Cachable должно разрешать ссылку на возвращаемое значение - Spring Projects Issue Tracker

Ответ 1

Hooray, а весны 3.2 рамки позволяет для этого с помощью Spring SPEL и unless. Примечание из java-документа, окружающего Cacheable:

http://static.springsource.org/spring/docs/3.2.x/javadoc-api/org/springframework/cache/annotation/Cacheable.html

публичная аннотация String if

Атрибут Spring Expression Language (Spel), используемый для кэширования методов вето.

В отличие от условия() это выражение оценивается после вызова метода и поэтому может ссылаться на результат. Значение по умолчанию - "", что означает, что кеширование никогда не накладывается вето.

Важным аспектом является то, что unless оценка unless будет оценена после вызова метода. Это имеет смысл, потому что метод никогда не будет выполнен, если ключ уже находится в кеше.

Поэтому в приведенном выше примере вы просто аннотируете следующее (#result доступен для проверки возвращаемого значения метода):

@Cacheable(value="defaultCache", key="#pk", unless="#result == null")
public Person findPerson(int pk) {
   return getSession.getPerson(pk);
}

Я бы предположил, что это условие возникает из-за использования подключаемых кэш-реализаций, таких как Ehcache, который позволяет кэшировать нули. В зависимости от вашего сценария использования это может быть или не быть желательным.

Ответ 2

обновить этот ответ устарел сейчас, для Spring 3.2 и более поздних см. ответ Tech Trip, OP: не стесняйтесь отмечать его как принятый.

Я не думаю, что это возможно (даже если условное выселение Cache в Spring, которая может быть выполнена после вызова метода с @CacheEvict параметра beforeInvocation значения ЛЖИ, что значение по умолчанию) изучение CacheAspectSupport класса показывает, что возвращаемое значение не хранится где-нибудь до inspectAfterCacheEvicts(ops.get(EVICT)); вызов.

protected Object execute(Invoker invoker, Object target, Method method, Object[] args) {
    // check whether aspect is enabled
    // to cope with cases where the AJ is pulled in automatically
    if (!this.initialized) {
        return invoker.invoke();
    }

    // get backing class
    Class<?> targetClass = AopProxyUtils.ultimateTargetClass(target);
    if (targetClass == null && target != null) {
        targetClass = target.getClass();
    }
    final Collection<CacheOperation> cacheOp = getCacheOperationSource().getCacheOperations(method, targetClass);

    // analyze caching information
    if (!CollectionUtils.isEmpty(cacheOp)) {
        Map<String, Collection<CacheOperationContext>> ops = createOperationContext(cacheOp, method, args, target, targetClass);

        // start with evictions
        inspectBeforeCacheEvicts(ops.get(EVICT));

        // follow up with cacheable
        CacheStatus status = inspectCacheables(ops.get(CACHEABLE));

        Object retVal = null;
        Map<CacheOperationContext, Object> updates = inspectCacheUpdates(ops.get(UPDATE));

        if (status != null) {
            if (status.updateRequired) {
                updates.putAll(status.cUpdates);
            }
            // return cached object
            else {
                return status.retVal;
            }
        }

        retVal = invoker.invoke();

        inspectAfterCacheEvicts(ops.get(EVICT));

        if (!updates.isEmpty()) {
            update(updates, retVal);
        }

        return retVal;
    }

    return invoker.invoke();
}

Ответ 3

Если весенняя аннотация

@Cacheable(value="defaultCache", key="#pk",unless="#result!=null")

не работает, вы можете попробовать:

@CachePut(value="defaultCache", key="#pk",unless="#result==null")

Меня устраивает.