Today we are going to understand how to perform a one-to-many mapping of objects between two Entity classes using Hibernate.
One-to-Many relationship is all about how an object of one class is associated with multiple objects of another class.
To understand one-to-one mapping, let's take an example of two Entity classes - User_Details and Mobile_Num, where objects of both Entity classes
are stored in separated database tables and each user object of User_Details is associated with multiple objects of Mobile_Num.
Let's put this example to work and make it easier for you to understand.
Note :
In the upcoming example, We are going to create a Java class which will be mapped to a table in our database(Oracle Express Edition 10g).
We are going to use some Hibernate Annotations in the upcoming example, hence, there will be no need to create the mapping-resource with extension hbm.xml .
For those who are not aware of Hibernate Annotations, please read
Hibernate Annotations for reference.
And yes, one more thing, we are going to create this program in an Integrated Development Environment (IDE),
so you may use any IDE of your choice.
Creating a primary Entity class
We are going to create a class that will contain all the Java code files
representing our Model class(whose objects needs to be persisted)
and the class that calls the Hibernate API and create an object-relational mapping(ORM) and perform object persistence.
This is a simple Java class whose objects needs to be persisted/saved, these objects are also known as Plain Old Java Objects(POJO) or Entity class.
Some may even refer to such class whose objects needs to be persisted as the Model class.
In the upcoming Entity class, we are going to a very important annotation -
@OneToMany - This annotation is placed just above an object property of another Entity class, which tells the Hibernate that the objects of this Entity class maintains a
one-to-many association with the objects of the Entity class containing this annotation.
cascade- The sub-element of @OneToMany annotation allows you to take an action on not just the parent object of one-to-many relationship but also on the associated child object as well.
For example -
cascade = {CascadeType.ALL}
Says - if an object of parent class is removed, refreshed/updated, merged or saved/persisted then its associated child class object should also be
removed, refreshed/updated, merged or saved/persisted(depending on the operation).
@JoinColumn - This annotation is used to specify a column of parent/primary class for joining the child/secondary entity class as a foreign key,
which is a primary key in the primary class.
Next, we are going to create the child Entity class whose objects will also be persisted in a separate database table.
The object of this class will contain a specific piece information about each user, such as -
email address and phone number. This class is also an entity class, hence it will be preceded with the @Entity annotation.
We have used a few important annotations such as -
GenerationValue - Using this annotation, we can specified a strategy which is used for the generation of primary key values, using its
strategy element as GenerationType.SEQUENCE, generates
the sequence of primary key column in the table on the basis of a sequence generator referred by element generator.
This sequence generator is defined in @SequenceGenerator annotation.
@SequenceGenerator - This annotations is used define a primary key generator.
The name element of @SequenceGenerator annotation is required to specify the name of primary key generator,
which is referenced from @GeneratedValue annotation from its generator element.
Mobile_Num.java
package decodejava;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
@Entity
@Table (name ="MOBILE_NUM")
public class Mobile_Num
{
@Id
@Column(name="MOBILE_ID")
@SequenceGenerator(name = "id_generator", sequenceName = "id_seq")
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "id_generator")
private int id;
public int getId()
{
return id;
}
public void setId(int id)
{
this.id = id;
}
private int mobile_number;
public int getMobile_number()
{
return mobile_number;
}
public void setMobile_number(int mobile_number)
{
this.mobile_number = mobile_number;
}
}
Creating the class that calls the Hibernate API
This class will create some User_Details objects , which will be persisted using the Hibernate API and an
object-relational mapping(ORM) in the database is performed.
The Hiber class creates a Configuration object, used to configure the Hibernate. This is the first object we use when using the Hibernate.
This object is used to specify the location of a configuration file and
mapping document used by Hibernate. Using Configuration object we can create a SessionFactory object, which is eventually used to create a Session
object to perform the object persistence operations.
Note : We will not need to set the Id value of each object, as it is automatically done by Hibernate's sequence object by using @GeneratedValue annotation.
Hiber.java
package decodejava;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class Hiber
{
public static void main(String... ar)
{
Configuration config = new Configuration();
SessionFactory sf = config.configure().buildSessionFactory();
Session session = sf.openSession();
//Creating the first User details
User_Details1 user1 = new User_Details1();
user1.setFirstName("First");
user1.setLastName("User");
//Creating the contact info for the user
Mobile_Num mob1 = new Mobile_Num();
mob1.setMobile_number(1111111);
//Creating the contact info for the user
Mobile_Num mob2 = new Mobile_Num();
mob2.setMobile_number(2222222);
List<Mobile_Num> collection = new ArrayList<Mobile_Num>();
collection.add(mob1);
collection.add(mob2);
//Setting the collection of mobiles to a user
user1.setMob(collection);
session.beginTransaction();
session.save(user1); //Saving the first User_Details object
session.getTransaction().commit();
session.close();
//Retrieve and displaying the user details.a
session = sf.openSession();
session.beginTransaction();
user1 = (User_Details1)session.get(User_Details1.class,1);
System.out.println("Retrieving the saved objects");
//Retrieving details of the first user.
System.out.println("First User");
System.out.println("User Id : " + user1.getId());
System.out.println("First Name : " + user1.getFirstName());
System.out.println("Last Name : " +user1.getLastName());
collection = (List<Mobile_Num>) user1.getMob();
mob1 = collection.get(0);
System.out.println("Mobile id : " + mob1.getId());
System.out.println("Mobile number : " + mob1.getMobile_number());
mob2 = collection.get(1);
System.out.println("Mobile id : " + mob1.getId());
System.out.println("Mobile number : " + mob1.getMobile_number());
}
}
Adding JARs
We are going to add some JARs files to the build path of our Java project.
These JARs make the Hibernate work with our database using a specific JDBC Driver for our particular
database.
All these JARs are included in the folder named required(within our Hibernate installation folder).
So, we need to add all the JARs in the required to our build path of our Java project.
Finally, we are going to add one more JAR file.
This is a specific JDBC JAR file(ojdbc14.jar) required by Hibernate to connect to our database(Oracle Express Edition 10g) and perform
object-relational mapping and object persistence.
Next, we are going to add a configuration file to our project.
This configuration document ends with an extension .cfg.xml,
and it is named as hibernate.cfg.xml.
This configuration file is an xml file and it allows us to specify the following features -
A database to which Hibernate connects to perform object-relational mapping and object persistence.
The JDBC driver class for our specific database, used by the Hibernate to connect to the database.
The username and password of database, using which the Hibernate connects to the database.
An SQL Dialect, used by the Hibernate to instruct the database.
A Entity/POJO class which will be mapped to a database table by Hibernate.
A command to echo all the SQL commands which are executed by the Hibernate on stdout.
The mapping entity class, used by Hibernate to correctly map the class to a database table. We will specify two Entity classes, User_Details and Mobile_Num.
hibernate.cfg.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- Database connection settings -->
<property name="connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
<property name="connection.url">jdbc:oracle:thin:@localhost:1521:XE</property>
<property name="connection.username">scott</property>
<property name="connection.password">tiger</property>
<!-- JDBC connection pool (use the built-in) -->
<property name="connection.pool_size">1</property>
<!-- SQL dialect -->
<property name="dialect">org.hibernate.dialect.Oracle10gDialect</property>
<!-- Disable the second-level cache -->
<property name="cache.provider_class">org.hibernate.cache.internal.NoCacheProvider</property>
<!-- Echo all executed SQL to stdout -->
<property name="show_sql">true</property>
<!-- Drop and re-create the database schema on startup -->
<property name="hbm2ddl.auto">create</property>
<!-- Names the mapping entity class -->
<mapping class ="decodejava.User_Details"/>
<mapping class ="decodejava.Mobile_Num"/>
</session-factory>
</hibernate-configuration>
Directory Structure of Hibernate Project
The picture above depicts how and where to arrange Pojo/Entity(.java) file, a secondary Entity (.java) class file and the other the class(that calls the hibernate into action)
in a specific directory structure.
Project Folder - HibernateOneToManyMapping is the name of our Project and it is a top-level directory.
This project folder contains the main package of our project i.e. decodejava,
which contains the parent Pojo/Entity class file i.e. User_Details.java, the secondary Entity class i.e. Mobile_Num.java
and the class that calls the hibernate into action i.e. Hiber.java.
Besides this, the project folder also contains the the hibernate configuration file i.e. hibernate.cfg.xml, and
as we have used some Hibernate Annotations in this project, hence, there will be no need to create the mapping-resource
with extension hbm.xml .
The project folder also contains all the needed Hibernate JARs present in the required folder of Hibernate
installation and the JDBC JAR file(ojdbc14.jar) required by Hibernate to connect to our database.
Execution
Finally, after executing Hiber class, two separate tables will be created in the database -
USER_DETAILS, representing object-relational mapping(ORM) for User_Details class.
MOBILE_NUM, representing object-relational mapping(ORM) for Mobile_Num class displaying
one-to-many relationship from USER_DETAILS to MOBILE_NUM
Hence, when you execute the Hiber.java class, you will see the following output which shows one-to-many mapping .
Hibernate: create sequence hibernate_sequence start with 1 increment by 1
Hibernate: create sequence id_seq start with 1 increment by 1
Hibernate: create table MOBILE_NUM (MOBILE_ID number(10,0) not null, mobile_number number(10,0) not null, USER_ID number(10,0), primary key (MOBILE_ID))
Hibernate: create table USER_DETAILS (USER_ID number(10,0) not null, First_Name varchar2(255 char), Last_Name varchar2(255 char), primary key (USER_ID))
Hibernate: alter table MOBILE_NUM add constraint FKq26cahdr6ogqve5cli5lvy09o foreign key (USER_ID) references USER_DETAILS
Hibernate: select hibernate_sequence.nextval from dual
Hibernate: select id_seq.nextval from dual
Hibernate: select id_seq.nextval from dual
Hibernate: insert into USER_DETAILS (First_Name, Last_Name, USER_ID) values (?, ?, ?)
Hibernate: insert into MOBILE_NUM (mobile_number, MOBILE_ID) values (?, ?)
Hibernate: insert into MOBILE_NUM (mobile_number, MOBILE_ID) values (?, ?)
Hibernate: update MOBILE_NUM set USER_ID=? where MOBILE_ID=?
Hibernate: update MOBILE_NUM set USER_ID=? where MOBILE_ID=?
Hibernate: select user_detai0_.USER_ID as USER_ID1_1_0_, user_detai0_.First_Name as First_Name2_1_0_, user_detai0_.Last_Name as Last_Name3_1_0_ from USER_DETAILS user_detai0_ where user_detai0_.USER_ID=?
Retrieving the saved objects
First User
User Id : 1
First Name : First
Last Name : User
Hibernate: select mob0_.USER_ID as USER_ID3_0_0_, mob0_.MOBILE_ID as MOBILE_ID1_0_0_, mob0_.MOBILE_ID as MOBILE_ID1_0_1_, mob0_.mobile_number as mobile_number2_0_1_ from MOBILE_NUM mob0_ where mob0_.USER_ID=?
Mobile id : 1
Mobile number : 1111111
Mobile id : 2
Mobile number : 2222222
This output shows you all the SQL commands executed by the Hibernate within the database to map the Java class to a database table and perform all the
activities of saving and retrieving the objects of both Entity classes involved in a one-to-one relationship.
Moreover, if you see the following SQL query in the database -
select * from USER_DETAILS;
you will get the following output displaying the objects of User_Details Entity class stored in USER_DETAILS table, as shown below.
And on executing the second query in the database -
select * from MOBILE_NUM;
you will get the following output displaying the objects of child Mobile_Num class stored in MOBILE_NUM table, as shown below, as shown below.
This table shows you how our first user(having id =1), is associated with multiple mobiles(each having different ids)
This output shows how each object of User_Details class is involved in a one-to-many relationship with objects of Mobile_Num.
Note :
If you wish to create a new table showing one-to-many relationship between two classes then instead of @JoinColumn in primary class,
you will have to use @JoinTable annotation.
The output clearly shows you that Hibernate has used its sequence object to generate the value of each primary key(Id) automatically as we have used @GeneratedValue
annotations with it.