英文:
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 = "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)
}
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。
我已经写过关于如何自定义 Spring 的 CacheManager
实现来逐个缓存由 @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<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());
}
}
Then, in another application component, you could...
@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();
}
...
}
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 = "fooByIDCache", key = "#id", unless = "#result == null")
public FooDto getFooByID(String id, FooDto fooDTO) {
return fooDTO;
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论