我找到了两种本地解决方案来使用复杂的集合值作为缓存键。
第一种方法是使用计算字符串作为缓存键:
@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 parameter
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);
}
}
注意1:此方法允许将本地键缓存符号与自定义包装器组合使用。与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
}
因此,所提出的方法允许:
@Cacheable(value = "Edges", key = "{#root.methodName, T(package.relationalDatabase.utils.Functions).getSpringCacheKey(#request.getParameterMap().values())}"
@Cacheable(value = "Edges", key = "{#root.methodName, T(package.relationalDatabase.utils.Functions).getSpringCacheKey(#request.getParameterMap().keySet())}"
无需为每个集合更新方法。
注意2: 此方法允许在Spring实体中使用Jackson视图,但在某些情况下需要@JsonIgnoreProperties({"hibernateLazyInitializer"})
注释。
最后,此方法的Spring缓存跟踪结果如下:
计算缓存密钥'[findWords,
[[""],["0"],[""],[""],[""],[""],["brazil"],["on"],["false"]]]'用于
操作Builder[public java.util.List
package.relationalDatabase.services.myClass.find(javax.servlet.http.HttpServletRequest)]
caches=[myClass] | key='{#root.methodName,
T(package.relationalDatabase.utils.Functions).getSpringCacheKey(#request.getParameterMap().values())}'
| keyGenerator='' | cacheManager='' | cacheResolver='' | condition=''
| unless='#result == null' | sync='false'
另一方面,建议使用字符串哈希函数来压缩结果键值。
为避免在JAR包生命周期中出现T()函数的问题,最好创建一个bean:
@Bean
KeySerializationComponent keySerializationComponent() {
return new KeySerializationComponent();
}
并使用以下方式调用:
@Cacheable(value = "Document", key = "{#root.methodName,#size,@keySerializationComponent.getSpringCacheKey(#ids)}", unless = "#result == null")