#Cacheable() not returning proper cache - spring

I am well aware that there are multiple questions on this topic, but I just can't get the sense of it. The problem seems to be that #CachePut does not add the new value to the #Cacheable list.
After debugging the problem I found out that the problem seems to be in the key.
Here is the code snippet
#CacheConfig(cacheNames = "documents")
interface DocumentRepository {
#CachePut(key = "#a0.id")
Document save(Document document);
#Cacheable()
List<Document> findAll();
}
So when I invoke the save method, the key being used for caching is incrementing integer, or 1,2,3...
But when I try to get all documents, the cache uses SimpleKey[] as key. If I try to use the same key for #Cacheable, I get SpelEvaluationException, property 'id' cannot be found on null.
So what I am left with at the end is functional cache (the data is saved in the cache), but somehow I am not able to retrieve it.
The underlying cache implementation is EhCache.

I really don't understand what you are expecting here.
It looks like you expect your findAll method to return the full content of the cache named documents. I don't think there is anything in the documentation that can let you conclude that this feature exists (it does not). It is also very fragile. If we were implementing that, findAll would return different results based on the state of the cache. If someone would configure this cache to have a max size of 100 for instance. Or If the cache isn't warm-up on startup.
You can't expect a cache abstraction (or even a cache library) to maintain a synchronized view of "a list of objects". What findAll does is returning the entry that corresponds to a key with no argument (new SimpleKey by default).

Related

Will Spring Data's save() method update an entity in the database if there are no changes in the entity?

When editing a form, the user may sometimes not change the form and still click the submit button. In one of the controller methods below, will the save() method perform a query to the database and update the fields even if the user didn't change anything?
PostMapping("/edit_entry/{entryId}")
public String update_entry(
#PathVariable("entryId") Long entryId,
#RequestParam String title,
#RequestParam String text
) {
Entry entry = this.entryRepo.findById(entryId).get();
if (!entry.getTitle().equals(title))
entry.setTitle(title);
if (!entry.getText().equals(text))
entry.setText(text);
this.entryRepo.save(entry);
return "redirect:/entries";
}
And also, are the "if" statements necessary in this case?
What exactly happens during a call to save(…) depends on the underling persistence technology. Fundamentally there a re two categories of implementations:
Implementations that actively manage entities. Examples of this are JPA and Neo4j. Those implementations keep track of the entities returned from the store and thus are able to detect changes in the first place. You pay for this with additional complexity as the entities are usually instrumented in some way and the change detection of course also takes time even if it ends up not detecting any changes. On the upside though the only trigger updates if needed.
Implementations that do not actively manage entities. Examples are JDBC and MongoDB. Those implementations do not keep track of entities loaded from the data store and thus do not instrument them. That also means that there is no way of detecting changes as all the implementation sees is an entity instance without any further context.
In your concrete example, a MongoDB implementation would still issue an update while JPA will not issue an update at all if the request params do not contain differing values.

Difference between CrudRepository findOne() and JpaRepository getOne()

