Spring Cache with Collection of Items/Entities

How to cache the list of entires based on its primary key using spring caching

You need to provide a customized CacheManager and Cache implementation for your caching provider (e.g. Ehcache, Redis or Hazelcast).

By default, OOTB, Spring's Cache Abstraction does not split up cached method array/collection type return values into separate entries in the targeted cache. You must handle this yourself.

See my last response for this nearly identical question.

Spring cache all elements in list separately

I'll self answer my question since no one gave any and could help others.

The problem I had when dealing with this issue was a problem of misconception of Cache usage. My need posted on this question was related to how to update members of a cached list (method response). This problem cannot be solved with cache, because the cached value was the list itself and we cannot update a cached value partially.

The way I wanted to tackle this problem is related to a "map" or a distributed map, but I wanted to use the @Cacheable annotation. By using a distributed map would have achieved what I asked in my question without using @Cacheable. So, the returned list could have been updated.

So, I had (wanted) to tackle this problem using @Cacheable from other angle. Anytime the cache needed to update I refreshed it with this code.

I used below code to fix my problem:

@Cacheable("users")
List<User> list() {
return userRepository.findAll()
}
// Refresh cache any time the cache is modified
@CacheEvict(value = "users", allEntries = true")
void create(User user) {
userRepository.create(user)
}
@CacheEvict(value = "users", allEntries = true")
void update(User user) {
userRepository.update(user)
}
@CacheEvict(value = "users", allEntries = true")
void delete(User user) {
userRepository.delete(user)
}

In addition, I have enabled the logging output for spring cache to ensure/learn how the cache is working:

# Log Spring Cache output
logging.level.org.springframework.cache=TRACE

How to cache a ListObject in Java such that the elements in list are cached as a single entry each, using Spring Boot provided caching techniques?

This question has been asked multiple times.

  1. How to cache the list of entires based on its primary key using spring caching
  2. Spring Cache with collection of items/entities
  3. Putting all returned elements into a Spring-Boot cache using annotations
  4. What strategies exist for using Spring Cache on methods that take an array or collection parameter?

The answer is generally the same.

I suspect any 1 of the links will help, but perhaps start with #3.

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

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.



Related Topics



Leave a reply



Submit