JPA EntityManager caching - java

I have an entity defined as follows:
public class Version {
#Id
private Long id;
private String content;
#Transient
private Model model;
//...
}
From what I can see, when a find operation is done on Entity Manager, it makes a SELECT on the underlying database only once, and then the entity is cached in the Entity Manager. However, I see that if I assign a Model to the model property, this change is not reflected to the cached entity. E.g. if in one call, a find operation is done and Model is assigned, when I do find again from another EJB, model property is null again. Is this change not reflected to the cached entity? Perhaps because it's #Transient?

The entity manager maintains a first level cache, and this first level cache is thrown away as soon as the transaction has ended. Else, the cache would return stale values, since other transactions, in the same application or in another one, could modify or remove the cached entities.
Moreover, concurrent transactions each have their own session-level cache, and thus their own instance of the same entity.
If in a subsequent transaction, you find the same entity, a new SQL query will be issued, and a different instance of the entity will be returned.
If something must be remembered across transactions for a given entity, then it should be made persistent in in the database. That's the point of a database.

I have to disagree with #JB Nizet. JPA's EntityManager and Hibernate's Session offer an extended Persistence Context. It is not at all true that "first level cache is thrown away as soon as the transaction has ended".
Persistence Context can be either Transaction Scoped-- the Persistence
Context 'lives' for the length of the transaction, or Extended-- the
Persistence Context spans multiple transactions.
https://web.archive.org/web/20131212234524/https://blogs.oracle.com/carolmcdonald/entry/jpa_caching
The solution however is correct, you have to persist changes to the object if you want it to be changed in the cache.

If you are using EclipseLink then the merge into the shared cache of transients can be configured in two ways.
If a #CloneCopyPolicy is used, then the object from the persistence context will be cloned into the shared cache, preserving the transient fields.
If a #InstantiationCopyPolicy is used, then a new instance will be created for the shared cache, and transients will not be preserved.
If you are using weaving and field access, then the default is #CloneCopyPolicy, otherwise #InstantiationCopyPolicy. You can also configure this using
You can also control what is merged into the shared cache using a DescriptorEventListener and the postMerge/postClone events.

Related

Why would HibernateTemplate.merge() complete without exception but not persist data?

I would like to make this question as generic as possible without submitting extensive code and configuration samples so that answer submitters can cover a wide range of possibilities, therefore make it somewhat "academic".
I have two entity classes, Foo and Bar. They are wired to the persistence store (in my case PostgreSQL but I think that shouldn't matter) using JPA with Hibernate as the provider. They are managed by FooDao and BarDao respectively and both DAOs extend a BaseDao which contains a save method:
public T save(T object)
{
return (T) hibernateTemplate.merge(object);
}
which neither DAO overrides (meaning they use the superclass method as is).
The problem is, when I call myFooDao.save(myFoo), it actually persists the objects to the DB but when I call myBarDao.save(myBar), the object is not persisted, YET NO EXCEPTION IS THROWN.
All of this runs out of a Spring context and both DAOs are injected. I should also add both tables have primary keys each tied to its own sequence. While the Bar insertion never actually gets persisted, the associated sequence does get incremented every time, which is odd. So Hibernate does prepare a transaction but getting the next value from the sequence, which increments the sequence, but the new row never shows in the datable.
I am looking to explore some general circumstances under which anomaly can occur. For one, could it be that the configuration is set so that Foo is auto-committed but Bar is not and I should dive into the context configs to find discrepancies? Or could it be that Hibernate thinks the write is successfully committed because the DB engine does not report a failure properly?
Hibernate does not necessarily persist your changes after each updating query (saveOrUpdate, merge for instance).
Its behavior toward persistency is defined by the FlushMode of the Session tied to your HibernateTemplate. The possible FlushModes are described here : https://docs.jboss.org/hibernate/orm/3.5/api/org/hibernate/FlushMode.html
By default, an Hibernate Session is setted to FlushMode.AUTO. It means that if not absolutly and explictly needed by following queries (to maintain database consistency), no persistent changes are done, except allocation of id by iterating sequences.
It is the result you observed.
To answer your question, if you want to persist your change immediatly after a merge, you will need either :
1) Changing the flush strategy of the Session tied to you HibernateTemplate to "ALWAYS" before merging (or when instanciating the HibernateTemplate).
hibernateTemplate.setFlushModeName("FLUSH_ALWAYS");
2) Explicitly flushing the Session after merging.
hibernateTemplate.flush();
But you should also note that HibernateTemplate is a deprecated approach to interact with databases using Hibernate, in particular because HibernateTemplate does not lead people to properly deal with database transactions.
In the first place, your merge used in a transaction would have automatically been persisted when the transaction is committed with FlushMode.AUTO.
In a Spring application, you could use a #Transactional annotation, which implicitly executes all the logic included in the annotated method through a transaction.
#Autowired
private SessionFactory sessionFactory;
#Transactional
public void doUpdate(Object myObject) {
Session hibSession = sessionFactory.getCurrentSession();
hibSession.merge(myObject);
}
See the complete explanation about Spring transaction management here : http://docs.spring.io/spring-framework/docs/4.2.x/spring-framework-reference/html/transaction.html (16.5.6 paragraph for #Transactional annotation).
What is the state of your entity at the time of merge? If the entity is in the persistence context (e.g. the session), then an update will occur, if there are any changes made to the object. (if no change, Hibernate will quietly ignore the merge.)
If the entity is not in the persistence context, but it is stored in the DB, then a new row will be inserted, so you'll have duplicate.
Also, please ensure that you are implementing equals() and hashCode() methods for your entity.

