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.
Related
We are working on a little web (will run on Tomcat) with the data layer done with JPA (Eclipselink).
I did similar thing some time ago. But i were always unsure when i need to begin and end transactions or do a flush.
At the moment i use transaction if i add (persist) and remove objects. If i call setters on an already persisted object i do not use transactions.
Is there a guide/ tutorial or a short answer when to use transactions or how to implement application managed JPA correctly.
I think one can summarize an answer to your question.
almost any JPA operation needs a transaction, except find/selects that do not lock entities (i.e any JPA operation that does not change the data).
(JTA transaction-scoped entity manager)
In the case of an JTA transaction-scoped entity manager it is better to quote from the spec (Chapter 3 Entity Operations):
The persist, merge, remove, and refresh methods must be invoked within
a transaction context when an entity manager with a
transaction-scoped persistence context is used.
If there is no transaction context, the javax.persistence.TransactionRequiredException is thrown.
Methods that specify a lock mode other than LockModeType.NONE must be invoked
within a transaction context.
If there is no transaction context, the javax.persistence.TransactionRequiredException is thrown.
The find method (provided it is invoked without a lock or invoked with
LockModeType.NONE) and the getReference method are not required to be
invoked within a transaction context. If an entity manager with
transaction-scoped persistence context is in use, the resulting
entities will be detached; if an entity manager with an extended
persistence context is used, they will be managed. See section 3.3 for
entity manager use outside a transaction.
(Application-managed/resource-local entity manager)
In the case of an Application-managed entity manager, the JPA spec is not clear about the behavior. In the case of Hibernate, it is pretty complicated what happens, when not inside a transaction (it could depend also on the JDBC driver and the autocommit mode of the DB connection). Check Hibernate's article on this theme. Basically you are strongly encouraged to always use transactions for the above mentioned operations.
To the second part of your question: if you called a setter of a managed entity, and without flushing you detached it (i.e before transaction commit), the behavior is unclear/undefined, i.e you should better correct the code.
Example of buggy code:
//begin Transaction
MyEntity entity = em.find(MyEntity.class, 1L);
entity.setField("New value");
em.detach();//it is not sure whether the "New value" will be persisted. To make sure it is persisted, ypu need to call em.flush() before detaching
//commit Transaction
Usually if the order of DB operations (not the same as the order of enity manager operations) is not important, you can leave the JPA implementation to decide when to flush (e.g on transaction commit).
The EntityManger is injected with #PersistenceContext annotation. This is a method from my "DAO/Repository" class which is not annotated. This class is injected into the EJB's that needs to get data from/to the database.
Until now I thought that after the persist method below is finished the persistencecontext is flushed and the data is stored to the database but from what happening in my app I start to doubt that. I have made the datasource and connection pool in Glassfish so I am using container managed transactions as far I know, however I do not use any transaction annotations.
Could somebody throw some light over it for me?
public void persist(QuestionFeedback questionFeedback) {
questionFeedback.setCreated(new Date());
entityManager.persist(questionFeedback);
}
Using Glassfish 3, Java EE6 compatiblity
The persist method makes the entity persistent, but doesn't yet write the change to the database. This normally happens when the transaction commits (the provider is free to optimize this, it could happen earlier).
With flush you can force the write to happen earlier, but it will still be visible only to code that participates in the current transaction. To make the write permanent (visible to all external code), the transaction still needs to be committed.
Without any explicit annotations, your EJB bean will be transactional by default.
Because EntityManager is injected via #PersistenceContext annotation, you are for sure using container managed transactions.
Anyway, your assumption that persist causes transaction commit is wrong. Change performed via persist is made to the database in commit. Documentation of EntityManager says that "new instance becomes both managed and persistent by invoking persist". In this context "becomes persist" does not mean entity is persisted to the database on that moment. On the moment when persist is called, entity is persisted in the terms of PersistenceContext. It is then later on persisted to the database latest when transaction commits.
Because you are not using any #TransactionAttribute annotations for your methods, default will apply. Default is TransactionAttributeType.REQUIRED. This will cause container to create transaction when first business method is called and propagate this on to other methods. Your transaction will commit when the call to the first business method is completed. Then your changes are in database (if no rollback was performed).
If you don't use any transaction annotations the default will be transactions being required. Thus your DAO will run in a transaction and the persistence context will no later be flushed than when the transaction is committed.
From the JavaDoc on TransactionAttribute:
If the TransactionAttribute annotation is not specified, and the bean uses container managed transaction demarcation, the semantics of the REQUIRED transaction attribute are assumed.
From the JavaDoc on FlushModeType:
When queries are executed within a transaction, if FlushModeType.AUTO is set on the Query or TypedQuery object, or if the flush mode setting for the persistence context is AUTO (the default) and a flush mode setting has not been specified for the Query or TypedQuery object, the persistence provider is responsible for ensuring that all updates to the state of all entities in the persistence context which could potentially affect the result of the query are visible to the processing of the query.
This means that the persistence context might be flushed earlier, if you use a query whose result might be influenced by that flush.
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.
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:
We are using Hibernate Spring MVC with OpenSessionInView filter.
Here is a problem we are running into (pseudo code)
transaction 1
load object foo
transaction 1 end
update foo's properties (not calling session.save or session.update but only foo's setters)
validate foo (using hibernate validator)
if validation fails ?
go back to edit screen
transaction 2 (read only)
load form backing objects from db
transaction 2 end
go to view
else
transaction 3
session.update(foo)
transaction 3 end
the problem we have is if the validation fails
foo is marked "dirty" in the hibernate session (since we use OpenSessionInView we only have one session throughout the http request), when we load the form backing objects (like a list of some entities using an HQL query), hibernate before performing the query checks if there are dirty objects in the session, it sees that foo is and flushes it, when transaction 2 is committed the updates are written to the database.
The problem is that even though it is a read only transaction and even though foo wasn't updated in transaction 2 hibernate doesn't have knowledge of which object was updated in which transaction and doesn't flush only objects from that transaction.
Any suggestions? did somebody ran into similar problem before
Update: this post sheds some more light on the problem: http://brian.pontarelli.com/2007/04/03/hibernate-pitfalls-part-2/
You can run a get on foo to put it into the hibernate session, and then replace it with the object you created elsewhere. But for this to work, you have to know all the ids for your objects so that the ids will look correct to Hibernate.
There are a couple of options here. First is that you don't actually need transaction 2 since the session is open you could just load the backing objects from the db, thus avoiding the dirty check on the session. The other option is to evict foo from the session after it is retrieved and later use session.merge() to reattach it when you what your changes to be stored.
With hibernate it is important to understand what exactly is going on under the covers. At every commit boundary it will attempt to flush all changes to objects in the current session regardless of whether or not the changes where made in the current transaction or any transaction at all for that matter. This is way you don't actually need to call session.update() for any object that is already in the session.
Hope this helps
There is a design issue here. Do you think an ORM is a transparent abstraction of your datastore, or do you think it's a set of data manipulation libraries? I would say that Hibernate is the former. Its whole reason for existing is to remove the distinction between your in-memory object state and your database state. It does provide low-level mechanisms to allow you to pry the two apart and deal with them separately, but by doing so you're removing a lot of Hibernate's value.
So very simply - Hibernate = your database. If you don't want something persisted, don't change your persistent objects.
Validate your data before you update your domain objects. By all means validate domain objects as well, but that's a last line of defense. If you do get a validation error on a persistent object, don't swallow the exception. Unless you prevent it, Hibernate will do the right thing, which is to close the session there and then.
What about using Session.clear() and/or Session.evict()?
What about setting singleSession=false on the filter? That might put your operations into separate sessions so you don't have to deal with the 1st level cache issues. Otherwise you will probably want to detach/attach your objects manually as the user above suggests. You could also change the FlushMode on your Session if you don't want things being flushed automatically (FlushMode.MANUAL).
Implement a service layer, take a look at spring's #Transactional annotation, and mark your methods as #Transactional(readOnly=true) where applicable.
Your flush mode is probably set to auto, which means you don't really have control of when a DB commit happens.
You could also set your flush mode to manual, and your services/repos will only try to synchronize the db with your app when you tell them to.