web-dev-qa-db-fra.com

Comment dire au cache Spring de ne pas mettre en cache la valeur nulle dans l'annotation @Cacheable

Existe-t-il un moyen de spécifier que si la méthode renvoie une valeur nulle, ne mettez pas le résultat en cache dans l'annotation @Cacheable pour une méthode comme celle-ci?

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

Mise à jour: voici le problème JIRA soumis concernant la mise en cache de la valeur nulle en novembre dernier, qui n'a pas encore été résolu: [# SPR-8871] La condition @Cachable devrait permettre de référencer la valeur de retour - Spring Projects Issue Tracker

54
David Zhao

Hourra, à partir de Spring 3.2, le framework permet cela en utilisant Spring SPEL et unless. Note du document Java entourant Cacheable:

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

chaîne abstraite publique sauf

Attribut Spring Expression Language (SpEL) utilisé pour opposer un veto à la mise en cache des méthodes.

Contrairement à condition (), cette expression est évaluée après l'appel de la méthode et peut donc faire référence au résultat. La valeur par défaut est "", ce qui signifie que la mise en cache n'est jamais refusée.

L'aspect important est que unless est évalué après l'appel de la méthode. Cela est parfaitement logique car la méthode ne sera jamais exécutée si la clé est déjà dans le cache.

Ainsi, dans l'exemple ci-dessus, vous annoteriez simplement comme suit (#result est disponible pour tester la valeur de retour d'une méthode):

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

J'imagine que cette condition découle de l'utilisation d'implémentations de cache enfichables telles que Ehcache qui permet la mise en cache des valeurs nulles. Selon votre scénario d'utilisation, cela peut être souhaitable ou non.

100
TechTrip

mise à jour cette réponse est désormais obsolète, pour Spring 3.2 et versions ultérieures voir Tech Trip answer , OP: n'hésitez pas à la marquer comme accepté.

Je ne pense pas que ce soit possible (même s'il y a une éviction conditionnelle du cache au printemps qui peut être exécutée après l'invocation de la méthode avec le paramètre @CacheEvict beforeInvocation défini sur false, ce qui est par défaut value) l'examen de la classe CacheAspectSupport montre que la valeur retournée n'est stockée nulle part avant l'appel de 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();
}
5
Boris Treukhov

Si annotation de printemps

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

ne fonctionne pas, vous pouvez essayer:

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

Ça marche pour moi.

3
Lex