Reputation: 3271
I'm having a problem where the entity manager is persisting an entity that I don't think has changed during the flush.
I know the following code is the problem because if I comment it out the entity doesn't persist. In this code all I'm doing is loading the entity and calling some getters.
Query qry = em.createNamedQuery("Clients.findByClientID");
qry.setParameter("clientID", clientID);
Clients client = (Clients) qry.getSingleResult();
results.setFname(client.getFirstName());
results.setLname(client.getLastName());
...
return results;
Later in a different method I do another namedQuery which causes the entity manager to flush. For some reason the client loaded above is persisted.
The reason this is a problem is because in the middle of all this, there is some old code that is making some straight JDBC changes to the client. When the entity manager persists the changes made by the straight JDBC are lost.
The theory we have at moment is that the entity manager is comparing the entity to the underlying record, sees that it's different, then persists it.
Can someone explain or confirm the behavior we're seeing?
Upvotes: 4
Views: 4360
Reputation: 26574
If your getter is performing any logic so that it is returning something other than what hibernate sent to the setter, then it will be marked dirty. For instance, if your getter logic is returning 0 when hibernate gave you a null from the database then it'll be marked dirty because it looks like it changed. I'm not aware of a way to tell hibernate not to mark that change as making the entity dirty.
Upvotes: 1
Reputation: 37533
You're right that the entity manager compares the objects (as long as they're not read-only in the session, evicted, session.clear() has been called, etc) and if they don't match then it will flush them out.
Tthis specific issue has bitten us before, and caused exactly the same needless-flush problems -- ours was also a performance problem, in a tight loop we needed to flush()
the session and every time we did hundreds of unchanged objects got dumped back to the DB.
In our case, it was that the entities in question did not implement equals()
and hashCode()
correctly -- Hibernate obviously relies on those to check the object's changed, and if they're incorrect in some way then it has no choice but to flush them back.
From memory (it was a while ago) it was actually a subsidiary entity which didn't implement hashCode()
correctly -- imagine a Student
-Teacher
relationship, where Teacher
doesn't correctly implement those methods. When dirty-checking a Student
, Hibernate checks in its entity cache for the Teacher
, thinks it's not there (due to incorrect implementations) and so thinks Student
has changed, reflushing it back to write out the new Teacher
ID.
So check hashCode()
and equals()
for the entitiy in question, all its related entities, and any other components/user types which get written to the DB.
Upvotes: 1
Reputation: 57707
I don't know the innards of hibernate, but my guess is that you are correct. But I don't think hibernate compares with the db, but compares with the local session cache.
You can avoid the problem by setting the object to readOnly - hibernate then does not check for changes, e.g.
session.setReadOnly(client, true);
Alternatively, you can also evict the object, using Session.evict(). Next time the object is required it will be read in from the db, including any changes you made using your custom JDBC.
Upvotes: 2