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

时间:2020-09-28 11:06:58

标签: java spring spring-boot caching spring-cache

我们在项目中添加了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时,它是从缓存中返回而不是从数据库中返回。现在,下一次当用户通过id请求单个对象时,我们想从已经缓存的foos数据中返回它,而不是调用JPA的findById()。有什么办法可以做到这一点?

2 个答案:

答案 0 :(得分:1)

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

请记住, Spring的缓存抽象是设计使然,它使用方法参数(如果有)作为键,并使用返回值作为缓存条目的值。如果该方法没有参数,则 Spring 将为您生成一个ID。

我有written关于如何自定义 Spring的 CacheManager实现,以缓存由@Cacheable方法返回的值的 Collection ,单独。

但是,暂时,假设您确实需要/想要缓存Foos的整个列表。

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

@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确保您绕过缓存代理(即,<<>所应用的MyFooService周围的AOP代理+缓存建议em> Spring )。如果getById(..)方法是MyFooService类的成员,并且直接调用了getAllFoos()方法,则您将绕过代理和缓存建议,从而导致每次访问数据库。

注意:如果要保留缓存代理,可以使用 Spring AOP Load Time Weaving (LTW)(请参阅doc)来避免绕过缓存代理。 getById(:Long)类中的MyFooService方法和getAllFoos()@Cacheable方法。但是...

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

希望这可以为您提供更多建议。

答案 1 :(得分:-1)

使用密钥对对象进行缓存,因此在从缓存中检索时,可以使用该密钥。

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