Inside Explanation Of Inverse Attribute in Hibernate

This post will show, how inverse attribute works in hibernate.  Essentially, inverse attribute tells which side of association is responsible for maintaining relationship.  It specifies that if parent is responsible for updating relationship with child while saving parent or child will update relationship itself. I will describe this by some examples.

I am assuming that you have good knowledge of:

  • Mapping a collection
  • Bidirectional Association
  • Parent Child Relationships

USING LIST…

Inverse=false

Following is a hibernate mapping for this example. You can see that Parent has a list of child with attribute inverse=false. And also child does not have reference of parent in mapping file. (I mean many-to-one). I am assuming cascade=all in all cases.

<class name="com.nik.dbobjects.Parent" table="parent">
		<id name="idParent" type="long" column="id_parent">
			<generator class="increment" />
		</id>
		<property name="name" type="string" column="name" />
		<list name="childList" cascade="all" inverse="false">
			<key column="id_parent" />
			<index column="idx" />
			<one-to-many class="com.nik.dbobjects.Child" />
		</list>
</class>
<class name="com.nik.dbobjects.Child" table="child">
		<id name="idChild" type="long" column="id_child">
			<generator class="increment" />
		</id>
		<property name="name" type="string" column="name" />
</class>

INSERT Operation

Code snippet:

Parent parent = new Parent("parent");
Child child1 = new Child("child_1");
parent.getChildList().add(child1);
session.save(parent);

Above code will execute following sql queries:

insert into parent (name, id_parent) values (?, ?)// insert parent
insert into child (name, id_child) values (?, ?) // insert child without foreign key
update child set id_parent=?, idx=? where id_child=?//update child with foreign key and index

You can see that hibernate execute three queries for saving parent in this case. Since, inverse-false, it is parent’s responsibility to update child’s relationship. It first saves child and then executes an extra query to update foreign key and list index column (id_parent and idx columns).  So, in this way, if you have n child elements, then there will be n extra queries to update relationship.

UPDATE Operation

Code Sample:

                Parent parent1 = (Parent)session.load(Parent.class, 1L);
		Parent parent2 = (Parent)session.load(Parent.class, 2L);

		Child c = parent1.getChildList().get(0);

		parent1.getChildList().remove(c);
		parent2.getChildList().add(c);

		session.flush();

SQL queries:

update child set id_parent=null, idx=null where id_parent=? and id_child=?//remove parent
update child set id_parent=null, idx=null where id_parent=? and id_child=?//remove parent
update child set id_parent=?, idx=? where id_child=?//set new parent and update index
update child set id_parent=?, idx=? where id_child=?//set new parent and update index.

We can see that, parent has to update its child index whenever a child removes from the list. So, it first sets null to all parent id and index id columns of child and then they are updated as per new parent reference. (It follows behaviour of List for maintaining order of its elements.)

Inverse = true

Now, make inverse=true in child list mapping of Parent class. Also add many-to-one reference to child class.

<many-to-one name="parent" class="com.nik.dao.Parent"
			column="id_parent"></many-to-one>

INSERT Operation

Example code:

                Parent parent = new Parent("parent");
		Child child1 = new Child("child_1");
		child1.setParent(parent);
		parent.getChildList().add(child1);

		session.save(parent);

It will execute following SQL queries:

insert into parent (name, id_parent) values (?, ?)
insert into child (name, id_parent, id_child) values (?, ?, ?)

As you can see, relationship (id_parent column in child table) is updated during child save: this is of the child responsibility. Hibernate will execute only two queries for saving parent in this case. In this case, there is no need to execute an extra query to update relationship.

But wait. In this case, index column (idx in childList of Parent class) in child class will be null. It is used by parent list to maintain order of child elements. So, next time, when you access child list from parent class (parent.getChildList().get(0)), it will throw an exception saying null index column for collection: Parent.childList.

So, we can say that if you are using List as one-to-many collection and inverse attribute is false, then there will be total n+n queries for saving n child elements. If you inverse = true, there will be no extra query for saving child elements, but also you will not be able to load child elements because index column of child will be null.

USING SET…

Now, let we take Set as a collection of child elements in Parent class. Since Set is not an indexed collection, it is not required to have an index column in child table.

Inverse = false

For insert operation, Set will act as same as List will do. It will first inserts the child element and then updates the relationship using update query.

UPDATE Operation

Code Snippet:

                Parent parent1 = (Parent)session.load(Parent.class, 1L);
		Parent parent2 = (Parent)session.load(Parent.class, 2L);

		Child c = parent1.getChildList().iterator().next();

		parent1.getChildList().remove(c);
		parent2.getChildList().add(c);

Will execute following queries:

update child set id_parent=null where id_parent=? and id_child=?
update child set id_parent=? where id_child=?

We can see that, it will first set null to parent id from which child is to be removed. And then set parent id of new parent. But this can be optimized using inverse = true.

Inverse=true

For inverse=true, Set has an advantage. Since, Set is not an indexed collection; it does not require maintaining an index column like List.

INSERT Operation

Code sample:

                Parent parent = new Parent("parent");
		Child child1 = new Child("child_1");
		child1.setParent(parent);
		parent.getChildList().add(child1);

		session.save(parent);

Will execute following queries:

insert into parent (name, id_parent) values (?, ?)
insert into child (name, id_parent, id_child) values (?, ?, ?)

So, there are no extra queries require to update relationship.

UPDATE Operation

Code sample:

                Parent parent1 = (Parent)session.load(Parent.class, 1L);
		Parent parent2 = (Parent)session.load(Parent.class, 2L);

		Child c = parent1.getChildList().iterator().next();

		parent1.getChildList().remove(c);// remove child from parent 1

		c.setParent(parent2); //set parent as parent 2
		parent2.getChildList().add(c);//add child to parent 2

		session.flush();

Will execute following query:

update child set name=?, id_parent=? where id_child=?

We can see that it requires only one update query to shuffle child from parent 1 to parent 2. Here, Child updates itself rather than setting parent id to null and then further updating to new parent.

Conclusion

Using and understanding inverse=”true” is very important to optimize your code. Prefer using inverse=”true” on bidirectional association. Also you must have noticed performance difference between using List and Set. Set is more preferable if is not requires to have an indexed collection.

Advertisements
  1. Excellent Explanation

    • Ram
    • January 29th, 2013

    Excellent

    • Kinjal
    • April 10th, 2013

    Really excellent

  2. oh this tutorial’s really helpful for such a newbie like me.I’ve searched around on internet but there were many tuts which made me confused but yours didn’t.Great tutorial ! Thanks alot !

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: