Spring cache and retrieve object without permanent store - java

I'm building a stub in spring that does not have a permanent store but I want to be able to retrieve objects that have been sent to the stub.
Thus when the stub receives a payload I want to add it to a cache, and be able to get that object from the cache when queried.
I've read about spring's cache annotation implementation (#Cacheable ect.) but I can't work out how to implement this without a permanent store for the first call to that function.
I think an object can be put into the cache using:
#CachePut(value = "addressCache", key = "#customerId")
public Submission cache(Address address, String customerId) {
return address;
}
Is there a way to retrieve this object from the cache using the key (customerId) without calling that original cache() function?
What would be a way to implement cache for what I need?

What you are looking for is #Cacheable annotation that does the look-up from the cache. #CachePut instead is invoked everytime to update the cache.

Related

How can I cache by method parameter in Spring Boot?

I use Redis for caching and have the following service method:
#Cacheable(value = "productCache")
#Override
public List<ProductDTO> findAllByCategory(Category category) {
// code omitted
return productDTOList;
}
When I pass categoryA to this method, the result is cached and is kept during expiration period. If I pass categoryB to this method, it is retrieved from database and then kept in cache. Then if I pass categoryA again, it is retrieved from cache.
1. I am not sure if it is normal, because I just use value parameter ("productCache") of #Cacheable annotation and have no idea how it caches categoryA and categoryB results separately. Could you please explain how it works?
2. As mentioned on this page, there is also key parameter. But when using it as shown below, I think it does not make any sense and it works as above. Is that normal or am I missing something?
#Cacheable(value = "productCache", key="#category")
#Override
public List<ProductDTO> findAllByCategory(Category category) {
// code omitted
return productDTOList;
}
3. Should I get cache via Cache cache = cacheManager.getCache("productCache#" + category); ?
Caches are essentially key-value stores, where – in Spring –
the key is generated from the method parameter(s)
the value is the result of the method invocation
The default key generation algorithm works like this (taken right from Spring docs):
If no params are given, return SimpleKey.EMPTY.
If only one param is given, return that instance.
If more than one param is given, return a SimpleKey that contains all parameters.
This approach works well for most use-cases, as long as parameters have natural keys and implement valid hashCode() and equals() methods. If that is not the case, you need to change the strategy.
So in your example, the category object acts as key per default (for this to work, the Category class should have hashCode() and equals() implemented correctly). Writing key="#category" is hence redundant and has no effect. If your Category class would have an id property, you could write key="#category.id" however.
Should I get cache via Cache cache = cacheManager.getCache("productCache#" + category); ?
No, since there is no such cache. You only have one single cache named productCache.

How to update only a subset of fields and update the repository?

I'm making a spring boot application, and I'm looking to update an existing entry in the DB through my service and controller. In my service layer I have the below method. So I'm retrieving the fields associated with a caseID, creating a model mapper which maps my entity object class to my VO, and then mapping the retrieved data to my DTO. Then I save my repository. The purpose is to add only the fields which I have specified in my req message ie if I only want to update 1 field out of 20, it updates this field and leaves the rest untouched. The below runs successfully, but the field I specify in my request message in postman does not update in the DB. Why is this? I have tried mapping different objects and saving different variables to the repository but nothing seems to update the DB.
public StoredOutboundErrorCaseVO updateCase(OutboundErrorCaseVO outboundErrorCaseVO, Long caseNumber) {
OutboundErrorCaseData existingCaseData = ErrorCaseDataRepository.findById(caseNumber).get();
ModelMapper mm = new ModelMapper();
mm.getConfiguration().setAmbiguityIgnored(true);
OutboundErrorCaseData uiOutboundErrorCaseData = mm.map(outboundErrorCaseVO,
OutboundErrorCaseData.class);
mm.map(existingCaseData, uiOutboundErrorCaseData);
ErrorCaseDataRepository.save(uiOutboundErrorCaseData);
return mm.map(uiOutboundErrorCaseData, StoredOutboundErrorCaseVO.class);
}
Controller - code omitted for brevity, POST method (I usually use PUT for updates but I believe I can still use POST)
StoredOutboundErrorCaseVO updatedCase = outboundErrorService.updateCase(outboundErrorCaseVO,
caseNumber);
Repo
#Repository
public interface OutboundErrorCaseDataRepository extends JpaRepository<OutboundErrorCaseData, Long> {
You are getting data and passing it into existingCaseData and save uiOutboundErrorCaseData. So my guess is Hibernate is adding a new object into the database with new Id and with you updated value. It of course depends on your model definition. Especially id.
I also think Hibernate won't let you save uiOutboundErrorCaseData with the same Id if you already have an object in Hibernate Session associated with that id. So, why don't you update existingCaseData with the new value and save it back.
I created a working solution, although I realise it can be improved, it certainly works. The only drawback is that I need to specify all the fields which can be updated, ideally I want a solution which takes in n number of fields and updates the record.
OutboundErrorCaseData existingCaseDta = ErrorCaseDataRepository.findById(caseNumber).get();
if (outboundErrorCaseVO.getChannel() != null) {
existingCaseDta.setChannel(outboundErrorCaseVO.getChannel());
}
ErrorCaseDataRepository.save(existingCaseDta);
ModelMapper mm = new ModelMapper();
return mm.map(existingCaseDta, StoredOutboundErrorCaseVO.class);

Get age of cache entry in Ehcache 3.x

I'm using Ehcache 3.8.0 with my Spring 5.x application for caching long running data aggregation. The aggregated data is displayed in a web frontend.
I'm now looking for a way to get the age of an cached element to display it alongside with the data.
Ehcache 2.x stored cache entries as Element (see javadoc) which had the public method getLatestOfCreationAndUpdateTime() to retrieve the timestamp, at which the cache entry was created or last updated (= the age/freshness of the cached data).
But in Ehcache 3.x cache entries are stored as Cache.Entry<V,K> (see javadoc) which is nothing more than a key/value tuple which doesn't provide something like the getLatestOfCreationAndUpdateTime() method.
I know that I could use something like cache eventlisteners to store the timestamp seperatly whenever a cache entry is created or updated, but I like to know if there's a more direct way to get the timestamp formerly provided with getLatestOfCreationAndUpdateTime().
The following manages to capture the internal epoch timestamp of creation of the Ehcache cache entry:
var method = Ehcache.class.getDeclaredMethod("doGet", Object.class);
method.setAccessible(true);
var valueHolder = (Store.ValueHolder<?>) method.invoke(ehcache, key);
return valueHolder.creationTime();
The use of reflection is unfortunate but this was sufficient for my debugging purposes.
The org.ehcache.core.Ehcache instance in my specific scenario was retrieved inside a custom org.ehcache.event.CacheEventListener:
#Override
public void onEvent(CacheEvent<? extends String, ? extends Object> cacheEvent) {
var ehcache = (Ehcache<?, ?>) cacheEvent.getSource();
...
}

Spring cache #CacheEvict matches key in a list?

I'm using Spring cache and trying to evict cache by a list of key(id).
#CacheEvict(value="cacheName",key=?, condition=? )
public void deleteByIds(List<Integer> ids){...}
How can I manage to do that?
#CacheEvict
Annotation indicating that a method (or all methods on a class)
triggers a cache evict operation.
The cachaName or value
Names of the caches in which method invocation results are stored.
Condition
Expression used for making the method caching conditional.
Key
root.method, root.target, and root.caches for references to the method, target object, and affected cache(s) respectively.
Solution for your problem:
Assuming that every object from the List it is cached into, for example cacheName = "entities" and for the key you can use the entity ID (which is the String representation of the Integer value) you should write a second method to evict the cache.
public void deleteByIds(List<Intiger> intigers){
for(Intigier i : intigers){
deleteEntity(i.toString());
}
}
#CacheEvict(cacheName = "entities", key="entityId", condition="entityId!=null")
private void deleteEntity(String entityId){
//processing : for ex delete from the database and also remove from cache
}

I want to find a record from cached list using key in #Cacheable or any other mechanism

Hi there i need help I'm new to caching data. I'm using ehcache in spring application using xml configuration and I want to use different keys on different method to find same record. Suppose, one method is annotated like this:
#Cacheable(value="getCustomerByAreaId",key="T(java.lang.String).valueOf(#areaid)")
public List<Customer> getCustomerByAreaId(String areaid) {
//code
return customerList;
}
it will return all the customers having same area id. This list will be stored in the cache as each customer have unique customer id. Can I use some mechanism to fetch single customer record from cache= getCustomerByAreaId based on customer id.
#Cacheable(value="getCustomerByAreaId",key="T(java.lang.String).valueOf(#customerId)")
public Customer getCustomerById(long customerId) {
// code
return customer;
}
I know if I make key like this it will enter a new record in the cache (getCustomerByAreaId) with the new key.
Instead I want to fetch record from list that is already being cached.
If it is possible can I do this using xml or java.
I'm using ehcache version 2.5.
This is not possible simply by using Ehcache APIs or Spring's caching abstraction.
If you want to achieve this, you will have to program your own logic to cache the list but also its elements individually.

Categories

Resources