I have two methods to fetch an entity with two different parameters. I also have a save method that uses one of those parameters. How can I evict the entity from the cache under both fetch keys? e.g. see below:
@Cacheable
public User getByUsername(String username);
@Cacheable
public User getByEmail(String email);
@CacheEvict(key="#entity.username")
User save(User entity);
In the above, a call to getByEmail will return stale date.
There are several options, of course, but as usual, Spring has your back.
The easiest and most simple approach is to leverage Spring's @Caching
annotation on your save
method, like so...
@Caching(evict = {
@CacheEvict(cacheNames = "Users", key="#user.name"),
@CacheEvict(cacheNames = "Users", key="#user.email")
})
User save(User user);
For your reference, I created an example test class demonstrating this working here.
You will notice I imitated your example above using Spring's Cache Abstraction annotations on my UserRepository. In this case, my repo is backed by Pivotal GemFire, but any data store will work. I use a ConcurrentMap
as my caching provider, using Spring's ConcurrentMapCacheManager, but of course, any caching provider will do.
My test case proceeds to save a new User
, ensuring that the user is stored by not yet cached. The test then proceeds to exercise the query methods (findByName
, findByEmail
) ensuring that the user entity is cached appropriately in each case. I then remove the entity from the underlying data store and ensure that the entity is still cached. And finally, the moment of truth, I modify the entity, re-save the entity, asserting that the entity is stored by that all entries have been "evicted" from the cache.
You could also try, as another optimization, to combine the @CachePut
annotation with the 2 @CacheEvict
annotations in this case, which could restore the cache based on the new, updated entity, something like...
@Caching(
evict = {
@CacheEvict(cacheNames = "Users", key="#a0.name", beforeInvocation = true),
@CacheEvict(cacheNames = "Users", key="#a0.email", beforeInvocation = true)
},
put = {
@CachePut(cacheNames = "Users", key="#result.name"),
@CachePut(cacheNames = "Users", key="#result.email")
}
)
User save(User user);
NOTE: notice the user of the beforeInvocation
attribute on the @CacheEvict
annotations along with the @CachePuts
However, you may prefer that the entity be lazily added to the cache based on need.
Although, you would presume the entity is being frequently accessed/used since the save
method on your repo was just called, and therefore rely on your underlying data store (such as GemFire) to set additional eviction (based on overflow)/expiration (based on LRU) settings, thereby better managing your system resources (e.g. memory) while still maintaining optimal application performance.
Food for thought.
Hope this helps.