Sunday, July 15, 2012

Hibernate Inverse Attribute – Re-Explained

It was few years ago, when I started working on the newer version of Hibernate, the first and the most confusing term I encountered is ‘inverse’ attribute. Although soon I realized that it is not that complex and perplex, however its name and the way people explained it, added lot of confusion around it.

This blog is yet another endeavor to explain this attribute, and possibly in the most simple way using easily understandable examples.

Before we start, note that inverse attribute is used in bidirectional mapping including one-to-many and many-to-many (soon you will realize that it make sense).

Now, whenever you get an inverse attribute with true value in collection mapping (for e.g. set) of an entity mapping, read it like this: “I am not the one who is responsible for maintaining this relationship, but the inverse is true i.e. the other entity in the relationship is responsible for this”.

Let’s explore this with examples.

We’ll consider an example which includes both one-to-many and many-to-many relationship. Assume 3 entities, Employee, Department and Certification. The relationship between Department and Employee is one-to-many from Department side (i.e. One department can have many employees, but an employee belongs to one department only). While the relationship between Employee and Certification is many-to-many (i.e. an employee can have many certifications while a certification can belong to multiple employees). Here is how this would look like



The relationship between Employee and Certification is maintained using a relationship table (Emp_Cert) as shown below.
 

 
The domain and mapping of the 3 entities would look like this:

public class Employee {

     private int id;
     private String name;
     private Department dept;
     private Set certs;
    //Setters Getters…
}

<class name="Employee">
     <id name="id">
        <generator class="sequence"/>
     </id>
     <property name="name"/>
     <many-to-one name="dept" class="Department" column="dept_id"></many-to-one>
     <set name="certs" inverse="false" table="emp_cert">
        <key column="emp_id"/>
        <many-to-many column="cert_id" class="Certification"/>
     </set>
</class>


public class Department {

      private int id;
      private String name;
      private Set emps;
      //Setters Getters…..
}

<class name="Department">
     <id name="id">
       <generator class="sequence"/>
     </id>
     <property name="name"/>
     <set name="emps" inverse="false">
        <key column="dept_id"/>
        <one-to-many class="Employee"/>
     </set>
</class>

public class Certification {

      private int id;
      private String name;
      private Set emps;
      //Setters Getters…..
}

<class name="Certification">
     <id name="id">
        <generator class="sequence"/>
     </id>
     <property name="name"/>
     <set name="emps" inverse="false" table="emp_cert">
        <key column="cert_id"/>
        <many-to-many column="emp_id" class="Employee"/>
     </set>
</class>

Note that inverse attribute is used only in collection mapping for e.g. set.

Let’s first discuss the one-to-many relationship.

In the Department mapping above, since the inverse attribute has false (default) value for employee set, it indicates that you can also add Employees through Department class.

Consider the code below:

//Set ID from DB.
int empID = 1;
int deptID = 1;

SessionFactory factory = HibernateUtil.getSessionFactory();
Session session = factory.openSession();
Transaction tx = session.beginTransaction();
Employee emp = (Employee) session.get(Employee.class, empID);
Department dept = (Department) session.get(Department.class, deptID);
dept.getEmps().add(emp);//This will be saved in DB as inverse is false.
tx.commit();
session.flush();

The execution of above code will result in an Update SQL statement to update the dept_id value of given Employee.

Now consider the case of inverse=”true”. The mapping would look like this:

<class name="Department">
     <id name="id">
        <generator class="sequence"/>
     </id>
     <property name="name"/>
     <set name="emps" inverse="true">
        <key column="dept_id"/>
        <one-to-many class="Employee"/>
     </set>
</class>

This tells hibernate that Department entity is not responsible for maintaining the relationship.

Thus if the same above code is executed again, Hibernate won’t execute any update SQL and employees’ dept_id won’t be updated.


Thus in one-to-many, we use inverse always in the side which is ‘one’ in the relationship and which indicates that this is not the side which will maintain relationship.

Now consider the case of many-to-many relationship. In this example, relationship between Employee and Certification is many-to-many. An employee can do any number of certificates while a certification can be done by more than one employee.

In many-to-many relationship, both sides or one of them can be responsible for maintaining the relationship. Thus inverse attribute can be set true at any side, which you think is not responsible for maintaining relationship. Consider the code snippet below

//Set ID from DB.
int empID = 1;
int certId = 1;

SessionFactory factory = HibernateUtil.getSessionFactory();
Session session = factory.openSession();
Transaction tx = session.beginTransaction();
Employee emp = (Employee) session.get(Employee.class, empID);
Certification cert= (Certification) session.get(Certification.class, certId);

//If inverse is false in both employee and certification mappings, you can execute any of the below 2 statements. Both will result in same (insertion in emp_cert table)
//But if inverse is true in certification mapping, statement 2 won't work.
//If inverse is true in employee certification, statement 1 won't work.
//emp.getCerts().add(cert); //statemet 1
//cert.getEmps().add(emp); //statement 2

tx.commit();
session.flush();

If inverse is false at both end, any of the below 2 statement can result in insertion of an entry in emp_cert table.

emp.getCerts().add(cert); //statemet 1
cert.getEmps().add(emp); //statement 2

But assume that inverse is true in Certification mapping, then statement 2 won’t result in SQL insert statement for emp_cert table and hence no row will be inserted in this table.

<class name="Certification">
     <id name="id">
        <generator class="sequence"/>
     </id> <property name="name"/>
     <set name="emps" inverse="true" table="emp_cert">
     <key column="cert_id"/>
       <many-to-many column="emp_id" class="Employee"/>   
     </set>
</class>

Similarly, if inverse attribute is true in Employee mapping, then statement 1 above will not insert any row in emp_cert table.

Conclusion: I hope after reading this article and understanding this example, you will realize that inverse attribute in hibernate is an easy to understand and very helpful parameter in optimizing the bidirectional relationship.

4 comments:

Alim Khan said...
This comment has been removed by the author.
Alim Khan said...
This comment has been removed by the author.
Alim Khan said...

nice article

Ashish Pathak said...

thanks for this very nice article..