Domanda @ Chiave memorizzabile su più argomenti del metodo


Dal documentazione di primavera :

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

Come posso specificare @Cachable usare isbn e checkWarehouse come chiave?


44
2017-12-28 16:14


origine


risposte:


Aggiornare: L'implementazione attuale della cache di Spring utilizza tutti i parametri del metodo come chiave di cache, se non diversamente specificato. Se si desidera utilizzare i tasti selezionati, fare riferimento a La risposta di Arjan che usa l'elenco SpEL {#isbn, #includeUsed} che è il modo più semplice per creare chiavi uniche.

A partire dal Documentazione di primavera

La strategia di generazione della chiave predefinita è cambiata con il rilascio di Spring   4.0. Le versioni precedenti di Spring utilizzavano una strategia di generazione di chiavi che, per più parametri chiave, considerava solo l'hashCode () di   parametri e non uguali (); questo potrebbe causare una chiave inaspettata   collisioni (vedi SPR-10237 per lo sfondo). Il nuovo   "SimpleKeyGenerator" utilizza una chiave composta per tali scenari.

Prima della primavera 4.0 

Ti suggerisco di concatenare i valori dei parametri nell'espressione Spel con qualcosa di simile key="#checkWarehouse.toString() + #isbn.toString()"), Credo che questo dovrebbe funzionare come org.springframework.cache.interceptor.ExpressionEvaluator restituisce Object, che viene successivamente utilizzato come chiave in modo da non dover fornire un int nella tua espressione SPEL.

Per quanto riguarda il codice hash con una probabilità di collisione elevata - tu non si può usalo come chiave.

Qualcuno in questa discussione ha suggerito di usare T(java.util.Objects).hash(#p0,#p1, #p2) ma NON FUNZIONERÀ e questo approccio è facile da interrompere, ad esempio ho usato i dati da 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)));

Entrambe le linee stampano -636517714 sul mio ambiente.

Post scriptum In realtà nella documentazione di riferimento che abbiamo

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

Penso che questo esempio sia SBAGLIATO e fuorviante e dovrebbe essere rimosso dalla documentazione, in quanto le chiavi dovrebbero essere uniche.

P.P.S. vedi anche https://jira.springsource.org/browse/SPR-9036 per alcune idee interessanti riguardanti la generazione di chiavi di default.

Mi piacerebbe aggiungere per motivi di correttezza e come un fatto divertente che usando un sicuro funzione di hash crittografica come SHA256, a causa delle proprietà di tale funzione È possibile per questo compito, ma calcolarlo ogni volta potrebbe essere troppo costoso.


50
2017-12-29 01:16



Dopo alcuni test limitati con Spring 3.2, sembra che si possa usare una lista SpEL: {..., ..., ...}. Questo può anche includere null valori. Spring passa la lista come chiave per l'effettiva implementazione della cache. Quando si usa Ehcache, ad un certo punto tale invocazione List # hashCode (), che tiene conto di tutti i suoi articoli. (Non sono sicuro se Ehcache solo si basa sul codice hash.)

Lo uso per una cache condivisa, in cui io includere il nome del metodo anche nella chiave, che è il generatore di chiavi predefinito di Spring non include. In questo modo posso cancellare facilmente la cache (singola), senza (troppo ...) rischiando chiavi corrispondenti per metodi diversi. Piace:

@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)
...

Naturalmente, se molti metodi lo richiedono e si utilizzano sempre tutti i parametri per la propria chiave, è possibile anche definire un generatore di chiavi personalizzato che includa il nome della classe e del metodo:

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

...con:

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;
    }
}

55
2017-07-01 14:13



Puoi usare un'espressione Spring-EL, ad esempio su JDK 1.7:

@Cacheable(value="bookCache", key="T(java.util.Objects).hash(#p0,#p1, #p2)")

2
2017-12-28 16:24



Questo funzionerà

@Cacheable (value = "bookCache", key = "# checkwarehouse.toString (). Append (# isbn.toString ())")


0
2018-05-31 12:38



Usa questo

@Cacheable(value="bookCache", key="isbn + '_' + checkWarehouse + '_' + includeUsed")

-1
2018-06-11 03:04