我发现两种原生解决方法可以使用复杂的集合值作为缓存键。
第一种方法是使用计算字符串作为缓存键:
@Cacheable(value = "Words", key = "{#root.methodName, #a1}", unless = "#result == null")
//or
@Cacheable(value = "Words", key = "{#root.methodName, #p1}", unless = "#result == null")
//or
@Cacheable(value = "Words", key = "{#root.methodName, #precomputedString}", unless = "#result == null")
public List<Edge> findWords(HttpServletRequest request, String precomputedStringKey) {
}
为了调用这个方法服务如下:
//use your own complex object collection to string mapping as a second parammeter
service.findWords(request.getParameterMap().values(),request.getParameterMap()
.values()
.stream()
.map(strings -> Arrays.stream(strings)
.collect(Collectors.joining(",")))
.collect(Collectors.joining(","));)
以及第二种方法(我喜欢的形式):
@Cacheable(value = "Edges", key = "{#root.methodName, T(package.relationalDatabase.utils.Functions).getSpringCacheKey(#request.getParameterMap().values())}", unless = "#result == null")
public List<Edge> findWords(HttpServletRequest request, String precomputedStringKey) {
}
其中 package.relationalDatabase.utils.Functions getSpringCacheKey 是自己创建的函数,如下:
public static String getSpringCacheKey(Object o) throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
boolean isSpringEntity = o.getClass().getAnnotation(javax.persistence.Entity.class) != null;
if (isSpringEntity) {
return objectMapper.writerWithView(JSONViews.Simple.class).writeValueAsString(o);
} else {
return objectMapper.writeValueAsString(o);
}
}
注意 I:此方法允许将本机密钥缓存表示法与自定义包装器结合使用。与 Spring 缓存的 keyGenerator 属性不同,该属性不允许键注释(它们是互斥的)并且需要创建 CustomKeyGenerator
@Cacheable(value = "Edges", unless = "#result == null", keyGenerator = "CustomKeyGenerator")
public List<Edge> findWords(HttpServletRequest request, String precomputedStringKey) {
}
////////
public class CustomKeyGenerator implements KeyGenerator {
Object generate(Object target, Method method, Object... params)
}
并为每个复杂的集合键创建一个返回包装器。例如:
@Override
public Object generate(Object target, Method method, Object... params) {
if(params[0] instanceof Collection)
//do something
if(params[0] instanceof Map)
//do something
if(params[0] instanceof HttpServletRequest)
//do something
}
因此,建议的方法允许:
//note #request.getParameterMap().values()
@Cacheable(value = "Edges", key = "{#root.methodName, T(package.relationalDatabase.utils.Functions).getSpringCacheKey(#request.getParameterMap().values())}"
//note #request.getParameterMap().keySet()
@Cacheable(value = "Edges", key = "{#root.methodName, T(package.relationalDatabase.utils.Functions).getSpringCacheKey(#request.getParameterMap().keySet())}"
无需为每个集合更新方法。
注意二:这种方法允许对 spring 实体使用 jackson 视图,但在某些情况下需要 @JsonIgnoreProperties({"hibernateLazyInitializer"}) 注释。
最后这个方法的spring cache的trace结果如下:
计算出的缓存键 '[findWords,
[[""],["0"],[""],[""],[""],[""],["巴西"],["on"],["false"]] ]' 为了
操作生成器[public java.util.List
package.relationalDatabase.services.myClass.find(javax.servlet.http.HttpServletRequest)]
缓存=[myClass] |键='{#root.methodName,
T(package.relationalDatabase.utils.Functions).getSpringCacheKey(#request.getParameterMap().values())}'
|密钥生成器='' |缓存管理器='' |缓存解析器='' |条件=''
|除非='#result == null' |同步='假'
另一方面,建议使用字符串散列函数来压缩生成的键值。