OpenJPA does not refresh entity on re-fetching of entity using JPQL(select query)

The question is specific to caching in Persistent Context(L1) and not to second level cache.
I want to know why entity in the cache of Persistent context are not refreshed on selection/re-loading of entity using JPQL.
Explanation of question using example:
Start of transaction A
Load entity A in the persistent context in transaction A.
Some other processing in transaction A. But the loaded entity is not modified. At the same time, the same entity is modified and committed in another transaction B i.e. in another persistent context.
Reload of entity A in transaction 1 using JPQL (select clause). Entity A has stale attributes.
I verified in the logs that query was fired. Then why was entity A not refreshed?
This is the way first level cache works. When OpenJPA reads the result set of the query, it checks the id field of the result set row. If the entity with that id is already present in the persistence context, the existing entity is used and the rest of the result set row is ignored, meaning that the entity is not assembled again.
If you want to force reloading of an entity instance, you can:
Refresh it.
Detach it so that a new managed one will be loaded from the db when the persistence provider looks for it in any subsequent operation it performs.
Clear the entire persistence context, so that the persistence provider reloads again everything it needs in any subsequent operation in the same transaction.
Depending on the use cases, you may want to flush any changes you may have done in the instances you intend to refresh/detach/clear before applying any of the above operations, otherwise those changes will not be synchronized with the database.

Why JPA Entity select result changes on each even query?

