Hibernate session merge
I've recently discovered the beauty of Hibernate's Session.merge() method.
To explain this.... I'll describe the situation on hand, using the classic Parent & Child relationship (Parent has many children, and each children can only have 1 parent - lets keep it that way for now... you might argue a child has 2 parents)
I mapped the Parent & Child as follows:
@Entity
@Table(name="parent")
public class Parent {
...
@OneToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY, mappedBy="parent")
@Cascade({org.hibernate.annotations.CascadeType.DELETE_ORPHAN})
private List
}
@Entity
@Table(name="child")
public class Child{
...
@ManyToOne
@JoinColumn(name="parent_id", nullable=false)
private Parent parent;
}
The usual situation - we load up parent & the children, then display parent information & the children on the UI layer. User might update both the parent & the children, and that is fine, since we have specified the Cascade ALL option. But what about deleting the child? Note that Cascade REMOVE is not what you want here.... that's saying ... if the parent is deleted, then delete all the children too (quite straight forward, isnt it?).
Hibernate has the org.hibernate.annotations.CascadeType.DELETE_ORPHAN annotation, which seems like what we need here. If the child is orphan (i.e. doesnt have any parent), then delete it from the database. Looks promising, except there's a catch.
The parent & the children has to be on the session. i.e. it has to be loaded on the same session.. before this can work with the usual session.saveOrUpdate() / session.update() method. Thats kinda... sucks.... because most of the time... you will load the parent & the children, display on the UI, and then do some operations (which might remove some children), then let the UI framework to recreate the parent & children through some kind of binding mechanism (or... you might do it yourself)... and call session.saveOrUpdate() on the new hibernate object.
i.e. the object that you gave to hibernate is transient. So... hibernate wouldnt delete that orphan child...
This is where session.merge() comes into play. It just simply works with session.merge(), because session.merge() will try to merge the transient object with the persisted object
From Javadoc:
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. The given instance does not become associated with the session. This operation cascades to associated instances if the association is mapped with cascade="merge".
So session.merge will also load the given object with the same identifier. Thats how it works.
Another beauty thing with session merge, which might be easier to explain using some pseudocodes..
Parent parent = new Parent();
Child child = new Child();
child.setChildId(1L);
parent.addChild(child);
Parent savedParent = session.merge(parent);
Session merge will return the persisted instance. So in the example above... (assuming child is already persisted in DB), then parent.getChildren().get(0).getName() will return the string from DB.
where as:
session.merge(parent);
System.out.println(parent.getChildren().get(0).getName()) will return null.
Another thing to point out, is that there's cascade MERGE type. Which, as the name suggests, does cascade on session.merge() operation.
One important thing to keep in mind, is that session merge returns a new instance.
So in the code above:
parent != savedParent
and you might want to work using savedParent onwards anyway.

0 Comments:
Post a Comment
Subscribe to Post Comments [Atom]
<< Home