3

I have this a class like bellow:

@Entity
@Table(name="work")
public class Work {

    @Id
    @Column(name="id")
    private String id;


    @OneToMany(orphanRemoval=true ,mappedBy="work", cascade=CascadeType.ALL , fetch=FetchType.EAGER)
    private List<PersonRole> personRoleList;
}

As mine is an web application, when i update (comes from client) a personRoleList item and call :

session.update(work); //`work` is in detached state

It does not update the existing personRoleList item it actually add a new one.

Some other people also having the same problem. REF:
using-saveorupdate-in-hibernate-creates-new-records-instead-of-updating-existi
jpa-onetomany-not-deleting-child

I tried all suggested solution, but none of them work for me.

But then i just tried :

 session.merge(work);  //replacing session.update(work)

And it works as expected.!!

This is where I get confused. Because I can't find any explanation for this difference in behaviors in case of OneToMany relationship (or may be i missed ). I read some threads to understand the differences between update() and merge() and gone through the doc. REF:

what-are-the-differences-between-the-different-saving-methods-in-hibernate

differences-among-save-update-saveorupdate-merge-methods-in-session

But still it is not clear What are those behavioral pattern/logic/steps that creating this difference.?

Community
  • 1
  • 1
Saif
  • 6,804
  • 8
  • 40
  • 61
  • when you are working with detached entities, there are chances that you have multiple entities. Now when you want to persist the changes, you need to tell hibernate what changes should go in as there can be multiple dirty versions. So, you have to use merge to make the correct changes. – dharam Jun 24 '15 at 17:43
  • Are you asking for the reason that these different methods exist, or are you asking for the reason merge worked and update didn't? – T.D. Smith Jun 24 '15 at 17:45
  • I am asking for the reason merge worked and update didn't? @Tyler – Saif Jun 24 '15 at 17:47

1 Answers1

1

Merge attempts to associate a currently transient object with a persistent object currently under management by the session by 'merging' them into one entity. Its intended use is when you have a detached object and an attached object and wish to resolve them.

In a merge(), Hibernate will read the entity from the database if there isn't already a managed instance in the session. In your example, this will result in Hibernate eagerly loading the collection (due to fetch=FetchType.EAGER). Then when your session ends, Hibernate will check for changes in the collection (due to cascade=CascadeType.ALL) and will perform the appropriate UPDATE in the database.

This differs from the update() scenario because in an update Hibernate always (by default) assumes the object is dirty and schedules an UPDATE. This update is likely what's causing creation of a new element in your collection - Hibernate hasn't looked in the database to bring the collection into session before issuing the UPDATE.

I'd bet you can get the desired behavior of update() by setting

select-before-update="true"

in your class mapping or by using the lock method to re-attach your object to the session before making changes.

From Chapter 9 of Java Persistence with Hibernate

It doesn’t matter if the item object is modified before or after it’s passed to update(). The important thing here is that the call to update() is reattaching the detached instance to the new Session (and persistence context). Hibernate always treats the object as dirty and schedules an SQL UPDATE, which will be executed during flush. You can see the same unit of work in figure 9.8. You may be surprised and probably hoped that Hibernate could know that you modified the detached item’s description (or that Hibernate should know you did not modify anything). However, the new Session and its fresh persistence context don’t have this information. Neither does the detached object contain some internal list of all the modifications you’ve made. UDPATE in the database is needed. One way to avoid this UDPATE statement is to configure the class mapping of Item with the select-before-update="true" attribute. Hibernate then determines whether the object is dirty by executing a SELECT statement and comparing the object’s current state to the current data- base state.

T.D. Smith
  • 984
  • 2
  • 7
  • 22