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

huangapple go评论75阅读模式
英文:

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) // 将实体映射为DTO
            .collect(Collectors.toList());
}

//想要实现类似以下的内容:
public FooDto getFoo(Long id) {
    // 从上面方法中缓存的foos返回单个对象
}

它将所有的 foos 存储在缓存中。如预期的那样,下次调用 getAllFoo 时,它会从缓存中返回,而不是从数据库返回。现在,当用户通过ID请求单个对象时,我们希望从已缓存的 foos 数据中返回,而不是调用 JPA 的 findById() 方法。有没有办法实现这一点?

英文:

We have spring-boot-starter-cache added in our project and not using any particular implementation of cache provider. we are loading all the data during application startup by calling following method:

@Override
@Cacheable(cacheNames = &quot;foos&quot;)
public List&lt;FooDto&gt; 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)
    }

It is storing all foos in the cache. And as expected next time when we are calling getAllFoo, it is returning from the cache rather than returning from the database. Now next time when user request for individual object by id, we want to return it from this already cached foos data rather than calling findById() of JPA. Is there any way to achieve this?

答案1

得分: 4

以下是翻译好的内容:

是否有任何理由使你希望或需要集体而不是逐个地将所有的 Foo 缓存于你的应用程序中?

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

我已经写过关于如何自定义 SpringCacheManager 实现来逐个缓存由 @Cacheable 方法返回的值的 Collection

然而,暂时假设您确实需要/希望缓存整个 Foos 列表。

然后,要创建一个从“缓存”的 Foos 列表中按 ID 获取单个 Foo 的方法,您可以在服务类中的原始缓存方法基础上,例如...

@Service
class MyFooService {

  private final FooRepository<Foo, Long> fooRepository;

  @Cacheable(cacheNames = "foos")
  public List<FooDto> getAllFoos() {
    return this.fooRepository.findAll().stream()
      .map(FooEntityDomainToDtoMapper::mapDomainToDto) // 将实体映射到数据传输对象
      .collect(Collectors.toList());
  }
}

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

@Component
class MyFooAccessor {

  private final MyFooService fooService;

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

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

  ...

}

MyFooAccessor 确保您不会绕过 缓存代理(即 Spring 应用于 MyFooService 的 AOP 代理 + 缓存建议)。如果 getById(..) 方法是 MyFooService 类的成员,并直接调用了 getAllFoos() 方法,您将绕过代理和缓存建议,从而导致每次访问数据库。

> 注意:如果您希望保留 MyFooService 类中的 getAllFoos()@Cacheable 方法,并将 getById(:Long) 方法放在其中,您可以使用 Spring 的 AOP Load Time Weaving(请参阅 文档)来避免绕过缓存代理。然而...

通常情况下,您可以通过适当地(重新)组织代码并使用适当的设计模式来解决这些问题。这并不是唯一的解决方案,Spring 提供了许多选择。这只是其中之一。

希望这能为您提供更多想法。

英文:

Is there any reason you want to, or need to, cache all Foos in your application collectively rather than individually?

Keep in mind, it is by design that Spring's Cache Abstraction uses the method parameter(s) (if any) as the key and the return value as the value of the cache entry. If the method has no parameters then Spring will generate an id for you.

I have written about how to customize Spring's CacheManager implementation to cache a Collection of values returned by a @Cacheable method, individually.

However, for the moment, let's assume you do need/want to cache the entire List of Foos.

Then, to create a method that pulls an individual Foo by ID from the "cached" List of Foos, you could, given your original cached method in a service class, do, for example...

@Sevice
class MyFooService {

  private final FooRepository&lt;Foo, Long&gt; fooRepository;

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

Then, in another application component, you could...

@Component
class MyFooAccessor {

  private final MyFooService fooService;

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

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

  ...

}

The MyFooAccessor makes sure you do not circumvent the caching proxy (i.e. the AOP Proxy + Caching Advice around the MyFooService applied by Spring). If the getById(..) method were a member of the MyFooService class, and called the getAllFoos() method directly, you would circumvent the proxy and caching advice resulting in a database access each time.

> 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...

Typically, you can solve these sort of problems by (re-)structuring your code appropriately, using the proper design pattern. This is not the only solution here, either. That is the beautiful thing about Spring is that it gives you many choices. This is just but 1 choice.

Hope this helps give you more ideas.

答案2

得分: 0

使用键将对象缓存起来,这样在从缓存中检索时可以使用该键。

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

Cache the object by using the key so while retrieving from the cache you can use that key.

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

huangapple
  • 本文由 发表于 2020年9月28日 19:06:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/64101012.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定