I read that getOne() is lazy loaded and findOne() fetches the whole entity right away. I've checked the debugging log and I even enabled monitoring on my sql server to see what statements gets executed, I found that both getOne() and findOne() generates and executes the same query. However when I use getOne() the values are initially null (except for the id of course).
So could anyone please tell me, if both methods executes the same query on the database, why should I use one over the other? I'm basically looking for a way to fetch an entity without getting all of its children/attributes.
EDIT1:
Entity code
Dao code:
#Repository
public interface FlightDao extends JpaRepository<Flight, Long> {
}
Debugging log findOne() vs getOne()
EDIT2:
Thanks to Chlebik I was able to identify the problem. Like Chlebik stated, if you try to access any property of the entity fetched by getOne() the full query will be executed. In my case, I was checking the behavior while debugging, moving one line at a time, I totally forgot that while debugging the IDE tries to access object properties for debugging purposes (or at least that's what I think is happening), so debugging triggers the full query execution. I stopped debugging and then checked the logs and everything appears to be normal.
getOne() vs findOne() (This log is taken from MySQL general_log and not hibernate.
Debugging log
No debugging log
It is just a guess but in 'pure JPA' there is a method of EntityManager called getReference. And it is designed to retrieve entity with only ID in it. Its use was mostly for indicating reference existed without the need to retrieve whole entity. Maybe the code will tell more:
// em is EntityManager
Department dept = em.getReference(Department.class, 30); // Gets only entity with ID property, rest is null
Employee emp = new Employee();
emp.setId(53);
emp.setName("Peter");
emp.setDepartment(dept);
dept.getEmployees().add(emp);
em.persist(emp);
I assume then getOne serves the same purpose. Why the queries generated are the same you ask? Well, AFAIR in JPA bible - Pro JPA2 by Mike Keith and Merrick Schincariol - almost every paragraph contains something like 'the behaviour depends on the vendor'.
EDIT:
I've set my own setup. Finally I came to conclusion that if You in any way interfere with entity fetched with getOne (even go for entity.getId()) it causes SQL to be executed. Although if You are using it only to create proxy (eg. for relationship indicator like shown in a code above), nothing happens and there is no additional SQL executed. So I assume in your service class You do something with this entity (use getter, log something) and that is why the output of these two methods looks the same.
ChlebikGitHub with example code
SO helpful question #1
SO helpful question #2
Suppose you want to remove an Entity by id. In SQL you can execute a query like this :
"delete form TABLE_NAME where id = ?".
And in Hibernate, first you have to get a managed instance of your Entity and then pass it to EntityManager.remove method.
Entity a = em.find(Entity.class, id);
em.remove(a);
But this way, You have to fetch the Entity you want to delete from database before deletion. Is that really necessary ?
The method EntityManager.getReference returns a Hibernate proxy without querying the database and setting the properties of your entity. Unless you try to get properties of the returned proxy yourself.
Method JpaRepository.getOne uses EntityManager.getReference method instead of EntityManager.find method. so whenever you need a managed object but you don't really need to query database for that, it's better to use JpaRepostory.getOne method to eliminate the unnecessary query.
If data is not found the table for particular ID, findOne will return null, whereas getOne will throw javax.persistence.EntityNotFoundException.
Both have their own pros and cons. Please see example below:
If data not found is not failure case for you (eg. You are just
verifying if data the data is deleted and success will be data to be
null), you can use findOne.
In another case, you can use getOne.
This can be updated as per your requirements, if you know outcomes.

cache eviction in spring using regex key

I am using spring cache where my cache should be updated and deleted for a particular key.
below is my code for inserting value in cache
#Cacheable(value = CACHE_NAME, key = "")
public InputStream getFiles(String fileName, String id) {
clearCache(fileName, id);
return restTemplate.getForObject(configurationFileUrl, InputStream.class, id);
}
for deleting
#CacheEvict(value = CACHE_NAME, key = " ")
public void clearCache(String fileName, String id) {
}
Here I am trying to pass the key as SpEL with regular expression which will delete the key starting with same file name but different than latest updated key. e.g if the cache had below entry
krishna1 -> obj1
now obj got modified in some other projects DB and now it is maintaining record
Krishna2 for reference to modified Obj1
So once call come to my service I need to check for combination name+key in cache, if it is not there I should insert new entry(Krishna2), but now my old entry with key name Krishna1 will never be used, so How do I delete it.
I can not create Key with filename otherwise it will not able to identify if file got modified or not.
Problem Statement:
suppose that I have one micro service which is maintaining the DB access and is deployed inside my domain name it Service1. The DB is containing 3 java class class1 -> for drawing rectancle, class2-> drawing circle and class3 -> for triangle. Now think you have micro service deployed outside somewhere and it is getting request to draw shape. Take the scenario that it got request to draw circle and it got config saying class2 and V1(version info) is responsible to draw circle. so it will make a rest call to service1 download class2 and compile it and cache it .class, so that next request it can serve directly if there is no change in draw circle logic(class2). The change in class2 may occur in future but rarely. Now coming to the problem next time once it got the request it will first check if it already has the class with same version if yes then draw the circle otherwise it will make rest call to get the update java file and will compile and cache it in memory. This is the reason I appended Version(id) appended to key so that it can differentiate if file is already there and changed.
Yeah, I am with #Hanno Binder on this one; seems you may have a design problem, specifically in the way you are trying to version objects in the Cache using the key to distinguish versions.
(Disclaimer) I am not fully understanding your requirements here, but it seems you could just simplify things a bit by keeping the latest (updated) version of an object in the cache using the same key. Why do the users of the cached object care what version is in use as long as they get the latest when it is available.
In this case, it is real simple to make sure the latest version is the one cached (and returned from #Cacheable methods) without having to resort to "special" eviction criteria by using the Spring #CachePut annotation. Then, you only need to make sure that all updates are routed though the method annotated with #CachePut. Subsequently, all #Cacheable methods called will get the latest "version".
Again, I apologize if I missed the mark on your requirements.

Spring/EhCache removing specific keys from the cache

#Cacheable(cacheName = "cacheOne")
public Map<String, Object> getSomeData(List<String> taglist,String queryString) {
I am using ehcache with Spring as shown in the code above. I can clear all the keys in cacheOne by doing this :
cacheManager.getCache("cacheOne").removeAll();
But what if I need to remove only those keys from this cache where taglist contains a particular tag? For e.g. I want to remove all the entries in cacheOne where there taglist contains a tag cricket?
I am afraid what you are asking will be on your shoulders.
What you are requesting, in addition to the caching you already perform, is a mapping between tags (cricket in your example) and the keys that contain these tags.
In order to store this mapping, you probably need to devise your own KeyGenerator that will keep track of this mapping in parallel of creating the cache key.
This mapping could even be smart if only a subset of tags are concerned by this purge need.
By default Spring will not keep track of that information for you, so you will not have a configuration based way of doing it.
The other option - not recommended - is to brute force by iterating over all the keys. And it should be pretty clear why this is a bad idea as soon as your dataset grows.

Fetch annotation in SDG 2.0, fetching strategy questions

Hi all patient developers using spring data graph. Since there is so less documentation and pretty poor test coverage it is sometimes very difficult to understand what is the expected behavior of the underlying framework how the framework is supposed to work. Currently i have some questions related to new fetching approach introduced in SDG 1.1. As opposite to SDG 1.1 write\read through in 2.0 only relations and related object annotated with #Fetch annotation are eagerly fetched others are supposed to be fetched lazily .. and now my first question:
Is it possible to configure SDG so that if the loading of entity and
invoking getter on lazy relation takes place in the same transaction,
requested collection is fetch automatically? Kind of Persistence
Context in transaction scope, or maybe it is planned for the feature
releases.
How can I fetch lazy collection at once for #RelatedTo annotation ? fetch() method on from Neo4jOperation allows to fetch only one entity. Do i have to iterate through whole list and fetch entity for each object? What would be the best way to check if given object is already fetched / initialized or not?
As suggestion i think it would be more intuitive if there will be kind of lazy loading exception thrown instead of getting NPE when working with not initialized objects. Moreover the behavior is misleading since when object is not initialized and all member properties are null apart from id, equals method can provide true for different objects which has not been initialized, which is quite serious issues considering for example appliance of sets
Another issue which i noticed when working with SDG 2.0.0.RC1 is following: when i add new object to not fetched collection sometimes is properly added and persisted,however sometimes is not. I wrote test for this case and it works in non deterministic way. Sometimes it fails sometimes end with success. Here is the use case:
Group groupFromDb = neoTemplate.findOne(group.getId(), Group.class);
assertNotNull(groupFromDb);
assertEquals("Number of members must be equals to 1", 1, groupFromDb.getMembers().size());
User secondMember = UserMappingTest.createUser("secondMember");
groupFromDb.addMember(secondMember);
neoTemplate.save(groupFromDb);
Group groupAfterChange = neoTemplate.findOne(groupFromDb.getId(), Group.class);
assertNotNull(groupAfterChange);
assertEquals("Number of members must be equals to saved entity", groupFromDb.getMembers().size(), groupAfterChange.getMembers().size());
assertEquals("Number of members must be equals to 2", 2, groupAfterChange.getMembers().size());
This test fails sometimes on the last assert, which would mean that sometimes member is added to the set and sometimes not. I guess that the problem lies somewhere in the ManagedFieldAccessorSet, but it is difficult to say since this is non deterministic. I run the test with mvn2 and mvn3 with java 1.6_22 and 1.6_27 and i got always the same result: sometimes is Ok sometimes test fails. Implementation of User equals seems as follows:
#Override
public boolean equals(final Object other) {
if ( !(other instanceof User) ) {
return false;
}
User castOther = (User) other;
if(castOther.getId() == this.getId()) {
return true;
}
return new EqualsBuilder().append(username, castOther.username).isEquals();
}
- I find it also a bit problematic that for objects annotated with #Fetch java HashSet is used which is serializable, while using for lazy loaded fields ManagedFieldAccessorSet is used which is not serializable and causes not serializable exception.
Any help or advice are welcome. Thanks in advance!
I put together a quick code sample showing how to use the fetch() technique Michael describes:
http://springinpractice.com/2011/12/28/initializing-lazy-loaded-collections-with-spring-data-neo4j/
The simple mapping approach was only added to Spring Data Neo4j 2.0, so it is not as mature as the advanced AspectJ mapping. We're currently working on documenting it more extensively.
The lazy loading option was also added lately. So your feedback is very welcome.
Right now SDN doesn't employ a proxy approach for the lazily loaded objects. So the automatic "fetch on access" is not (yet) supported. That's why also no exception is thrown when accessing non-loaded fields and there is no means of "discovering" if an entity was not fully loaded.
In the current snapshot there is the template.fetch() operation to fully load lazy loaded objects and collections.
We'll look into the HashSet vs. ManagedSet issue, it is correct that this is not a good solution.
For the test-case. is the getId() returning a Long object or a long primitive? It might be sensible to use getId().equals(castOther.getId()) here as reference equality is not guaranteed for Number objects.

Resources