My question is related to strange read/select behavior when same query returns different results after each call. Description of my situation is written below:
I have the following code, returning list of documents from DB
#RequestMapping(value={"/docs"}, method = RequestMethod.GET)
#ResponseBody
public ArrayList<Document> getMetaData(ModelMap modelMap) {
return (ArrayList<Document>)documentDAO.getDocuments();
}
DocumentDAO.getDocuments looks like
public List<Document> getDocuments() {
Query query = entityManager.createQuery("from Document");
List<Document> list = query.getResultList();
for(Document doc:list) System.out.println(doc.getName()+" "+doc.isSigned());
return list;
}
In other controller, I'm also extracting Document and changing boolean property with
Document doc = documentDAO.getDocumentById(id)
doc.setSigned(true);
documentDAO.updateDocument(doc); // IS IT NECESSARY??
getById and updateDocument are the following:
public Document getDocumentById(Long id) {
return entityManager.find(Document.class, id);
}
#Transactional
public void updateDocument(Document document) {
entityManager.merge(document);
entityManager.flush();
}
Questions:
As far as I know, setting property of managed object is enough to propagate changes to DB. But I want to flush changes immediately. Is my approach with extra call of update is appropriate solution or calling setter is enough for making immediate changes in DB? By extra update I mean documentDAO.updateDocument(doc); // IS IT NECESSARY??
How JPA stores managed objects - in some internal data structure or simply keeps them in references like Document doc;? Internal structure most likely makes duplicate/sameID managed object impossible, references most likely makes possible to have multiple managed objects with same id and other properties.
How merge works internally - tries to find managed object with the same ID in internal storage and, in the case of detecting, refreshes it's fields or simply updates DB?
If internal storage really exists (most likely this is persistence context, futher PC), what is criteria for distinquish managed objects? #Id annotated field of hibernate model?
My main problem is different results of entityManager.createQuery("from Document");
System.out.println(doc.getName()+" "+doc.isSigned()); shows isSigned true on odd calls and false on even calls.
I suspect that first select-all-query returns entities with isSigned=false and put them to PC, after that user performs some operation which grabs entity byID, sets isSigned=true and just extracted entity conflicts with already presented in PC. First object has isSigned=false, second has isSigned=true and PC confused and returns different managed objects in rotation. But how its possible? In my mind, PC has mechanisms to not allow such confusing ambigious situations by keeping only one managed object for each unique id.
First of all you want to enrol both the read and the write in a single transactional service method:
#Transactional
public void signDocument(Long id) {
Document doc = documentDAO.getDocumentById(id)
doc.setSigned(true);
}
So this code should reside on the Service side, not in your web Controller.
As far as I know, setting property of managed object is enough to propagate changes to DB. But I want to flush changes immediately. Is
my approach with extra call of update is appropriate solution or
calling setter is enough for making immediate changes in DB? By extra
update I mean documentDAO.updateDocument(doc); // IS IT NECESSARY??
This applies only to managed entities, as long as the Persistence Context is still open. That's why you need a transactional service method instead.
How JPA stores managed objects - in some internal data structure or simply keeps them in references like Document doc;? Internal structure
most likely makes duplicate/sameID managed object impossible,
references most likely makes possible to have multiple managed objects
with same id and other properties.
The JPA 1st level cache simply stores entities as they are, it doesn't use any other data representation. In a Persistence Context you can have one and only one entity representation (Class and Identifier). In the context of a JPA Persistence Context, the managed entity equality is the same with entity identity.
How merge works internally - tries to find managed object with the
same ID in internal storage and, in the case of detecting, refreshes
it's fields or simply updates DB?
The merge operation makes sense for reattaching detached entities. A managed entity state is automatically synchronized with the database during flush-time. The automatic dirty checking mechanism takes care of this.
If internal storage really exists (most likely this is persistence context, further PC), what is criteria for distinguish managed objects? #Id annotated field of hibernate model?
The PersistenceContext is a session-level cache. The managed objects always have an identifier and an associated database row.
I suspect that first select-all-query returns entities with
isSigned=false and put them to PC, after that user performs some
operation which grabs entity byID, sets isSigned=true and just
extracted entity conflicts with already presented in PC.
In the same Persistence Context scope this can't ever happen. If you load an entity through a query, the entity gets caches in the 1st level cache. If you try to load it again with another query or with the EntityManager.find() you will still get the same object reference, that's already cached.
If the first query happens against a Persistence Context and the second query/find will be issued on a second Persistence Context, then each Persistence Context will have to cache its own version of the entities being queried.
First object has isSigned=false, second has isSigned=true and PC
confused and returns different managed objects in rotation. But how
its possible?
This can't happen. The Persistence Context always maintains entity object integrity.

JPA merge vs. persist [duplicate]

