How is the #Cacheable annotation notified about the changes in the database. For eg : If I Cache the result of CRUDRepository.findAll() function call by annotating the function call by #Cacheable the result is stored in a named cache. The Cache however continues to be updated with DB changes. Consider the below example :
#Cacheable(value = "employee", key = "#id")
public Iterable<Employee> getAllEmployees(){
long startTime = System.currentTimeMillis();
Iterable<Employee> itr = employeeEntityRepository.findAll();
long stopTime = System.currentTimeMillis();
System.out.println("Time-->" + (stopTime - startTime));
return itr;
}
When this method is called the first time it takes 300 ms and the 2nd time it takes hardly 5 ms which means it Caches the result set. Fair enough. But now if I update a record in the employee table directly in the DB and call this method again two things happen :
The new record is returned by this method call.
The method returns very fast.
This means that the cache is updated from the DB every time. How does this Sync work if the resultset is returned from Cache ?
thanks.
The main question is: cache is updated from the DB every time. How does this Sync work if the resultset is returned from Cache?
As per spring doc (5.3.7) #Cacheable annotation is used when we need to cache the result of a call.
in the above scenario caching behavior will be applied, it's checked if the method was already invoked for the given arguments.
below attributes are used in your example. Their description is as below:
value : this name of cache, in above case its "employee"
Key : this is SpEl, Spring Expression Language, to compute the key dynamically.
Ideally, you should have "id" as method-argument in the method getAllEmployees().
But this method seems to return all employees, so it seems incorrect usage in this scenario.
SpEl expression is "#id" = a custom key generator
By default it is "" = all method parameter are considered as key.
If no value is found in the cache for the computed key, the target method will be invoked and returned value will be stored in the associated cache.
The scenario which I encountered is that the method which is annotated by #Cacheable
#Cacheable(value= myConstant,key="#accountId")
public Integer getAccountNumber( Integer accountId){.
// an API call is made here..}
So, cache is not updated from DB, it's updated from a call (API call) next, the result is stored in the cache when the method is invoked for the first time. When the method is called a second time the cached result is returned and API call in the method do not happen.
I have referred: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/cache/annotation/Cacheable.html
Related
I'm experiencing what seems to be a record lock between sequential (not concurrent) database operations, which I can't explain.
Situation
Method saveRegistrationToken is called from a REST controller. I test the calls to the method via Postman (HTTP client); the method is not called anywhere else, this is the only operation executed.
The method execution is supposed to behave as follows:
Take a String (registration token) and a user ID (also a string) as input
It should first update a USERS table, setting the value of the REGISTRATION_TOKEN column to null for every row where the column has the same value as the input registration token
It should then update the USERS table for the row with the specified user ID, setting the value of the REGISTRATION_TOKEN column to the input registration token.
Problem
Every first execution of the method will behave as expected: sets the value of the DB column REGISTRATION_TOKEN (table USER) to null wherever it is the specified value, and then sets the registration token to the input value for the row with the input user ID. As such, the value of the registration token for the row in question is the input value at the end of the execution of the method.
Every second execution will correctly do the first step ("void" the registration token wherever it exists) but does not update the value for the row with the specified user ID. As such, the value of the registration token for the row in question is null at the end of the execution of the method.
DefaultUserService.java
#Override
public void saveRegistrationToken(String userId, String registrationToken) {
usersRepository.voidRegistrationToken(registrationToken);
User u = usersRepository.findById(userId).get();
u.setRegistrationToken(registrationToken);
usersRepository.save(u);
}
UsersRepository.java
#Repository
public interface UsersRepository extends JpaRepository<User, String> {
#Modifying
#Transactional
#Query(value = "UPDATE USERS " +
"SET registration_token = null " +
"WHERE registration_token = :registration_token",
nativeQuery = true)
void voidRegistrationToken(#Param("registration_token") String registrationToken);
}
User.java
#Entity(name = "users")
#AllArgsConstructor //lombok
#Data
#NoArgsConstructor
#ToString
#EqualsAndHashCode
public class User {
#Id
private String id;
private String registrationToken;
private String email;
}
What I've tried
I initially thought it would be a flush problem: that once the registration token had been set to null everywhere, the transaction would not be flushed until after the registration token had been set again for the user ID, leading to conflicting behaviour between both DB operations. I disproved that explicitly calling usersRepository.flush(); after the first operation, and observing the same behaviour.
I tried different propagation and isolation levels on the repository operation: #Transactional(propagation = Propagation.SUPPORTS, isolation = Isolation.READ_UNCOMMITTED), which didn't help.
I tried explicitly setting the flush mode on the repository operation: #QueryHints(value = { #QueryHint(name = org.hibernate.annotations.QueryHints.FLUSH_MODE, value = "ALWAYS") }) , which didn't change anything.
It now seems to me that the first operation "locks" the updated record, which prevents the second operation from updating it, but I don't understand how.
Explicitly specifying auto-commit true: spring.datasource.auto-commit=true
Dependencies: compile("org.springframework.boot:spring-boot-starter-data-jpa") effectively version 2.1.1.RELEASE
Any ideas, explanations, links to docs would be very much appreciated - I've tried everything I can think of.
Many thanks, Chris
UPDATE:
Another reason I think it's some kind of flush problem.
I updated this method as follows:
#Override
public void saveRegistrationToken(String userId, String registrationToken) {
usersRepository.voidRegistrationToken(registrationToken);
String check = usersRepository.findById(userId).get().getRegistrationToken();
/* breakpoint on the following line */
User u = usersRepository.findById(userId).get();
u.setRegistrationToken(registrationToken);
usersRepository.save(u);
}
When stopping at the breakpoint where indicated:
Every first ("normal") execution, the value of the check variable is null
Every second execution, its value is the same as the input registration token
Although I always prefer to mark the service method as #Transactional as a whole, reviewing your code, I think you defined the appropriate transaction demarcation in your methods, by explicitly define the #Transactional annotation in voidRegistrationToken, and by using the methods provided by JpaRepository, implicitly annotated in such a way.
In any case, as you indicated, as a result of performing the different operations over the User who will be assigned the registration token, you are obtaining inconsistent values.
It is a clear indication that the information of the affected User entity maintained by the EntityManager in the persistence context is being polluted somewhere across the different methods invocation.
I honestly cannot give you the exact reason about this behavior.
It may have to do with the moment in which the changes are flushed to the database, to entirely discretion of the EntityManager, but you already tried to flush the different changes manually and your transactions seems appropriate and, as a consequence, it will probably not be the cause of the problem.
Maybe it have to do with a second level cache as #Guillaume suggested, maybe with the way the #Modifying operation is implemented by Spring Data.
One think you can try is to instruct your #Modifying annotation to clear the persistence context once the operation is completed:
#Modifying(clearAutomatically = true)
This will provide you a clean state to perform the registration token update.
Please, see the relevant docs.
Please, be aware that the possible implications of the use of this solution.
The use of flush and clear in EntityManager is an anti-pattern, something that should be avoided if you can by using the appropriate transaction demarcations and component architecture.
The call of clear leads to all objects being decoupled from the EntityManager. Be aware that, depending of the use case, even modified object data will not be saved to the database - this is the main difference with flush that always will preserve the modifications performed to the entities managed in the persistence context before detach them.
In any case, due to the way your transactions are defined, probably in your use case it will work properly.
When an object is written to database and the primary identifier (id) is known, it can be retrieved by the code below:
MyObject myObject = session.get(Class<MyObject>, id);
It seems, there is another way similar to get() method:
IdentifierLoadAccess<MyObject> ila = session.byId(Class<MyObject>);
MyObject myObject = ila.load(id);
I'm looking for a scenario which clarifies differences between them and describes the reason for having two similar methods for the same job in API.
same question can be asked about session.load() and session.byId().getReference().
Edit 1:
According to API documentation:
session.get() and session.byId().load() return persistent instance with given identifier, or null if there is no such persistent instance.
session.load() and session.byId().getReference() might return a proxied instance that is initialized in demand.
IdentifierLoadAccess allows you to specify:
LockOptions
CacheMode
even specifying both of them at once:
Post post = session
.byId( Post.class )
.with( new LockOptions( LockMode.OPTIMISTIC_FORCE_INCREMENT) )
.with( CacheMode.GET )
.load( id );
The same for getting a Proxy reference via getReference(id).
So, they are more flexible than the standard get or load which only take the entity identifier.
The similarity between
MyObject myObject = session.get(Class<MyObject>, id);
and
IdentifierLoadAccess<MyObject> ila = session.byId(Class<MyObject>);
MyObject myObject = ila.load(id);
is that both uses the copncept of hibernate cache mechanism but difference comes in fetching the data from database i.e
When we use session.get(Class,id) data from database comes in cache and you can make changes on that data and will be reflected back in database, as hibernate internally maintains a time stamp cache. This time stamp cache records the time at which a particular Hibernate managed table got modified and before returning the data from entity cache it validate whether the result cache are older with respect to table modification time.
But in case of session.byId().getReference() hibernate uses the concept of natural id in which data from database comes in cache but only onces.If you do any changes on that data using session.save(entity object) approach hibernate will throw an exception and if you do manually modification of table(insert,update,delete) it will not be reflected back when you fetch the data again as it always get the data from cache without checking whether that table for that entity has been modified again or not.
In case of session.get() and session.load() if there is any change in database like (insert,delete,update) of record it will get reflected either in the form of record or null pointer exception if record gets deleted.But in case of session.byId().load() and session.byId().getReference() it will first get the record from database when you try to fetch first time then it will save those record in session and will be shown to user from session only if any (insertion,deletion,updation) occurs then it will not be reflected back
It's mostly used in polymorphic association/queries. assume you have an entity named User with the BillingDetails association. If BillingDetails was mapped with
lazy="true" (which is the default), Hibernate would proxy the association target. In this case, you wouldn’t be able to perform a type-cast to the concrete class CreditCard (which is a subclass of BillingDetails) at runtime, and even the instanceof operator would behave strangely:
User user = (User) session.get(User.class, userid);
BillingDetails bd = user.getDefaultBillingDetails();
System.out.println( bd instanceof CreditCard ); // Prints "false"
CreditCard cc = (CreditCard) bd; // ClassCastException!
To perform a proxy-safe typecast, use load()
User user = (User) session.get(User.class, userId);
BillingDetails bd = user.getDefaultBillingDetails();
// Narrow the proxy to the subclass, doesn't hit the database
CreditCard cc =
(CreditCard) session.load( CreditCard.class, bd.getId() );
expiryDate = cc.getExpiryDate();
Note that you can avoid these issues by avoiding lazy fetching, as in the follow-ing code, using an eager fetch query
User user = (User)session.createCriteria(User.class)
.add(Restrictions.eq("id", uid) )
.setFetchMode("defaultBillingDetails", FetchMode.JOIN)
.uniqueResult();
// The users defaultBillingDetails have been fetched eagerly
CreditCard cc = (CreditCard) user.getDefaultBillingDetails();
expiryDate = cc.getExpiryDate();
Truly object-oriented code shouldn’t use instanceof or numerous typecasts. If you find yourself running into problems with proxies, you should question your design, asking whether there is a more polymorphic approach.
The key difference between get() and load() method is that load() will throw an exception if an object with id passed to them is not found, but get() will return null. Another important difference is that load can return proxy without hitting the database unless required (when you access any attribute other than id) but get() always go to the database, so sometimes using load() can be faster than the get() method. It makes sense to use the load() method if you know the object exists but get() method if you are not sure about object's existence.
We are using Spring and IBatis and I have discovered something interesting in the way a service method with #Transactional handles multiple DAO calls that return the same record. Here is an example of a method that does not work.
#Transactional
public void processIndividualTrans(IndvTrans trans) {
Individual individual = individualDAO.selectByPrimaryKey(trans.getPartyId());
individual.setFirstName(trans.getFirstName());
individual.setMiddleName(trans.getMiddleName());
individual.setLastName(trans.getLastName());
Individual oldIndvRecord = individualDAO.selectByPrimaryKey(trans.getPartyId());
individualHistoryDAO.insert(oldIndvRecord);
individualDAO.updateByPrimaryKey(individual);
}
The problem with the above method is that the 2nd execution of the line
individualDAO.selectByPrimaryKey(trans.getPartyId())
returns the exact object returned from the first call.
This means that oldIndvRecord and individual are the same object, and the line
individualHistoryDAO.insert(oldIndvRecord);
adds a row to the history table that contains the changes (which we do not want).
In order for it to work it must look like this.
#Transactional
public void processIndividualTrans(IndvTrans trans) {
Individual individual = individualDAO.selectByPrimaryKey(trans.getPartyId());
individualHistoryDAO.insert(individual);
individual.setFirstName(trans.getFirstName());
individual.setMiddleName(trans.getMiddleName());
individual.setLastName(trans.getLastName());
individualDAO.updateByPrimaryKey(individual);
}
We wanted to write a service called updateIndividual that we could use for all updates of this table that would store a row in the IndividualHistory table before performing the update.
#Transactional
public void updateIndividual(Individual individual) {
Individual oldIndvRecord = individualDAO.selectByPrimaryKey(trans.getPartyId());
individualHistoryDAO.insert(oldIndvRecord);
individualDAO.updateByPrimaryKey(individual);
}
But it does not store the row as it was before the object changed. We can even explicitly instantiate different objects before the DAO calls and the second one becomes the same object as the first.
I have looked through the Spring documentation and cannot determine why this is happening.
Can anyone explain this?
Is there a setting that can allow the 2nd DAO call to return the database contents and not the previously returned object?
You are using Hibernate as ORM and this behavior is perfectly described in the Hibernate documentation. In the Transaction chapter:
Through Session, which is also a transaction-scoped cache, Hibernate provides repeatable reads for lookup by identifier and entity queries and not reporting queries that return scalar values.
Same goes for IBatis
MyBatis uses two caches: a local cache and a second level cache. Each
time a new session is created MyBatis creates a local cache and
attaches it to the session. Any query executed within the session will
be stored in the local cache so further executions of the same query
with the same input parameters will not hit the database. The local
cache is cleared upon update, commit, rollback and close.
What is difference between get () and load() method? with respect to data fetching approach
public static void main(String[] args) {
SessionFactory factory= new Configuration().configure().buildSessionFactory();
Session session = factory.openSession();
Transaction tx = null;
tx = session.beginTransaction();
System.out.println("1 st time calling load method");
Account acc =
(Account)session.load(Account.class, 180);
System.out.println("bal"+acc.getBalance());
System.out.println("2nd time calling load method");
Account acc1=(Account)session.load(Account.class, 180);
System.out.println("bal"+acc1.getBalance());
System.out.println("1 st time calling get method");
Account acc2= (Account) session.get(Account.class, accId);
System.out.println("bal"+acc2.getBalance());
System.out.println("2 st time calling get method");
Account acc2= (Account) session.get(Account.class, accId);
System.out.println("bal"+acc2.getBalance());
tx.commit();
session.close();
}
I got following output
1 st time calling load method
Hibernate:
/* load com.abcd.Account */ select
account0_.ACCOUNTID as ACCOUNTID1_0_,
account0_.ACCOUNTTYPE as ACCOUNTT2_1_0_,
account0_.CREATIONDATE as CREATION3_1_0_,
account0_.BALANCE as BALANCE1_0_
from
a.MYACCOUNT account0_
where
account0_.ACCOUNTID=?
bal3000.0
2nd time calling load method
bal3000.0
1 st time calling get method
bal3000.0
2 st time calling get method
bal3000.0
From ouput it is clear that get method did not hit database.It behaves like load() method. Could any one tell me is this behavior correct.
As T Mishra states here:
By default, hibernate creates run-time proxies. It loads the objects as a proxy unless a fetch mode is specified or set to false.
That's because once the object is loaded in cache, the next subsequent calls perform repeatable read.
Although the state of this object changes from persistent to detached
The entity can be retrieved in 2 ways.
load() - returns the proxy object with an identifier.
get() - returns the complete object from database.
for more details click this link
Actually, both functions are use to retrieve an object with different mechanism,
session.load()
It will always return a “proxy” (Hibernate term) without hitting the database. In Hibernate, proxy is an object with the given identifier value, its properties are not initialized yet, it just look like a temporary fake object.
If no row found , it will throws an ObjectNotFoundException.
session.get()
It always hit the database and return the real object, an object that represent the database row, not proxy.
If no row found , it return null.
When you call session.load() method, it will always return a “proxy” object, whats the meaning of proxy object ?
Proxy means, hibernate will prepare some fake object with given identifier value in the memory without hitting the database, for example if we call session.load(Student.class,new Integer(107)); > hibernate will create one fake Student object [row] in the memory with id 107, but remaining properties of Student class will not even be initialized.
GET
When you call session.get() method, it will hit the database immediately and returns the original object.
If the row is not available in the database, it returns null.
hibernatesession.get() will fetch the real object from the database and hibernatesession.load() will return proxy without hitting the database. For more details click here. It explains get and load method and their difference with example codes.
Use load:
If you are sure about the object availability that you are retrieving
from DB. Else you will end up catching ObjectNotFoundException.
When you have heavy object to be loaded (Since it loads lazily whenever you use it)
Use get:
If you are not sure about the object availability in the DB.
You get luxury to check for null, when there is not object available in DB.
When you have light object to be loaded (since it loads eagerly).
** Load:** Whenever the load() method is called, the hibernate creates a proxy object of a POJO class, and it will set the id to the proxy object, then it returns the proxy object to the program. Based on the operations performed on the proxy object, the hibernate will decide whether to go cache or database to load the data. This process is called lazy loading.
** Get:** When we call the get() method, then hibernate first goes to first level cache and if that object doesn’t exist in the first level cache then it goes to the database and loads the object from the database. If Id doesn’t exist in the database, then get() method returns null. When get() method is called no proxy object is created, hence it is called as early loading.
Ref : http://docs.jboss.org/hibernate/orm/4.3/javadocs/
Complete Example you can find # my blog: http://www.onlinetutorialspoint.com/hibernate/hibernate-session-differences-between-load-and-get.html
Here is a simple explanation of what you need:
http://www.mkyong.com/hibernate/different-between-session-get-and-session-load/
Or by looking at the API:
http://docs.jboss.org/hibernate/orm/4.3/javadocs/
with get : Return the persistent instance of the given named entity. The persistent one, so the one stored in the database.
with load: Read the persistent state associated with the given identifier into the given transient instance.
In the first link you find very useful examples to test the differences..
Load: when we call session.load() method it doesn't hit directly database. It creates and return proxy object if object didn't belongs in db it throws "ObjectNotFountException". And supports lazy loading.
Get: It hits directly object in db and gives original value if object not found then it returns null. And it supports eager loading.
From the above example - both the functions are not working same.
load() - hibernate will only load proxy of the object with the specified ID. All the properties will not be set in advanced. Once you call the getter methods on the object, the query will be issues.
So when you can a getAccount method , select query will be issued and the result object will be saved in cache as it is retrieved by ID. So any subsequent calls via get will not result any select statement.
get() - will always retrieve the object from database with full properties populated. For properties defined in collections, depends on lazy initialization configuration.
Please note - Any subsequent calls on the same session will return the object from the cache only and hence no queries will be returned. That is what happening with the call to get method in the first time and the second time as the object is ready in cache by calling the acc.getBalance() on the object retrieved by load.
Don't forget performance aspect between get and load method
The get() method fetches data as soon as it’s executed while the load() method returns a proxy object and fetches only data when object properties is required.
So that the load() method gets better performance because it support lazy loading.
Whe should use the load() method only when we know data exists because it throws exception when data is not found.
You can see the example that demo this difference on the tutorial Difference between get and load method in Hibernate
When you call session.load() method, it will always return a proxy object, whats the meaning of proxy object ?
Proxy means, hibernate will prepare some fake object with given identifier value in the memory without hitting the database, for example if we call.
session.load(Student.class,new Integer(107));
hibernate will create one fake Student object [row] in the memory with id 107, but remaining properties of Student class will not even be initialized, observe this graphical representation…
It will hit the database only when we try to retrieve the other properties of Student object I mean stdName, stdCountry.
If we call s2.getStdName() then hibernate will hit the database and search the row with student id 107 and retrieve the values, if object [row] not found in the database it will throws ObjectNotFoundException.
session.get()
When you call session.get() method, it will hit the database immediately and returns the original object.
If the row is not available in the database, it returns null.
So which is the best method to use, hibernate load() or get()?
It's completely your choice .
I'm having trouble with #Cacheable using with multi parameters and pagination.
#Cacheable(value = "books", key = "#p0")
public List<Book> findBooks(Long loggedUserId, String q, Integer firstResult, Integer maxResults) {
...
return result;
}
The problem is when I call the method by the first time, the content is cached with success and the result is like I expect. But when I call by the second time, the result returned is equals to the first time. If I disable the cache or remove the key the results are different.
The use of the key is obligatory because sometimes the cache of a specified user is removed.
Thanks.
I think your issue is a lack of understanding of the Cacheable annotation, and you you are using it. So let me see if I can help
Your annotation is #Cacheable(value = "books", key = "#p0")
This means that when this method is called it will take the first parameter and look it up in the cache and if there is a result, it will be returned instead of executing the method. IT IS ONLY CHECKING THE FIRST PARAMETER.
Your cache key needs to be something that uniquely identifies the result set. In this case user id is common across multiple results, and does not uniquely identify a page.
You know your use case better than I, but something like this would probably be better:
#Cacheable(value = "books", key = {"#p1","#p2","#p3"})
public List<Book> findBooks(Long loggedUserId, String q, Integer firstResult, Integer maxResults) {
...
return result;
}
The above will cache based on the search query and the index of the page (start and end of results). Since I don't know your use case I don't know why user id is in there, but from the information I have those three should uniquely identify a result page.