Caching de-attached Hibernate entities in external Cache - java

I have a set of very heavy queries whose result I want to cache into an external Cache implementation (cache the whole object list not just ids like in Hibernate's 2nd level cache).
The issue is that due to the lazy loading of several collections in the root object, once the session that queried the results is done, the objects become de-attached and the next request that tries to use the object might throw a LazyLoading exception.
Environment: Spring 4, Hibernate 4.3, Ehcache.
Is there any way to be able to re-attach the object to a new session without having it modify the underlaying DB (like with merge and update)?

There is no way to reattach a detached entity to a session just to load a lazy-initialized collection.
In order to get an updated copy of a persistent object without overwriting the session / calling merge, it's necessary to call either EntityManager.find() or do a query.
This is because the main goal of the session is to keep the database and the objects in memory in sync. Due to this there is no API for attaching new state without persisting it, as this is not in line with the main functionality of the session.
The 2nd level cache, if configured together with the query cache can solve the problem of caching the entities, queries and their associations in a much better way than any custom solution.
Everything can get cached to the point that no query hits the database. The two caches really go together, check this blog post for further info.

Related

How to force Hibernate read external database changes

I have a common database that is used by two different applications (different technologies, different deployment servers, they just use the same database).
Let's call them application #1 and application #2.
Suppose we have the following scenario:
the database contains a table called items (doesn't matter its content)
application #2 is developed in Spring Boot and it is mainly used just for reading data from the database
application #2 retrieves an item from the database
application #1 changes that item
application #2 retrieves the same item again, but the changes are not visible
What I understood by reading a lot of articles:
when application #2 retrieves the item, Hibernate stores it in the first level cache
the changes that are done to the item by application #1 are external changes and Hibernate is unaware of them, and thus, the cache is not updated (same happens when you do a manual change in the database)
you cannot disable Hibernate's first level cache.
So, my question is, can you force Hibernate into refreshing the entities every time they are read (or make it go into the database) without explicitly calling em.refresh(entity)? The problem is that the business logic module from application1 is used as a dependency in application1 so I can only call service methods (i.e. I don't have access to the entityManager or session references).
Hibernate L1 cache is roughly equivalent to a DB transaction when you run in a repeatable-read level isolation. Basically, if you read/write some data, the next time you query in the context of the same session, you will get the same data. Further, within the same process, sessions run independent of each other, which means 2 session are looking at different data in the L1 cache.
If you use repeatable read or less, then you shouldn't really be concerned about the L1 cache, as you might run into this scenario regardless of the ORM (or no ORM).
I think you only need to think about the L2 cache here. The L2 cache is what stores data and assumes only hibernate is accessing the DB, which means that if some change happens in the DB, hibernate might not know about it. If you just disable the L2 cache, you are sorted.
Further reading - Short description of hibernate cache levels
Well, if you cannot access hibernate session you are left with nothing. Any operations you want to do requires session access. For instance you can remove entity from cache after reading it like this:
session.evict(entity);
or this
session.clear();
but first and foremost you need a session. Since you calling only services you need to create service endpoints clearing session cache after serving them or modify existing endpoints to do that.
You can try to use StatelessSession, but you will lose cascading and other things.
https://docs.jboss.org/hibernate/orm/current/userguide/html_single/Hibernate_User_Guide.html#_statelesssession
https://stackoverflow.com/a/48978736/3405171
You can force to start a new transaction, so in this manner hibernate will not be read from the cache and it will redo the read from the db.
You can annotate your function in this manner
#Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW)
Requesting a new transaction, the system will generation a new hibernate session, so the data will not be in the cache.

Hibernate first level cache and get method

I have read that the session.get(Employee.class, new Long(1)) method will take the data from cache or database.
If there are two users who are accessing the application concurrently.
if user - > User1 is doing get then data will be retrieved from DB. Now data is moved to cache.
If user - > User2 has deleted the record or updated the record. then
If user - > User1 is doing get then data will it be retrieved from cache.
Isnt User1 is getting old data. Does it falls to pitfall of caching.
Or am I missing something here?
I can say on this that why User1 is doing 2 times session.get in the same session. But still I need different opinions.
You understand it correctly: the cache is bound to the session, and if an object is loaded into the first-level cache, then no SQL will executed with #get(). You could use #evict() to clear one object from the cache, or #clear() to clear every object from the cache, without closing the session. Closing the session will always delete the entire cache.
See a nice explanation here.
You need to read more about Container-managed entity manager
The most common and widely used entity manager in a Java EE
environment is the container-managed entity manager. In this mode, the
container is responsible for the opening and closing of the entity
manager (this is transparent to the application). It is also
responsible for transaction boundaries. A container-managed entity
manager is obtained in an application through dependency injection or
through JNDI lookup, A container-managed entity manger requires the
use of a JTA transaction.
It's responable of what do you want understand and archive and how is used it.
More doucmentation Entity Mananger
No, because Hibernate saves data on cache, but whether you update the data with Hibernate it will know that some change exists. You will have troubles if you update the data with SQL or from other point where Hibernate cannot see that something happends.

How do i properly cache common objects / entities when using JPA?

I have a web app that needs to load some rows from tables in my database as objects that will be re-used in various places within the application.
For instance, I might retrieve a list of locations that change infrequently, so I want to store those in memory for quicker access later on.
Next, in a future request, I want to associate one of these entities to another entity in the database; however, JPA throws errors when attempting to save, because the cached entity is detached.
How do I reattach the cached entity so I can include it in the current transaction?
I'm using the Apache OpenJPA implementation.
Calling EntityManager.merge() will re-attach a Detached object.
Have a look at this informative page

after performing an merge on the detached object in hibernate in the current session will the changes on the object be tracked?

In a container managed transaction i get a detached object and merge it so that the detached object is brought to managed state.My initial question is by caching the Pojo java objects and merging is a better idea to get the object into session or performing the get of the data from the DB to get in to session context a better idea in terms of cost of operation/time involved in getting the data from the DB?If i am performing an merge at start to get the object into the session context and doing the modification on this merged object will the hibernate take care of generating all the required sql statements and at the end will it be taken care ?
Please comment back which is better approach to get the entity to session , using a merge of the cached detached object or fetching the data from the DB is lesser time consumption?
when you call detach and then merge, merge returns you the attached entity in the context. it's a common mistake that users would use the passed entity after merge operation hoping that would be managed but this is not the case. you have to use the returned entity from merge which will be managed by hibernate and subsequent changes will be flushed at transaction end automatically.
it doesnt matter much when u load your entity coz hibernate will anyways fire a select if it is already not loaded in the context. also even if you keep on doing changes to your managed entity, hibernate will fire update only when you exit your transaction or call flush() explicitly.
Copy the state of the given object onto the persistent object with the same identifier. If there is no persistent instance currently associated with the session, it will be loaded. Return the persistent instance. If the given instance is unsaved, save a copy of and return it as a newly persistent instance. The given instance does not become associated with the session. This operation cascades to associated instances if the association is mapped with cascade="merge".
According to the API it saves a copy when you perform the merge and then returns a new instance. Based on my experience its always better to merge at the end after you have performed all the updates on the objects in detached state. Its better because you will call merge operation only at the end when the object state is ready to be persisted.
Also this will perform better because the object is moved to persistent context at the end and hence Hibernate will not have to come into picture till the end.

How long are Entities persited?

I am trying to understand how JPA works. From what I know, if you persist an Entity, that object will remain in the memory until the application is closed. This means, that when I look for a previously persisted entity, there will be no query made on the database. Assuming that no insert, update or delete is made, if the application runs long enough, all the information in it might become persistent. Does this mean that at some point, I will no longer need the database?
Edit
My problem is not with the database. I am sure that the database can not be modified from outside the application. I am managing transactions by myself, so the data gets stored in the database as soon as I commit. My question is: What happens with the entities after I commit? Are they kept in the memory and act like a cache? If so, how long are they kept there? After I commit a persist, I make a select query. This select should return the object I persisted before. Will that object be brought from memory, or will the application query the database?
Not really. Think about it.
Your application probably isn't the only thing that will use the database. If an entity was persisted once and stored in memory, how can you be sure that, let's say, one hour later, it won't be changed by some other means? If that happens, you will have stale data that can harm logic of your application.
Storing data in memory and hoping that everything will be alright won't bring any benefits. That's why data stored in database is your primary source of information, and you should query it every time, unless you are absolutely sure that a subset of data won't change.
When you persist an entity an entity this will add it to the persistence context which acts like a first level cache (this is in-memory). When the actual persisting happens depends on whether you use container managed transactions or deal with transactions yourself. The entity instance will live in memory as long as the transaction is not commited, and when it is it will be persisted to the database or XML etc.
JPA can't work with only the persistence context (L1 cache) or the explicit cache (L2 cache). It always needs to be combined with a datasource, and this datasource typically points to a database that persists to stable storage.
So, the entity is in memory only as long as the transaction (which is required for JPA persist operations) isn't committed. After that it's send to the datasource.
If the transaction manager is transaction scoped (the 'normal' case) then the L1 cache (the persistence context) is closed and the entities do not longer exist there. If the L1 cache somehow bothers you, you can manage it a bit explicitly. There are operations to clear it and you could separate your read operations (which don't need transactions) from write operations. If there's no transaction active when reading, there's no persistence context, an entity becomes never attached and is thus never put into this L1 cache.
The L2 cache however is not cleared when the transaction commits and entities inside it remain available for the entire application. This L2 cache must be explicitly configured and you as an application developer must indicate which entities should be cached in it. Via vendor specific mechanisms (e.g. JBoss Cache, Infinispan) you can put a max on the number of entities being cached and set/define so-called eviction policies.
Of course, nothing prevents you from letting the datasource point to an in-memmory embedded DB, but this is outside the knowledge of JPA.
Persistence means in short terms: you can shut down your app, and the data is not lost.
To achieve that you need a database or some sort of saving data in a way that it's not lost when you shut down the app.
To "persist" an entity means to actually save it in the data base. Sure, JPA maintains some entity information in memory in the persistence context (and this is highly dependent on configuration and programming practices), but at certain point information will be stored in the data base - for instance, when a transaction commits, or likely (but not necessarily) after flush() or merge() operations.
If you want to keep your entities after committing and for a select query, you need to use the query cache. Just Google around on that term and it should be clear to you.

Categories