Update () and merge behave differently when updating an item in a OneToMany collection

I have a class like below:

@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;
}

      

Like my web app, when I update (come from client) an element personRoleList

and call:

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

      

It doesn't update an existing item personRoleList

, which actually adds a new one.

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

I tried all the 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 got confused. Because I can't find an explanation for this difference in behavior in the case of OneToMany relationships (or maybe I missed it). I read several threads to understand the differences between update()

and merge()

and went 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 it is still unclear what are these behavioral patterns / logics / steps that create this difference.

+3


source to share


1 answer


Combine attempts to bind the current transient object to a persistent object under session control by combining them into a single object. Its intended use is when you have a separate object and an attached object and want to resolve them.

B merge()

, Hibernate will read an entity from the database if there is no managed instance in the session yet. In your example, this will cause Hibernate to eagerly load the collection (because of fetch=FetchType.EAGER

). Then, when your session ends, Hibernate will check for changes to the collection (due to cascade=CascadeType.ALL

) and perform the appropriate UPDATE on the database.

This differs from scenario update()

because in an update, Hibernate always (by default) assumes the object is dirty and schedules an UPDATE. Perhaps this update is causing a new item to be created in your collection. Hibernate did not look into the database to collect the collection into a session before issuing an UPDATE.

I bet you can get the behavior you want by update()

setting



select-before-update="true"

in your class mapping, or using a method lock

to reattach your object to the session before making changes.

From Chapter 9 Java Persistence with Hibernate

It doesn't matter if the object object is modified before or after passing it to Update (). The important thing is that the call to update () repeats the binding of the individual instance to the new session (and save context). hibernate always treats the object as dirty and schedules an SQL UPDATE to be executed during the flush. Figure 9.8 shows the same unit of work. You might be surprised, and probably hoped, that Hibernate might know that you have changed the description of individual elements (or that Hibernate should know that you have changed nothing). However, the new session and its fresh save context do not have this information. In addition, a single item does not contain an internal list of all the changes you made. Requires UDPATE on the database.One way to avoid this UDPATE statement is to customize the element's class mapping using the select-before-update = "true" attribute. Hibernate then determines if the object is contaminated by performing a SELECT and comparing the current state of the objects with the current data, the underlying state.

+1


source







All Articles