我在spring boot
应用程序中使用redis cache
。要求是-我们需要特定日期过滤器上的数据列表。因此,为了实现这一点,我使用了cacheable
注解。
现在,当我第一次运行它时,它从数据库中获取记录。当运行多次时,它返回存储在cache
中的数据。完美!直到这里,它运行得很好。
然后,我使用jpa repository
在db
中创建一个条目,并带有CachePut
注解。
现在,假设我将再次获得更新条目的该高速缓存数据,我向我的GET
API发出请求。但令我惊讶的是,它无法执行。忘记最新条目,这次它甚至没有显示早期缓存的数据列表。
@Cacheable(cacheNames = "custDetails", key="#newApplicationDate")
@Override
public List<FetchResponse> fetchAll2(String newApplicationDate) {
//some code to return list
}
@CachePut(cacheNames = "custDetails", key="#strDate")
@Override
public CustomerDetail updateCustomer(CustomerDetail customerDetail,String strDate) {
CustomerDetail customerDetail2 = custRepository.saveAndFlush(customerDetail);
return customerDetail2;
}
字符串
我使用print语句交叉检查,newApplicationDate
和strDate
的日期值是相同的。
在redis client
中,我检查了keys *
,它显示了如下内容:
1) "custDetails:\xac\xed\x00\x05t\x00\n2023-11-01"
型
在Sping Boot 控制台中,我收到以下错误:
java.lang.ClassCastException: com.myapp.model.CustomerDetail cannot be cast to java.util.List
at com.myapp.service.MyServiceImpl$$EnhancerBySpringCGLIB$$347be321.fetchAll2(<generated>) ~[classes/:na]
型
如何使用cachePut
注解更新现有的cached
列表?
我还尝试了下面的代码,即再次调用相同的返回类型方法,这次我把cachePut
注解放在这个新方法中:
@Override
public CustomerDetail updateCustomer(CustomerDetail customerDetail,String strDate) {
// TODO Auto-generated method stub
System.out.println("!!!!! "+strDate+" !!!!!");
CustomerDetail customerDetail2 = custRepository.saveAndFlush(customerDetail);
if(customerDetail2!=null) {
refreshCache(strDate);
}
return customerDetail2;
}
@CachePut(cacheNames = "custDetails", key="#strDate")
public List<FetchResponse> refreshCache(String strDate){
return ekycService.fetchAll2(strDate);
}
型
但没有成功。
现在我已经将Cacheable
和CachePut
放在同一个服务类中。
@Cacheable(value = "custDetails", key = "#date")
@Override
public List<CustomerDetail> fetchByDate(String date) {
return custRepository.findByCreateDatess(date);
}
@CachePut(value = "custDetails", key="#strDate")
@Override
public CustomerDetail updateCustomer(CustomerDetail customerDetail,String strDate) {
CustomerDetail customerDetail2 = custRepository.saveAndFlush(customerDetail);
return customerDetail2;
}
型
再次相同的问题.当我尝试添加CustomerDetail
到List<CustomerDetail>
,然后向前我没有得到列表.我得到下面的错误,而不是当试图得到List<CustomerDetail>
:
java.lang.ClassCastException: com.myapp.model.CustomerDetail cannot be cast to java.util.List
型
如果缓存不能与自己现有的列表同步,那么究竟为什么它是如此谈论概念!
1条答案
按热度按时间djmepvbi1#
正如@WildDev在上面的评论中所解释的那样,当使用一个特定的“命名”缓存时,缓存的值的返回类型必须与给定的键匹配。
在你的例子中,这将等同于:
字符串
请记住,在调用
fetchByDate(..)
服务方法后,您的“customerDetails”缓存将具有以下条目:型
这意味着您需要在
updateCustomer(..)
服务方法中使用逻辑来更新update方法返回的CustomerDetail
对象的List
,以便随后更新“date”键的该高速缓存条目。警告:请记住,您不能简单地从服务代理内部调用
fetchByDate(..)
服务方法,因为这不会调用Spring的缓存AOP逻辑,原因在文档中解释。可以说,如果
List
中的任何CustomerDetail
条目被更新,那么该高速缓存条目应该无效。因此,您的updateCustomer(..)
方法应该是:型
在这种情况下,服务方法返回类型不需要匹配。
是的,这将完全删除缓存的
List
,但下次在获取时请求特定日期(即fetchByDate(..)
)时,List
可以被延迟重新加载/缓存。这种方法可能会以更长的延迟为代价,但占用的内存更少。
此外,除了更新的“并发性”之外,还需要考虑存储在该高速缓存
List
中的各个CustomerDetail
对象的更新频率。(例如),特别是如果您的CustomerDetail
对象的List
大小很大,并且更新的并发性和频率很高。这本身就违背了缓存的目的。其他需要考虑的事情是,是否有其他应用程序正在使用缓存更新这个应用程序之外的后端数据源。
提示:您通常只想缓存非高度事务性的数据(非频繁更改的数据),这些数据创建起来需要很高的计算成本,检索起来也需要很高的成本(例如,网络绑定的数据,例如调用某些Web服务,如REST API),但经常被引用。
就个人而言,我更喜欢缓存单个对象,而不是这些对象的集合。然而,如果你缓存的是一个集合,那么我认为1)保持相关的
List
和2)小。有几种方法来处理你想在你的情况下做什么,但我在这里提出一种方法。
答:这种方法不一定是最好的方法,它取决于应用程序的用例、需求和SLA。
请参阅我为这篇文章编写的示例测试。