Spring cache #CacheEvict matches key in a list? - java

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
}

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.

reading a modified value from bean instead of DB within same transaction

I am facing a scenario where, I need to update the parameter and want to retrieve the modified value within same transaction
For example :
#Transactional(propagation = Propagation.REQUIRED)
public void modifiyParameter(Object object, BigInteger attribute_id) {
...
Attribute attrValue = object.getParameter(attribute_id);
attrValue.setValue("new_value");
object.setParameter(attr_id, attrValue);
...
object.getParameter(attribute_id); //getting old value instead of modified value
}
In the above case, It would return the old value itself, but I tried to wrap in separate transaction and I could able to retrieve the modified value.
My question is that, can't we retrieve the modified value from the bean itself within same transaction, instead of committing the inner transaction (i.e new transaction) and retrieving it from DB?
If you have the value then why you want to fetch the value from DB, Use the same value. That is a good design from a performance and maintainability perspective.

Spring cache and retrieve object without permanent store

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.

hibernate java curiosity - after saving the object, both the object to save and the saved one have id set

I have the following simple code:
#Test
public void saveExpense() {
// Create dummy Expense object i.e. { "description": "Short Description", "date": etc }
Expense expenseToSave = ExpenseHelper.createExpense("Short Description", new Date(), user);
Expense savedExpense = expenseService.save(expenseToSave);
// What is strange, is that here, both expenseToSave and savedExpense have id set to 1 for example; after save the expense should have an id;
Expense expected = ExpenseHelper.createExpense("Short Description", new Date(), user);
// Check if expected object is equal to the saved one
Assert.assertTrue(expected.equals(expenseService.findByDescription("Short Description")));
}
Normally I would expect that expenseToSave to be without id and savedExpense with id, but both have id after save. Why?
That made another variable to be necessary and complicate the test.
Thanks.
That's just how the Hibernate Session.save() method is specified. From the documentation:
Persist the given transient instance, first assigning a generated
identifier. (Or using the current value of the identifier property if
the assigned generator is used.) This operation cascades to associated
instances if the association is mapped with cascade="save-update".
IDs are the mechanism how Hibernate differentiates between persisted and transient objects, and how it identifies specific objects. Therefore, the ID is set early in the persistence step, as for example cyclic references in an object tree are resolved via IDs while persisting.
What differentiates the returned object vs. the original object is that the returned object is attached to the Hibernate session. For example, with active cascading, contained entities (e.g. in a one-to-many collection) are now persistent instances as well in the returned object.
Please be aware that
void EntityManager#persist(java.lang.Object entity)
(http://docs.oracle.com/javaee/6/api/javax/persistence/EntityManager.html#persist%28java.lang.Object%29)
Persists the given object by changing the object passed in and does not return a persisted copy - I suspect your ExpenseHelper to return the original object additionally so that you receive the same object via return as you already have by passing it in.
This follows a common anti-pattern for a kind of unified behaviour of DAO to be something like
public T create(T entity) {
this.entityManager.persist(entity);
return entity;
}
to get a kind of synchronicity with saving something
public T save(T entity) {
return this.entityManager.merge(entity);
}
Where
<T> T EntityManager#merge(T entity)
does indeed merge and pass you the merged entity.
It can depend on Hibernate mapping of the Expense entity, or implementation of ExpenseHelper class.
Also, take a look on Expense.equals() implementation.
Based on this statement:
Expense savedExpense = expenseService.save(expenseToSave);
the value of the savedExpense object will depend on what your are doing in the save method. Usually save methods don't return an object. You already have a reference to the object that you just saved (expenseToSave) available to you. And you are trying to assert that your expected object equals the object that was saved, which is fine. So I am not sure what the purpose of returning an object in expenseService.save(expenseToSave)
Also, note that the id of the object expenseToSave would have been populated by your ORM (Hibernate, I assume) based on your configuration, when you save it. There is no need to return this object or another object in the save method.

Hibernate Search is not reindexing based on changes to "calculated" values

We use Hibernate Search (4.1) throughout our application to manage searching and indexing of resources, but sometimes need to manage "calculated" values in the index, eg, calls to #IndexEmbedded or #Field attached to getters without actual properties:
public class Resource {
#ManyToMany(...)
private List<Keyword> keywords = new ArrayList<Keyword>();
public List<Keyword> getKeywords() {
return keywords;
}
public List<Keyword> setKeywords(List<Keyword> keyword>) {
this.keywords=keywords;
};
#IndexedEmbedded
public List<Keyword> getIndexedKeywords() {
List<Keyword> toReturn = new ArrayList<Keyword>();
for (Keyword keyword: getKeywords()) {
if (keyword.isIndexed) {
toReturn.add(keyword);
}
}
return toReturn;
}
}
...
public void saveResource(Resource resource, Collection<Keyword> keywords) {
resource.getKeywords().retainAll(keywords);
resource.getKeywords().addAll(keywords);
session.saveOrUpdate(resource);
// will trigger a persist in the database correctly, but will not trigger a reindex
};
but calling saveResource does not cause HibernateSearch to reindex. Shouldn't it?
HibernateSearch 4.1 (or anything after 3.4) uses 'smart' checks to see whether reindexing needs to happen, which matches the #IndexedEmbedded calls to Hibernate's persisted fields. If these do not match, than Hibernate Search does not reindex the object, because the checks do not see anything that matches, and thus will not call reindex. This is likely because HibernateSearch would have to either perform tremendous amounts of reflection and introspection on indexed fields to determine whether underlying values have changed, or calculate and store the result of each call such that it can determine whether the value has changed or not, thus both slowing down the reindexing process and/or bloating the lucene index with a stored hash or entire result value. Probably best to simply either taint a field that causes the reindexing, or manually reindex the object at the end.

Categories

Resources