我在
redis cache
应用程序中使用 spring boot
。要求是 - 我们需要特定日期过滤器的数据列表。因此,为了实现这一目标,我使用了 cacheable
注释。
现在,当我第一次运行它时,它会从数据库中获取记录。多次运行时,它会返回存储在
cache
中的数据。完美的!到这里为止一切都运行良好。
然后我使用我的
db
在 jpa repository
中输入条目,并带有 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;
}
我使用打印语句划线检查,
newApplicationDate
和strDate
日期值是相同的。
在
redis client
我检查了keys *
它显示了类似的内容:
1) "custDetails:\xac\xed\x00\x05t\x00\n2023-11-01"
在 Spring 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]
如何使用
cached
注释更新现有 cachePut
列表?
============================================
我还尝试了下面的代码,即再次调用相同的返回类型方法,这次我将
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
如果缓存不能与自己现有的列表同步,那么到底为什么这个概念如此被谈论!
正如 @WildDev 在上面的评论中解释的那样,当使用专门的“命名”缓存时,要缓存的值的返回类型必须与给定的键匹配。
在您的示例中,这相当于:
@Service
class CacheableCustomerServiceExample {
@Cacheable(cacheNames="customerDetails")
List<CustomerDetail> fetchByDate(String date) {
// ...
}
@CachePut(cacheNames="customerDetails", key="#date")
List<CustomerDetail> updateCustomer(String date, CustomerDetail customer) {
// ...
}
}
请记住,在调用 fetchByDate(..)
服务方法后,您的“
customerDetails”缓存将具有以下条目:
KEY | VALUE
------------
date | List<CustomerDetail>
这意味着您需要
updateCustomer(..)
服务方法中的逻辑来更新更新方法返回的 List
对象的 CustomerDetail
,以便随后更新“date”键的缓存条目。
警告:请记住,您不能简单地从服务代理内部调用
服务方法,因为这不会调用 Spring 的缓存 AOP 逻辑,原因在 文档中进行了解释。fetchByDate(..)
可以说,如果
CustomerDetail
中的任何 List
条目被更新,那么缓存条目应该失效。因此,你的 updateCustomer(..)
方法宁愿是:
@CacheEvict(cacheNames="customerDetails", key="#date")
CustomerDetail updateCustomer(String date, CustomerDetail customer) {
// ...
}
在这种情况下,服务方法返回类型不需要匹配。
是的,这将完全删除缓存的
List
,但下次在获取时请求特定日期(即 fetchByDate(..)
)时,则可以延迟重新加载/缓存 List
。
这种方法可能会以更长的延迟为代价,但消耗更少的内存。
此外,除了更新的“并发性”之外,您还需要考虑存储在缓存
CustomerDetail
中的各个 List
对象的更新频率。在没有某种形式的同步(对于example)的情况下,您可能会遇到当前安排的竞争条件,特别是如果您的List
中的CustomerDetail
对象大小很大并且更新的并发性和频率很高。这本身就违背了缓存的目的。
其他需要考虑的事情是,是否有其他应用程序正在使用缓存更新该应用程序之外的后端数据源。从技术上来说,有很多事情需要考虑。
提示:您通常只想缓存非高度事务性数据(不经常更改的数据),这些数据的创建或检索的计算成本较高(例如网络绑定数据,例如调用某些 Web 服务,如 REST API),但经常被提及。
就我个人而言,我更喜欢缓存单个对象,而不是这些对象的集合。但是,如果您要缓存集合,那么我会主张 1) 保持相关事物的
List
和 2) 小。
有多种方法可以处理您想要做的事情,但我在这里介绍一种方法。
警告:此方法不一定是最佳方法,它取决于应用程序的用例、要求和 SLA。
参见示例测试我为这篇文章编写的。