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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With