@ Cacheable key en múltiples argumentos de método


De la documentación de la primavera :

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

¿Cómo puedo especificar @Cachable para usar isbn y checkWarehouse como clave?

Author: phury, 2012-12-28

5 answers

Actualizar: La implementación actual de Spring cache usa todos los parámetros del método como clave de caché si no se especifica lo contrario. Si desea utilizar las claves seleccionadas, consulte Arjan's answer que utiliza Spel list {#isbn, #includeUsed} que es la forma más sencilla de crear claves únicas.

De Documentación de primavera

La estrategia de generación de claves predeterminada cambió con el lanzamiento de Spring 4.0. Las versiones anteriores de Spring usaban una estrategia de generación de claves que, para múltiples parámetros clave, solo se considera el hashCode() de parámetros y no es igual (); esto podría causar clave inesperada colisiones (ver SPR-10237 para el fondo). El nuevo 'SimpleKeyGenerator' utiliza una clave compuesta para tales escenarios.

Antes de la Primavera 4.0

Sugiero concat los valores de los parámetros en la Spel expresión con algo como key="#checkWarehouse.toString() + #isbn.toString()"), creo que esto debería funcionar como org.springframework.cache.interceptor.Evaluador de expresión devuelve Object, que luego se usa como clave para que no tenga que proporcionar un int en su expresión SPEL.

En cuanto al código hash con una alta probabilidad de colisión, no puede usarlo como clave.

Alguien en este hilo ha sugerido usar T(java.util.Objects).hash(#p0,#p1, #p2) pero NO FUNCIONARÁ y este enfoque es fácil de romper, por ejemplo, he utilizado los datos 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)));

Ambas líneas imprimen -636517714 en mi entorno.

P.d. En realidad en el documentación de referencia tenemos

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

Creo que este ejemplo es erróneo y engañoso y debe eliminarse de la documentación, ya que las claves deben ser únicas.

P. P. S. véase también https://jira.springsource.org/browse/SPR-9036 para algunas ideas interesantes sobre la generación de claves por defecto.

Me gustaría añadir en aras de la corrección y como un hecho entretenido que el uso de una función hash criptográfica segura como SHA256, debido a la las propiedades de dicha función ES posible para esta tarea, pero para calcularlo cada vez puede ser demasiado caro.

 53
Author: Boris Treukhov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:10:04

Después de algunas pruebas limitadas con Spring 3.2, parece que uno puede usar una lista de juegos: {..., ..., ...}. Esto también puede incluir valores null. Spring pasa la lista como la clave para la implementación real de la caché. Cuando se usa Ehcache, en algún momento invocará List#hashCode () , que toma en cuenta todos sus elementos. (No estoy seguro si Ehcache solo se basa en el código hash.)

Uso esto para una caché compartida, en la que incluyo el nombre del método en la clave también, que el generador de claves predeterminado de primavera no incluye . De esta manera puedo borrar fácilmente la caché (única), sin (demasiado...) arriesgando claves coincidentes para diferentes métodos. Como:

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

Por supuesto, si muchos métodos necesitan esto y siempre estás usando todos los parámetros para tu clave, entonces también se puede definir un generador de claves personalizado que incluye el nombre de la clase y el método:

<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;
    }
}
 56
Author: Arjan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-07-10 13:00:10

Puede usar una expresión Spring-EL, por ejemplo en JDK 1.7:

@Cacheable(value="bookCache", key="T(java.util.Objects).hash(#p0,#p1, #p2)")
 2
Author: Biju Kunjummen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-12-28 16:24:17

Esto funcionará

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

 0
Author: Niraj Singh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-05-31 12:38:09

Usa esto

@Cacheable(value="bookCache", key="isbn + '_' + checkWarehouse + '_' + includeUsed")
 -1
Author: Gavy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-06-11 03:04:56