Hibernate - One to Many
So far we were dealing with Value Objects or Components, which did not have an existence of its own. But now we will be dealing with objects which has its own identity.
Example :
Just imagine can a 'Wing' exist without a 'Bird'? The answer is no. So, if there is a 'Bird' class and it contains a reference of a 'Wing' object. The 'Wing' is a 'Value Object' and cannot exist on its own.
Now, can a 'Cage' exist without a 'Bird'? The answer is yes. So, if a 'Bird' class contains
a reference of a 'Cage' object. The 'Cage' is said to be an 'Entity' as it can exist on its
own.
Entity
An Entity is an object which has its own identity. Each and every entity will have a table defined in the Relational Database.
So far we were considering 'Address' to be a 'Value Object'. This time we will be considering 'Address' to be an Entity.
Let's say 'Address' also have an existence of its own.
Let us see the 'Employee' class:
class Employee {
String id;
String name;
List <Address> addressList;
--- getters and setters ---
}
An 'Employee' can have a 'List' of 'Address' objects. So, in the 'Employee' class we have put:
List <Address> addressList;
The 'Address' class is just the same.
class Address{
int addressId;
String streetName;
String city;
String pinCode;
Employee employee;
---Getters & Setters---
}
So, from the database point of view there is a one-to-many relationship among 'Employee' and 'Address'.
Since, 'Address' is an Entity, an 'Address' object can also access an 'Employee' object. And so we have put :
Employee employee;
And we try to establish a bi-directional relationship between 'Employee' and 'Address'.
Now, since both 'Employee' and 'Address' are Entities, we have to define two mapping files for 'Employee' and 'Address':
1. Firstly in 'Employee.hbm.xml' mapping file we need to tell Hibernate that the 'Address' class has to be treated as a 'List'.
Employee.hbm.xml
<?xml version = "1.0" encoding = "utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name = "Employee" table = "EMPLOYEE">
<id name = "id" type = "string">
<column name = "ID">
</id>
<property name = "name" type = "string">
<column name = "NAME">
</property>
<list name = "addressList">
<key column = "ADDRESS_ID"/>
<list-index column="COUNTER">
<one-to-many class="Address"/>
</list>
</class>
</hibernate-mapping>
In the above statement we have informed Hibernate that multiple 'Address' has to be linked to the 'EMPLOYEE' table and we have used 'List' of java to achieve that.
<list name = "addressList">
Now, to create a link between the 'EMPLOYEE' and 'ADDRESS', this time we have used the primary key 'ADDRESS_ID' in the 'ADDRESS' table to establish a primary key - foreign key relationship.
<key column = "ADDRESS_ID"/>
The next line is specific to 'List'. Since, a 'List' is capable of managing the order of the values inserted, we need an extra column in database to maintain this order as
well. So, we have used the <list-index> tag to create a column 'COUNTER' which will be keeping a track of the order in the Database side.
<list-index column="COUNTER">
Then we have informed Hibernate about the 'Address' class using the '<one-to-many>' tag of Hibernate. This is going to tell Hibernate that, for one 'Employee' there can exist multiple 'Address' objects or addresses.
Please note, <one-to-many> can only be used for Entities.
2. Next, let's write the 'Address.hbm.xml' mapping file:
Address.hbm.xml
<?xml version = "1.0" encoding = "utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name = "Address" table = "ADDRESS">
<id name = "addressId" type = "string">
<column name = "ADDRESS_ID">
</id>
<property name = "streetName" type = "string">
<column name = "STREET_NAME">
</property>
<property name = "city" type = "string">
<column name = "CITY">
</property>
<property name = "pinCode" type = "string">
<column name = "PIN_CODE">
</property>
<many-to-one name="employee" class="Employee" not-null="true">
<column name="ADDRESS_ID"/>
</many-to-one>
</class>
</hibernate-mapping>
3. Now that we have defined the mapping files for the Entities 'Employee' and 'Address', we will be writing the main class where we will be saving the 'Employee' data and its corresponding 'Address' data to the database using Hibernate.
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class HibernateSave {
public static void main(String[] args) {
static SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
Session session = sessionFactory.openSession();
session.beginTransaction();
List <Address> addressList = new ArrayList <Address>();
Address localAddress = new Address();
localAddress.setStreetName("Walls Street");
localAddress.setCity("Delhi");
localAddress.setPinCode("110012");
addressList.add(localAddress);
Address permanentAddress = new Address();
permanentAddress.setStreetName("Murugeshpalya");
permanentAddress.setCity("Bangalore");
permanentAddress.setPinCode("560019");
addressList.add(permanentAddress);
Employee employee = new Employee();
employee.setId(1);
employee.setName("Joe");
employee.setAddressList(addressList);
session.save(employee);
session.save(localAddress);
session.save(permanentAddress);
session.getTransaction().commit();
session.close();
sessionFactory().close();
}
}
So, in the above example we have created two 'Address'.
First is the local address of the Employee.
Address localAddress = new Address();
localAddress.setStreetName("Walls Street");
localAddress.setCity("Delhi");
localAddress.setPinCode("110012");
Second is the Permanent Address of the Employee.
Address permanentAddress = new Address();
permanentAddress.setStreetName("Murugeshpalya");
permanentAddress.setCity("Bangalore");
permanentAddress.setPinCode("560019");
And since the Addresses are stored in 'List'. We have created a 'List' type object
List <Address> addressList = new ArrayList <Address>();
to store the 'localAddress'
addressList.add(localAddress)
addressList.add(permanentAddress);
And finally we need to add the List 'addressList' to the 'Employee' object.
employee.setAddressList(addressList);
and save the 'employee' object using the 'save()' method of Hibernate.
session.save(employee);
But we are still not done. Since, the 'Address' is an Entity, we have to save 'localAddress' and 'permanentAddress' using the save() method of Hibernate.
session.save(localAddress);
session.save(permanentAddress);
CASCADE
So, what if we do not want to write the above two lines.
i.e.
session.save(localAddress);
session.save(permanentAddress);
Hibernate also provides a way to do that,
In 'Employee.hbm.xml' mapping file we just need to add 'cascade="all"' in the '<list>' tag:
<list name = "addressList" cascade="all">
<key column = "ADDRESS_ID"/>
<list-index column="COUNTER">
<one-to-many class="Address"/>
</list>
With cascade="all", we are telling Hibernate that if a record of the parent object (i.e. Employee) is modified or deleted or needs to be saved, the same record should be modified or deleted or needs to be saved in the child object (i.e. Address). And most importantly, we do not have to write the extra lines:
session.save(localAddress);
session.save(permanentAddress);
EMPLOYEE
ADDRESS
COUNTER |
ID |
ADDRESS_ID |
STREET_NAME |
CITY |
PIN_CODE |
0 |
1 |
1 |
Walls street |
Delhi |
110012 |
1 |
1 |
2 |
Murugeshpalya |
Bangalore |
560019 |
If you see the above table, a new column 'COUNTER' is created in the 'ADDRESS' table which keeps track on the order using the index(Which is 0 for the first row and 1 for the second).