#Cacheable - how to evict object without knowing cache ID? - java

I have a simple Spring application which has a method:
#Cacheable("getSession")
public Session getSession(String sessionId) { ...
which is backed by eh-cache and works well. Where I'm having problems is that I need to evict objects from this cache not based on sessionId (a GUID by which they are keyed in the cache) but instead by the database Id of the returned object:
public void evictSessionFromCache(long id) { ...
There is no way for me to know the sessionId at the point this object needs to be evicted.
Does anybody have any pointers on this? I've tried extending EhCacheCacheManager to return a wrapped Cache object with a mapping between db ID and sessionID but this feels like a clunky solution:
if(name.equals("getSession")) {
if(wrappedCache == null) {
wrappedCache = new WrappedCache(super.getCache(name));
}
return wrappedCache;
} else {
return super.getCache(name);
}
Are there any properties I can use out of the box to utilise this annotation and access objects by more than one id? Or should I forget the annotation and manipulate the cache / cacheManager directly?
Thanks!

Related

Spring Cache - partial cache elements in list inside of DTO

So from my understanding in bellow code, when string ID is already in cache as key, DTO value gets returned from cache, instead of calling externalService method:
#Cachable("elements")
public ElementsDTOResponse getById(String id) {
return externalService.getById(id);
}
But can i use cache, if externalService takes list of IDs instead? My external service takes DTO in form:
ElementsDTO { List < ElementDTO > elementDTOList; }
Where ElementDTO { String id }
So can i do it, so externalService.getByIds(elementsDTO) will only be called with list of IDs that are not already in cache? Also if all IDs in DTO are already in cache (as keys) then do not call externalService at all, instead get values from cache. Something like that
#Cachable("elements")
#CachePut(key = "#elementsDTO.elementDTOList.id")
public ElementsDTOResponse getById(ElementsDTO elementsDTO) {
return externalService.getAllByIds(id);
}
Is it possible to do that simply with Spring Cache?

Spring Cache - How can i get list of all actual data entities after changing in other cache-methods with custom key?

I have crud methods that modify the data in the cache and database
I also have a method that returns all entities when I use it after changes in the cache and database, I get irrelevant data.
As I understand it, the point is in the method of returning all entities. It uses the default key, it is different from other methods.
What do I need to do so that I can return the actual data sheet?
#Service
#CacheConfig(cacheNames = "configuration")
class ServiceConfiguration{
#Cacheable //this method returns non actual data
public List<MySomeConfiguration> getAllProxyConfigurations() {
return repository.getAllConfigurations();
}
#Cacheable(key = "#root.target.getConfigurationById(#id).serverId")
public MySomeConfiguration getConfigurationById(Long id) {
...
return configuration;
}
#CachePut(key = "#configuration.serverId", condition = "#result.id != null")
public MySomeConfiguration addOrUpdateConfiguration(Configuration configuration) {
return configuration;
}
#Cacheable(key = "#serverId")
public MySomeConfiguration getConfigurationByServerId(String serverId) {...
return configuration;
}
#CacheEvict(key = "#root.target.getConfigurationById(#id).serverId")
public void deleteConfigurationById(Long id) {
...
}
}//end class
p.s. sorry for my english
By default redis cache manager uses StringRedisSerializer for Key serializer
Your class toString() is used to serializer the key of the object so don't give different keys for different methods like put, get, evict etc jus rely on your toString() to get the key or override by using Spring Cache Custom KeyGenerator
Refer
https://www.baeldung.com/spring-cache-custom-keygenerator

Hibernate 5.2.9.Final cache not updated

I have a service (which I for some reason call controller) that is injected into the Jersey resource method.
#Named
#Transactional
public class DocCtrl {
...
public void changeDocState(List<String> uuids, EDocState state, String shreddingCode) throws DatabaseException, WebserviceException, RepositoryException, ExtensionException, LockException, AccessDeniedException, PathNotFoundException, UnknowException {
List<Document2> documents = doc2DAO.getManyByUUIDs(uuids);
for (Document2 doc : documents) {
if (EDocState.SOFT_DEL == state) {
computeShreddingFor(doc, shreddingCode); //here the state change happens and it is persisted to db
}
if (EDocState.ACTIVE == state)
unscheduleShredding(doc);
}
}
}
doc2DAO.getManyByUUIDs(uuids); gets an Entity object from the database.
#Repository
public class Doc2DAO {
#PersistenceContext(name = Vedantas.PU_NAME, type = PersistenceContextType.EXTENDED)
private EntityManager entityManager;
public List<Document2> getManyByUUIDs(List<String> uuids) {
if (uuids.isEmpty())
uuids.add("-3");
TypedQuery<Document2> query = entityManager.createNamedQuery("getManyByUUIDs", Document2.class);
query.setParameter("uuids", uuids);
return query.getResultList();
}
}
However When I do second request to my API, I see state of this entity object unchanged, that means the same as before the logic above occoured.
In DB there is still changed status.
After the api service restart, I will get the entity in the correct state.
As I understand it, Hibernate uses it's L2 cache for the managed objects.
So can you, please point me to what I am doing wrong here? Obviously, I need to get cached entity with the changed state without service restart and I would like to keep entities attached to the persistence context for the performance reasons.
Now, can you tell me what I am
In the logic I am making some changes to this object. After the completition of the changeDocState method, the state is properly changed and persisted in the database.
Thanks for the answers;

How to update spring cache partiallly(one field only)?(when object mutates)

Let's imagine that I have a method which can updates one field for all entities.
public void makeAllCarsRed(){...}
I know that Spring offers 3 annotation to manage cache:
#Cacheable - tries to find value in cache. If cannot find - execute method and adds to cache;
#CachEvict - just remove objects from cache by criteria;
#CachPut - put new value to cache
I don't see a way to apply these annotations for my situation.
P.S.
I think that it is too expensive to invalidate all cach
Spring caches a whole entity, not a single field. So you cannot evict a field from the cache.
If you would want to evict the entities from the cache, you would also have aproblem, but that can be solved :
Outside your method it is not visible, which entites are updated, so you cannot use any of these annotations.
What you can do, is get the CacheManager injected into the class that owns the method makeAllCarsRed() and than update the cache manually for all entites that are updated :
in your config
#Bean
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager("cacheName");
}
class that has makeAllCarsRed() :
public class MakeAllCarsRedService{
#Autowired CacheManager cm;
...
public void makeAllCarsRed(){
Cache cache = cm.getCache("cacheName");
//remove from cache
cache.evict(key)
//or, if neddec add to cache
put(key, entity);
...
}
}
A cached method do not return a deep copy of the cache content so you can make side effects on it. Let getCars(boolean) be your cached method:
#Cacheable("cars")
public List<Car> getCars(boolean isAvailable) {
return ...;
}
Call your cached method with the right parameters and then corrupt the cache:
public void makeAllCarsRed() {
List<Car> cache = getCars(true);
for (Car carInCache : cache) {
carInCache.setColor("red");
}
}
The next time you will call getCars(boolean), it will return the updated cache.

How to return number of entries using spring cache abstraction

I am using spring cache abstraction to cache objects in the service layer.
This is fine for simple get/put operations, as follows:
static private final String cacheName = "messages";
#CacheEvict(value=cacheName, key="#message.id")
public void deleteMessage(Message message) {
...
}
#Cacheable(value=cacheName, key="#id")
public Message findMessage(Long id) {
...
}
#CachePut(value=cacheName, key="#message.id")
public void saveMessage(Message message) {
...
}
#CachePut(value=cacheName, key="#message.id")
public Message updateMessage(Message message) {
...
}
However, what annotation would I use for the following method:
public long countAllMessages() {
...
}
Since all the objects will be in the cache, there should be a way to get the answer from the cache and not having to go to the repository layer.
Also, the cache is being applied on the following method:
#Cacheable(cacheName)
public List<Message> findAllMessages() {
...
}
I could make the count method call the find all method like this:
public long countAllMessages() {
return findAllMessages().size();
}
But this would not be efficient in the case where the cache is disabled, as the call would then load all records from the db instead of doing a SELECT COUNT(*)...
The Spring cache abstraction doesn't currently provide a direct way of accessing statistics about the underlying caches. For example, there is no direct way to get the size of all the caches, the keys of the cached objects, etc. It's a higher level abstraction than that. You can, however, get access to the underlying native cache classes via the Cache#getNativeCache() method. Grabbing an instance of Cache from the CacheManager class will give you access to this method. Assuming you know the underlying types of the Cache instances managed by the CacheManager, you can cast them to the appropriate types and gain access to the helper methods on each (assuming there are any).
For example, if your underlying cache store is EHCache, you can grab an instance of the Cache named "messages" by calling CacheManager#getCache("messages"). You'd then be able to check the type and cast the type to net.sf.ehcache.Ehcache, from which you can call the various statistics helper methods (eg. getStatistics()).
Here's an example of going through all the caches managed by CacheManager, checking if they're of type net.sf.ehcache.Ehcache, and subsequently getting their statistics.
public class EhCacheStatistics {
private CacheManager cacheManager;
#Inject
public void setCacheManager(CacheManager cacheManager) {
this.cacheManager = cacheManager;
}
public long getTotalEhCacheSize() {
long totalSize = 0l;
for (String cacheName : cacheManager.getCacheNames()) {
Cache cache = cacheManager.getCache(cacheName);
Object nativeCache = cache.getNativeCache();
if (nativeCache instanceof net.sf.ehcache.Ehcache) {
net.sf.ehcache.Ehcache ehCache = (net.sf.ehcache.Ehcache) nativeCache;
totalSize += ehCache.getStatistics().getObjectCount();
}
}
return totalSize;
}
}

Categories