首页 > 解决方案 > Spring Boot 使用 findAll 方法填充 findById 方法使用的缓存

问题描述

我在springboot应用程序中有一个简单的服务,其中一种方法通过Id(主键)从数据库中获取一个对象。我还有另一种方法可以返回该表中的所有对象。

@Cacheable("stores")
public List<Store> findAllStores() throws InterruptedException {
    Thread.sleep(5000);
    return storeRepository.findAll();
}

@Cacheable("stores")
public Store findById(int storeId) throws InterruptedException {
    Thread.sleep(5000);
    return storeRepository.findById(storeId).orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST, "No store with specified ID exist"));
}

在我的测试中,我调用了第一种方法,该方法使用表中的所有存储对象填充了名为“stores”的缓存,但是当我调用第二种方法通过 Id 单独查找存储时,我仍然得到 5 秒的等待时间?为什么会这样?没有错误,缓存不是变量,所以我发现很难调试这个问题,因为在调试会话期间我看不到缓存的内容。

标签: javaspringspring-bootcachingspring-data-jpa

解决方案


任何存储在缓存中的数据都需要一个键来快速检索。默认情况下,Spring 使用带注释的方法的签名作为键来创建缓存键。

所以在这里你基本上使用了两种不同的方法,它们将使用单独的键,即方法签名。因此,如果您再次调用相同的方法,那么结果将从缓存中返回,而不是从数据库中获取数据。我希望你能明白。您可以查看以下链接

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/cache/annotation/Cacheable.html#:~:text=Annotation%20Type%20Cacheable&text=Annotation%20indicating%20that %20the%20result,调用%20for%20the%20给定%20arguments

而且您的期望是完全错误的,如上所述,每个缓存都需要一个 cacheKey 并且它存储针对该键的值,如果您执行第二种方法 - 第一次从 storeId 获取 Store 并将其保存在缓存中,方法签名作为键也将将 storeId 作为键,因此第二次调用相同的方法时,它将从缓存中获取它,因为它已经具有该键的条目。第一种方法和第二种方法的键是不同的,所以你的期望是错误的。

<---->编辑<--->

您可以像下面这样修改您的代码,明确指定 cacheID。

@Cacheable("stores",key = "#storeId")
public Store findById(int storeId) throws InterruptedException {
   
    return storeRepository.findById(storeId).orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST, "No store with specified ID exist"));
}

删除不需要的 findAllStores() 方法。并直接使用 cacheManager 来存储值。像下面的东西。

@Autowired
 CacheManager cacheManager;
@Autowired
 StoreRepository repo;

public void loadCache() {
  for(Store store:repo.getAll()) {
    cacheManager.getCache("stores").put(store.getId(),store);
  }
}

您必须在启动时调用它。而已。


推荐阅读