Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should Hibernate Session#merge do an insert when receiving an entity with an ID?

This seems like it would come up often, but I've Googled to no avail.

Suppose you have a Hibernate entity User. You have one User in your DB with id 1.

You have two threads running, A and B. They do the following:

  • A gets user 1 and closes its Session
  • B gets user 1 and deletes it
  • A changes a field on user 1
  • A gets a new Session and merges user 1

All my testing indicates that the merge attempts to find user 1 in the DB (it can't, obviously), so it inserts a new user with id 2.

My expectation, on the other hand, would be that Hibernate would see that the user being merged was not new (because it has an ID). It would try to find the user in the DB, which would fail, so it would not attempt an insert or an update. Ideally it would throw some kind of concurrency exception.

Note that I am using optimistic locking through @Version, and that does not help matters.

So, questions:

  1. Is my observed Hibernate behaviour the intended behaviour?
  2. If so, is it the same behaviour when calling merge on a JPA EntityManager instead of a Hibernate Session?
  3. If the answer to 2. is yes, why is nobody complaining about it?
like image 989
David van Geest Avatar asked Jan 31 '14 20:01

David van Geest


People also ask

Is it necessary to close hibernate session?

Should I close the session in the query method? It all depends on how you obtain the session. if you use sessionFactory. getCurrentSession() , you'll obtain a "current session" which is bound to the lifecycle of the transaction and will be automatically flushed and closed when the transaction ends (commit or rollback).

Why do we need session in hibernate?

The Session interface is the main tool used to communicate with Hibernate. It provides an API enabling us to create, read, update, and delete persistent objects. The session has a simple lifecycle. We open it, perform some operations, and then close it.

What happens if hibernate session is not closed?

When you don't close your Hibernate sessions and therefore do not release JDBC connections, you have what is typically called Connection leak. So, after a number of requests (depending on the size of your connection pool) the server will not be able to acquire a connection to respond your request.

Is hibernate session thread safe?

No, Session object is not thread-safe in Hibernate and intended to be used with-in single thread in the application.


2 Answers

Please see the text from hibernate documentation below.

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.

It clearly stated that copy the state(data) of object in database. if object is not there then save a copy of that data. When we say save a copy hibernate always create a record with new identifier.

Hibernate merge function works something like as follows.

  1. It checks the status(attached or detached to the session) of entity and found it detached.
  2. Then it tries to load the entity with identifier but not found in database.
  3. As entity is not found then it treat that entity as transient.
  4. Transient entity always create a new database record with new identifier.

Locking is always applied to attached entities. If entity is detached then hibernate will always load it and version value gets updated.

Locking is used to control concurrency problems. It is not the concurrency issue.

like image 84
Sumit Tyagi Avatar answered Nov 15 '22 12:11

Sumit Tyagi


I've been looking at JSR-220, from which Session#merge claims to get its semantics. The JSR is sadly ambiguous, I have found.

It does say:

Optimistic locking is a technique that is used to insure that updates to the database data corresponding to the state of an entity are made only when no intervening transaction has updated that data since the entity state was read.

If you take "updates" to include general mutation of the database data, including deletes, and not just a SQL UPDATE, which I do, I think you can make an argument that the observed behaviour is not compliant with optimistic locking.

Many people agree, given the comments on my question and the subsequent discovery of this bug.

From a purely practical point of view, the behaviour, compliant or not, could lead to quite a few bugs, because it is contrary to many developers' expectations. There does not seem to be an easy fix for it. In fact, Spring Data JPA seems to ignore this issue completely by blindly using EM#merge. Maybe other JPA providers handle this differently, but with Hibernate this could cause issues.

I'm actually working around this by using Session#update currently. It's really ugly, and requires code to handle the case when you try to update an entity that is detached, and there's a managed copy of it already. But, it won't lead to spurious inserts either.

like image 26
David van Geest Avatar answered Nov 15 '22 14:11

David van Geest