如何从 Spring 缓存中的 Spring 缓存中的缓存中按键获取单个项目 Spring Boot?

How to get individual item by key from cache in Spring cache in Spring Boot?

我们已经 spring-boot-starter-cache 添加到我们的项目中,并且没有使用缓存提供程序的任何特定实现。我们通过调用以下方法在应用程序启动期间加载所有数据:

@Override
@Cacheable(cacheNames = "foos")
public List<FooDto> getAllFoo() {
    return fooRepository.findAll().stream()
            .map(FooEntityDomainToDtoMapper::mapDomainToDto) // mapping entity to dto
            .collect(Collectors.toList());
}

//Want to implement something like:
    public FooDto getFoo(Long id) {
    //return single object from foos(which are cached in above method)
    }

它将所有 foos 存储在缓存中。正如预期的那样,下次我们调用 getAllFoo 时,它是从缓存中 returning 而不是从数据库中 returning。现在下次当用户通过 id 请求单个对象时,我们希望从这个已经缓存的 foos 数据中 return 它而不是调用 JPA 的 findById()。有什么办法可以实现吗?

您是否有任何理由想要或需要将所有 Foos 缓存在您的应用程序中而不是单独缓存?

请记住,Spring 的缓存抽象 按照设计使用方法参数(如果有)作为键,return value 作为缓存条目的值。如果该方法没有参数,那么 Spring 将为您生成一个 ID。

我有 关于如何自定义 Spring 的 CacheManager 实现以缓存 Collection 个值 return 通过 @Cacheable 方法单独编辑。

但是,目前,我们假设您 need/want 缓存 Foos 的整个列表。

然后,要创建一个方法,通过 ID 从 Foos 的“缓存”列表中提取个人 Foo,您可以在服务 class 中给定您的原始缓存方法],例如...

@Sevice
class MyFooService {

  private final FooRepository<Foo, Long> fooRepository;

  @Cacheable(cacheNames = "foos")
  public List<FooDto> getAllFoos() {
    return this.fooRepository.findAll().stream()
      .map(FooEntityDomainToDtoMapper::mapDomainToDto) // mapping entity to dto
      .collect(Collectors.toList());
  }
}

然后,在另一个应用程序组件中,您可以...

@Component
class MyFooAccessor {

  private final MyFooService fooService;

  MyFooAccessor(MyFooService fooService) {
    this.fooService = fooService;
  }

  Optional<FooDto> getById(Long id) {
    this.fooService.getAllFoos().stream()
      .filter(fooDto -> fooDto.getId().equals(id))
      .findFirst();
  }

  ...

}

MyFooAccessor确保您不会规避缓存代理(即围绕MyFooServiceSpring 应用)。如果 getById(..) 方法是 MyFooService class 的成员,并直接调用 getAllFoos() 方法,您将绕过代理和缓存建议,导致每次访问数据库.

NOTE: You could use Spring AOP Load Time Weaving (LTW) (see doc) to avoid circumventing the caching proxy if you want to keep the getById(:Long) method in the MyFooService class with the getAllFoos(), @Cacheable method. However...

通常,您可以通过使用适当的设计模式(重新)适当地构造您的代码来解决这类问题。这也不是这里唯一的解决方案。 Spring 的美妙之处在于它给了你很多选择。这只是一种选择。

希望这有助于给你更多的想法。

使用密钥缓存对象,以便在从缓存中检索时可以使用该密钥。

@Override
@Cacheable(value = "fooByIDCache", key = "#id", unless = "#result == null")
public FooDto getFooByID(String id, FooDto fooDTO) {
    return fooDTO;
}