I have two methods as below .One is annotated with #CachePut and another is annotated with #Cacheable.Both of them uses the same key . Based on some of the answers from other posts I figured that #cacheput replaces the value. If that is the case , would the method annotated with #Cacheable be executed for the same id if there is an update to one of the fields in document.
#CachePut(value="testcache",key="#document.id")
public Document update(Document document){
// code to update db
return document
}
#Cacheable(value="testcache")
public Document getDocument(String id){
// query database and fetch document
return document
}
The method annotated with #Cacheable will only be executed if the key has no value in the cache. So, it does not matter whether your #CachePut has updated the document or not. As long as the document is in the cache, getDocument(id) would not go to database. (CachePut does the db & cache update for you)
Related
I am working by spring data to access database and do not use any cache. I have a problem that after deleting a record from database, I am sending an event to other micro system to re-query to update list of objects. So basically my code is :
private void deleteObject(MyObject object) {
myRepository.deleteById(object.getId());
myRepository.flush();
...
sendEventToSystemX();
}
Basically other micro service captures the event which is sent by sendEventToSystemX method and make a query to myRepository.
#Transactional(isolation = Isolation.READ_UNCOMMITTED, readOnly = true)
public Page<T> findAll(Specification<T> spec, Pageable pageable) {
TypedQuery<T> query = getQuery(spec, getDomainClass(), pageable.getSort());
if(pagable.isUnpaged()) {
return new PageImpl<>(query.getResultList())
}
return readPage(query, getDomainClass(), pageable, spec);
}
So note that I am flushing repo after deletion. And select query is done by different service so it is not in the same transaction. So why I still get deleted object for the first time I query after deletion. If I re-run findAll method then I get up-to data result. And this also does not happen always. So what can be reason behind it ?
I have a impl class where I have 2 update methods , method1 is updating complete row in DB whereas method2 is updating only one column in DB of that table.
Now I need to use #Caceable and #CacheEvict here , How can I use in this condition ?
From Spring 3.1 introduces a new feature to allow methods to be cached and evicted thus allowing resource heavy methods to be avoided where possible. Caching is enabled via the new #Cacheable and #CacheEvict annotations.
One example of caching would be, for example, database activity. We can apply the #Cacheable annotation to a find operation and then apply the #CacheEvict to an update / delete operation. In this sense, caching would work much like a second level cache in Hibernate or JPA.
To enable caching on a find method, the method needs to be annotated with the #Cacheable annotation identifying which cache to use. Spring allows multiple caches to be defined each of which can be backed by a different caching abstraction.
#Cacheable("items") //#Cacheable(value = "items", key = "#itemId")
public Item find(long itemId) {
Item item = entityManager.find(Item.class, itemId);
return item;
}
When it is time to invoke the find method, Spring checks in the specified cache to see if the results of the operation have already been cached and if the results can be therefore be returned from cache instead of invoking the method. Spring uses the method arguments as the key, so in this case the itemId parameter.
To evict an entry from the cache when an object is updated in the database, the #CacheEvict annotation can be used. Again, this annotation takes a parameter identifying which cache to use.
#CacheEvict(value = "items", key = "#item.id")
public void updateItem(Item item) {
entityManager.merge(item);
}
EDIT:
#CacheEvict
Used for Cache-removal /cache-cleanup operation. #CacheEvict annotation indicates that a method (or all methods on a class) triggers a cache evict operation, removing specific [or all] items from cache. Various attributes provides complete control to enforce the required behavior for cache-eviction.
for example,
#CacheEvict(value = "products", key = "#product.name")
public void refreshProduct(Product product) {
//This method will remove only this specific product from 'products' cache.
}
#CacheEvict(value = "products", allEntries = true)
public void refreshAllProducts() {
//This method will remove all 'products' from cache, say as a result of flush-all API.
}
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.
A Grails 2.3.4 application is connecting to an Oracle database using the following domain class:
class Person {
String name
static mapping = {
id column: "PERSON_ID", generator: "sequence", params: [sequence: 'person_seq']
}
}
The PersonController makes a call to a method in PersonService and it makes a call to UtilService. The method in UtilService being called has some logic based on wether this Person object is new:
if (personInstance.id == null) { ... }
What I have found is that the id property of personInstance (which is passed through the method calls described above) is assigned when the UtilService is called.
The controller action calling PersonService is #Transactional, and the services do not have any transaction configuration.
So, a couple of questions:
When is id value assigned by GORM (I assumed at insert but that seems wrong)?
Is there a better way of checking if the object is new (isAttached() returns true so that's not good for me)?
EDIT: save() has not been called on the personInstance when UtilService does the id check.
The id is assigned when you call save(). For most persistence calls, Hibernate delays flushing the change until it feels it has to flush) to ensure correctness. But save() calls are treated differently. I believe the motivation is that even if we know that flush() will eventually be called, even if it's at the very end of the request, we want to retrieve the id early so it doesn't suddenly change.
Note that a service that does "not have any transaction configuration" is transactional - the only way to get a non-transactional service is to remove all #Transactional annotations (the newer Grails annotation and the older Spring annotation) and add
static transactional = false
All other services are transactional, although you can configure individual methods to be ignored ## Headin.
Turns out, I had a findBy which was flushing the session:
utilService.someMethod(Person.findByUsername(username))
It was at this point that the id was populated.
Got around it by using withNewTransaction:
def personInstance = Person.withNewSession { Person.findByUsername(username) }
utilService.someMethod(personInstance)
Which now leads me onto the next question...
I have an #ModelAttribute("myModel") that is injected into a spring form and also stored as part of the session in a #SessionAttribute("myModel").
At the controller, the model is picked up and changes merged into the #SessionAttribute.
#RequestMapping(value = "/save", method = RequestMethod.POST)
public String save(#ModelAttribute("myModel") MyModel myModel) {
myModel.getSomething();
...
}
The problem is that all my hibernate domain objects are LAZY. Because the Hibernate Session that created myModel no longer exists, when I try to access someting that was not included as part of a fetch statement in the HQL, I get a LazyInitializationException.
Hibernate has a merge() method, how can I use this to merge an object created in a previous hibernate session into the current one so that all the LAZY properties can be accessed? Or is there another way of doing this?
Call merge(), and use its returned value: it's the attached entity containing the values found in the detached entity passed as argument:
Foo modifiedAttachedFoo = session.merge(modifiedDetachedFoo);
modifiedAttachedFoo.getLazyCollection().size(); // no problem: the entity is attached