This question already has answers here:
JPA EntityManager: Why use persist() over merge()?
(16 answers)
Closed 2 years ago.
So far, my preference has been to always use EntityManager's merge() take care of both insert and update. But I have also noticed that merge performs an additional select queries before update/insert to ensure record does not already exists in the database.
Now that I am working on a project requiring extensive (bulk) inserts to the database. From a performance point of view does it make sense to use persist instead of merge in a scenario where I absolutely know that I am always creating a new instance of objects to be persisted?
It's not a good idea using merge when a persist suffices - merge does quite a lot more of work. The topic has been discussed on StackOverflow before, and this article explains in detail the differences, with some nice flow diagrams to make things clear.
I would definitely go with persist persist() if, as you said:
(...) I absolutely know that I am always creating a new instance of objects to be persisted (...)
That's what this method is all about - it will protect you in cases where the Entity already exists (and will rollback your transaction).
If you're using the assigned generator, using merge instead of persist can cause a redundant SQL statement, therefore affecting performance.
Also, calling merge for managed entities is also a mistake since managed entities are automatically managed by Hibernate and their state is synchronized with the database record by the dirty checking mechanism upon flushing the Persistence Context.
To understand how all this works, you should first know that Hibernate shifts the developer mindset from SQL statements to entity state transitions.
Once an entity is actively managed by Hibernate, all changes are going to be automatically propagated to the database.
Hibernate monitors currently attached entities. But for an entity to become managed, it must be in the right entity state.
First, we must define all entity states:
New (Transient)
A newly created object that hasn’t ever been associated with a Hibernate Session (a.k.a Persistence Context) and is not mapped to any database table row is considered to be in the New (Transient) state.
To become persisted we need to either explicitly call the EntityManager#persist method or make use of the transitive persistence mechanism.
Persistent (Managed)
A persistent entity has been associated with a database table row and it’s being managed by the current running Persistence Context. Any change made to such entity is going to be detected and propagated to the database (during the Session flush-time).
With Hibernate, we no longer have to execute INSERT/UPDATE/DELETE statements. Hibernate employs a transactional write-behind working style and changes are synchronized at the very last responsible moment, during the current Session flush-time.
Detached
Once the current running Persistence Context is closed all the previously managed entities become detached. Successive changes will no longer be tracked and no automatic database synchronization is going to happen.
To associate a detached entity to an active Hibernate Session, you can choose one of the following options:
Reattaching
Hibernate (but not JPA 2.1) supports reattaching through the Session#update method.
A Hibernate Session can only associate one Entity object for a given database row. This is because the Persistence Context acts as an in-memory cache (first level cache) and only one value (entity) is associated with a given key (entity type and database identifier).
An entity can be reattached only if there is no other JVM object (matching the same database row) already associated to the current Hibernate Session.
Merging
The merge is going to copy the detached entity state (source) to a managed entity instance (destination). If the merging entity has no equivalent in the current Session, one will be fetched from the database.
The detached object instance will continue to remain detached even after the merge operation.
Removed
Although JPA demands that managed entities only are allowed to be removed, Hibernate can also delete detached entities (but only through a Session#delete method call).
A removed entity is only scheduled for deletion and the actual database DELETE statement will be executed during Session flush-time.
To understand the JPA state transitions better, you can visualize the following diagram:
Or if you use the Hibernate specific API:

How can I create a hibernate collection that will be re-read every time I request it?

I have an entity that has a state table associated with it. The state table is managed by another process, and contains a list of objects that my business logic must process. I would like to get a new snapshot of the state table each time I reload the entity. How can I ensure that no part of Hibernate or its support libraries ever caches any of the values of this table? Basically, I want to get a new view of the collection every time I call getMyStateValues ().
Most of the point of Hibernate is to prevent that from happening and return a consistent view of an entity's state in the scope of a given transaction. So either reload the whole entity, in different transactions every time. Or, if you need to reload the state table during a business transaction, load only the state table by the parent entity's id in a separate Hibernate session.
You can create a method in your entity that queries the database and return the collection. getXYXReload(). It´s not a very nice design decision, thought.
You can use Hibernate's CacheMode. It allows you to instruct a hibernate session on how to interact with the cache. You can get access to the underlying session with:
#PersistenceContext EntityManager manager;
...
org.hibernate.Session session = (Session)manager.getDelegate();
Unfortunately, this technique applies to the whole session, and not specifically to an entity.

Categories