【问题标题】:How to bind parameter for Cacheable annotation in an interface如何在接口中绑定可缓存注释的参数
【发布时间】:2018-11-03 12:23:43
【问题描述】:

我正在尝试使用 Spring-JPA,如下面的代码所示。

@Repository
public interface EmployeeCrud extends CrudRepository<Employee, Integer> {
    @Cacheable(cacheNames = "emp_by_last_name, key = "#lastName")
    List<Employee> findAllByLastName(@Param("lastName") String lastName);
}

由于接口没有参数名称(除非编译时启用调试信息),由于以下异常,我无法获取数据。

 java.lang.IllegalArgumentException: Null key returned for cache operation (maybe you are using named params on classes without debug info?) Builder[public abstract java.util.List EmployeeCrud.findAllByLastName(java.lang.String)] caches=[emp_by_last_name] | key='#lastName' | keyGenerator='' | cacheManager='' | cacheResolver='' | condition='' | unless='' | sync='false'
at org.springframework.cache.interceptor.CacheAspectSupport.generateKey(CacheAspectSupport.java:561)
at org.springframework.cache.interceptor.CacheAspectSupport.findCachedItem(CacheAspectSupport.java:502)
at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:389)
at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:327)
at org.springframework.cache.interceptor.CacheInterceptor.invoke(CacheInterceptor.java:61)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)

即使我使用@Param@P 注释,CacheOperationExpressionEvaluator 也无法解决它,因为它使用内部使用StandardReflectionParameterNameDiscovererLocalVariableTableParameterNameDiscovererDefaultParameterNameDiscoverer。 如果是这样,它也会使用AnnotationParameterNameDiscoverer@Param 会被解析。

在不启用编译器调试信息或实现EmployeeCrud 接口的情况下,我们还需要哪些其他解决方案才能完成这项工作?

【问题讨论】:

  • 如果删除 key = "#lastName" 会发生什么?
  • 它会起作用的。在某些情况下,表达式可能如下所示。在这种情况下,它可能不起作用。 #entity.lastName
  • 你的意思是什么场景?
  • 我在之前的评论中已经提到过。如果表达式是#entity.lastName,那么在这种情况下它可能不起作用。
  • 但只要你有专用的缓存,这就是 irelevatn

标签: spring interface crud spring-cache


【解决方案1】:

简单!不开启调试信息,可以按位置引用参数,比如这个...

@Cacheable(cacheNames = "emp_by_last_name, key = "#a0")
List<Employee> findAllByLastName(@Param("lastName") String lastName);

有关详细信息,请参阅Reference Documentation

另外,因为@CacheablefindAllByLastName(..)Repository方法只有一个参数,所以不需要显式调用key来存储值(即方法返回值,在List&lt;Employee&gt;的情况下) ) 在缓存 ("emp_by_last_name") 中,因为默认情况下,Spring 的缓存抽象 使用可缓存方法的方法参数作为键。见here

您还应该注意缓存结果(即键/值)将是整个 Employee 对象列表。如果这是您要查找的内容,则不会单独缓存 List 的元素。如果您需要此功能,请参阅我在 1 次以上有 answered 的另一篇文章。

希望这会有所帮助。

干杯! -约翰

【讨论】:

  • 我期待一个解决方案,我可以使用参数名称绑定而不是使用像 a0p0 这样的位置。我知道如果我实现了接口,那么参数名称绑定将起作用,但是接口可以吗?
  • 为什么引用密钥的方式很重要?反正key的值就是方法参数的值?此外,@Param 注释是 Spring Data 注释。为什么核心 Spring Framework Cache Abstraction 对 @Param 注释一无所知?无论如何,你也可以......
  • 实现你自己的(1个或多个),自定义o.s.cache.interceptor.KeyGenerator(s),这将允许你检查@Caheable方法,也许在方法参数上搜索@Param注解,等等。Spring的缓存抽象使用KeyGenerator 接口作为“策略”来解析可缓存操作中的键。见这里 (github.com/spring-projects/spring-framework/blob/master/…)。因此,任何形式的ParameterNameDiscoverer 都不会帮助您。
  • 我们如何引用密钥很重要。显然值是相同的,但#lastName#p0 更直观。前者比后者更易于维护。
  • 您对 KeyGenerator 的看法是正确的。但是为所有密钥实现密钥生成器是我不想要的。如果#lastName 无法以简单的方式实现,那么下一个最佳解决方案是#p0。如果我能够传递我自己的ParameterNameDiscoverer,那么这个问题就会得到解决。类似于扩展的自定义参数名称发现器之类的东西,在这种情况下,我可能已经传递了AnnotationParameterNameDiscoverer,我相信这并没有错。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-22
  • 2018-08-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-13
相关资源
最近更新 更多