web-dev-qa-db-fra.com

@Cacheable key sur plusieurs arguments de méthodes

De la documentation spring :

@Cacheable(value="bookCache", key="isbn")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)

Comment puis-je spécifier @Cachable pour utiliser isbn et checkWarehouse en tant que clé?

49
phury

Update: l'implémentation actuelle du cache Spring utilise tous les paramètres de la méthode comme clé de cache, sauf indication contraire. Si vous souhaitez utiliser les clés sélectionnées, reportez-vous à Réponse d'Arjan qui utilise la liste SpEL {#isbn, #includeUsed}, qui est le moyen le plus simple de créer des clés uniques.

De Documentation de printemps

La stratégie de génération de clé par défaut a changé avec la publication de Spring 4.0 Les versions précédentes de Spring utilisaient une stratégie de génération de clé qui, pour plusieurs paramètres de clé, ne considérait que le hashCode () de paramètres et non égaux (); cela pourrait provoquer une clé inattendue collisions (voir SPR-10237 pour l’arrière-plan). Le nouveau 'SimpleKeyGenerator' utilise une clé composée pour de tels scénarios.

Avant le printemps 4.0 

Je vous suggère de concatter les valeurs des paramètres dans l'expression Spel avec quelque chose comme key="#checkWarehouse.toString() + #isbn.toString()"). Je crois que cela devrait fonctionner comme org.springframework.cache.interceptor.ExpressionEvaluator renvoie Object, qui est utilisé plus tard comme clé afin fournissez une int dans votre expression SPEL.

En ce qui concerne le code de hachage avec une probabilité de collision élevée, vous ne pouvez pas l’utiliser comme clé.

Quelqu'un dans ce fil a suggéré d'utiliser T(Java.util.Objects).hash(#p0,#p1, #p2) mais cela NE FONCTIONNERA PAS et cette approche est facile à casser, par exemple, j'ai utilisé les données de SPR-9377 :

    System.out.println( Objects.hash("someisbn", new Integer(109), new Integer(434)));
    System.out.println( Objects.hash("someisbn", new Integer(110), new Integer(403)));

Les deux lignes affichent -636517714 sur mon environnement.

P.S. En fait, dans la documentation de référence, nous avons 

@Cacheable(value="books", key="T(someType).hash(#isbn)") 
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)

Je pense que cet exemple est faux et trompeur et doit être supprimé de la documentation, car les clés doivent être uniques.

P.P.S. Voir aussi https://jira.springsource.org/browse/SPR-9036 pour des idées intéressantes sur la génération de clés par défaut. 

J'aimerais ajouter par souci d'exactitude et de façon amusante l'utilisation d'une fonction de hachage sécurisée cryptographique comme SHA256, en raison des propriétés de cette fonction EST possible pour cette tâche , mais le calculer à chaque fois peut être trop cher.

60
Boris Treukhov

Après quelques tests limités avec Spring 3.2, il semble que l’on puisse utiliser une liste SpEL: {..., ..., ...}. Cela peut également inclure les valeurs null. Spring transmet la liste en tant que clé de la mise en œuvre réelle du cache. Lors de l'utilisation d'Ehcache, un tel utilisateur invoquera à un moment donné List # hashCode () , qui prend en compte tous ses éléments. (Je ne sais pas si Ehcache only s'appuie sur le code de hachage.)

J'utilise ceci pour un cache partagé, dans lequel je inclut le nom de la méthode dans la clé également, que le générateur de clé par défaut de Spring n'inclut pas . De cette façon, je peux facilement effacer le cache (unique), sans (trop ...) risquer de faire correspondre des clés pour différentes méthodes. Comme:

@Cacheable(value="bookCache", 
  key="{ #root.methodName, #isbn?.id, #checkWarehouse }")
public Book findBook(ISBN isbn, boolean checkWarehouse) 
...

@Cacheable(value="bookCache", 
  key="{ #root.methodName, #asin, #checkWarehouse }")
public Book findBookByAmazonId(String asin, boolean checkWarehouse)
...

Bien sûr, si de nombreuses méthodes en ont besoin et que vous utilisez toujours tous les paramètres de votre clé, vous pouvez également définir un générateur de clé personnalisé comprenant le nom de la classe et de la méthode:

<cache:annotation-driven mode="..." key-generator="cacheKeyGenerator" />
<bean id="cacheKeyGenerator" class="net.example.cache.CacheKeyGenerator" />

...avec:

public class CacheKeyGenerator 
  implements org.springframework.cache.interceptor.KeyGenerator {

    @Override
    public Object generate(final Object target, final Method method, 
      final Object... params) {

        final List<Object> key = new ArrayList<>();
        key.add(method.getDeclaringClass().getName());
        key.add(method.getName());

        for (final Object o : params) {
            key.add(o);
        }
        return key;
    }
}
58
Arjan

Vous pouvez utiliser une expression Spring-EL, par exemple sur JDK 1.7:

@Cacheable(value="bookCache", key="T(Java.util.Objects).hash(#p0,#p1, #p2)")
3
Biju Kunjummen

Ça va marcher 

@Cacheable(value="bookCache", key="#checkwarehouse.toString().append(#isbn.toString())")
0
Niraj Singh