Hibernate One To Many Annotation tutorial

Welcome to the Hibernate Tutorial Series. In previous tutorial we saw how to implement One to Many relationship using XML mapping. In this tutorial we will modify the source code from previous One To Many XML mapping tutorial and add JPA/Annotation support to it.

1. Database Setup

For this example, we will use MySQL database. Create following two tables in MySQL. Note that Employee and Department table exhibits One-to-many relationship. Each Department can be associated with multiple Employees and each Employee can have only one Department.
one-to-many-relationship-diagram

CREATE TABLE `department` (
	`department_id` BIGINT(20) NOT NULL AUTO_INCREMENT,
	`dept_name` VARCHAR(50) NOT NULL DEFAULT '0',
	PRIMARY KEY (`department_id`)
)
COLLATE='latin1_swedish_ci'
ENGINE=InnoDB
ROW_FORMAT=DEFAULT
AUTO_INCREMENT=115



CREATE TABLE `employee` (
	`employee_id` BIGINT(10) NOT NULL AUTO_INCREMENT,
	`firstname` VARCHAR(50) NULL DEFAULT NULL,
	`lastname` VARCHAR(50) NULL DEFAULT NULL,
	`birth_date` DATE NULL DEFAULT NULL,
	`cell_phone` VARCHAR(15) NULL DEFAULT NULL,
	`department_id` BIGINT(20) NULL DEFAULT NULL,
	PRIMARY KEY (`employee_id`),
	INDEX `FK_DEPT` (`department_id`),
	CONSTRAINT `FK_DEPT` FOREIGN KEY (`department_id`) REFERENCES `department` (`department_id`)
)
COLLATE='latin1_swedish_ci'
ENGINE=InnoDB
ROW_FORMAT=DEFAULT

2. Project Setup

Download the source code: Hibernate-one-to-many-set-example.zip (9 KB) and import the project in Eclipse. We will update the source code.

3. Update Maven Dependency

File: pom.xml

<?xml version="1.0" encoding="UTF-8"?><project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>HibernateCache</groupId>
  <artifactId>HibernateCache</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <description></description>
  <dependencies>
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>ejb3-persistence</artifactId>
      <version>1.0.1.GA</version>
    </dependency>
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-annotations</artifactId>
      <version>3.3.1.GA</version>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.10</version>
    </dependency>
  </dependencies>
</project>

3. Remove Hibernate Mapping (hbm) Files

We are not going to use hibernate mapping files or hbm files as we will map the model using Java 5 Annotations. Delete the files employee.hbm.xml and department.hbm.xml.

4. Update Hibernate Model Class

File: Employee.java

package net.viralpatel.hibernate;

import java.sql.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;


@Entity
@Table(name="EMPLOYEE")
public class Employee {

	@Id
	@GeneratedValue
	@Column(name="employee_id")
	private Long employeeId;
	
	@Column(name="firstname")
	private String firstname;
	
	@Column(name="lastname")
	private String lastname;
	
	@Column(name="birth_date")
	private Date birthDate;
	
	@Column(name="cell_phone")
	private String cellphone;

	@ManyToOne
	@JoinColumn(name="department_id")
	private Department department;
	
	public Employee() {
		
	}
	
	public Employee(String firstname, String lastname, String phone) {
		this.firstname = firstname;
		this.lastname = lastname;
		this.birthDate = new Date(System.currentTimeMillis());
		this.cellphone = phone;
	}

	// Getter and Setter methods
}

@ManyToOne annotation defines a single-valued association to another entity class that has many-to-one multiplicity. It is not normally necessary to specify the target entity explicitly since it can usually be inferred from the type of the object being referenced.

@JoinColumn is used to specify a mapped column for joining an entity association.

File: Department.java

package net.viralpatel.hibernate;

import java.util.Set;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@Entity
@Table(name="DEPARTMENT")
public class Department {

	@Id
	@GeneratedValue
	@Column(name="DEPARTMENT_ID")
	private Long departmentId;
	
	@Column(name="DEPT_NAME")
	private String departmentName;
	
	@OneToMany(mappedBy="department")
	private Set<Employee> employees;

	// Getter and Setter methods
}

@OneToMany annotation defines a many-valued association with one-to-many multiplicity.
If the collection is defined using generics to specify the element type, the associated target entity type need not be specified; otherwise the target entity class must be specified.

The association may be bidirectional. In a bidirectional relationship, one of the sides (and only one) has to be the owner: the owner is responsible for the association column(s) update. To declare a side as not responsible for the relationship, the attribute mappedBy is used. mappedBy refers to the property name of the association on the owner side. In our case, this is passport. As you can see, you don’t have to (must not) declare the join column since it has already been declared on the owners side.

5. Update Hibernate Configuration File

File: hibernate.cfg.xml

<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
 
<hibernate-configuration>
    <session-factory>
        <!-- Database connection settings -->
        <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="connection.url">jdbc:mysql://localhost:3306/tutorial</property>
        <property name="connection.username">root</property>
        <property name="connection.password"></property>
        
        <property name="connection.pool_size">1</property>
        <property name="dialect">org.hibernate.dialect.MySQLDialect</property>
        <property name="current_session_context_class">thread</property>
        <property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>
        <property name="show_sql">true</property>
        <property name="hbm2ddl.auto">validate</property>
 
	<mapping class="net.viralpatel.hibernate.Department"/>
	<mapping class="net.viralpatel.hibernate.Employee"/>
 		 
    </session-factory>
</hibernate-configuration>

6. Review Project Structure

Once all the source files are in place, the project structure should looks like below:

7. Execute example

Execute following Main.java file which will create one Department and two Employees.
File: Main.java

package net.viralpatel.hibernate;

import org.hibernate.Session;
import org.hibernate.SessionFactory;

public class Main {

	@SuppressWarnings("unchecked")
	public static void main(String[] args) {

		SessionFactory sf = HibernateUtil.getSessionFactory();
		Session session = sf.openSession();
		session.beginTransaction();

		Department department = new Department();
		department.setDepartmentName("Sales");
		session.save(department);
		
		Employee emp1 = new Employee("Nina", "Mayers", "111");
		Employee emp2 = new Employee("Tony", "Almeida", "222");

		emp1.setDepartment(department);
		emp2.setDepartment(department);
		
		session.save(emp1);
		session.save(emp2);

		session.getTransaction().commit();
		session.close();
	}
}

Output:

Hibernate: insert into DEPARTMENT (DEPT_NAME) values (?)
Hibernate: insert into EMPLOYEE (firstname, lastname, birth_date, cell_phone, department_id) values (?, ?, ?, ?, ?)
Hibernate: insert into EMPLOYEE (firstname, lastname, birth_date, cell_phone, department_id) values (?, ?, ?, ?, ?)

Thus we saw in above example how to implement One to Many relationship in Hibernate using Annotation. Also we used java.util.Set for our example.

8. One- To Many Bi-directional Indexed mapping

Above example was pretty straightforward. We mapped multiple employees with a department. For this we used java.lang.Set. But the order in which the employees are mapped with department is not conserved. What if you have a requirement where you want to preserve order for entities that you save.

We can use java.util.List to map ordered entities. For this first we will need to add a column IDX in Employee table which will store the index value.

8.1 Modify Employee table

CREATE TABLE `employee` (
	`employee_id` BIGINT(10) NOT NULL AUTO_INCREMENT,
	`firstname` VARCHAR(50) NULL DEFAULT NULL,
	`lastname` VARCHAR(50) NULL DEFAULT NULL,
	`birth_date` DATE NULL DEFAULT NULL,
	`cell_phone` VARCHAR(15) NULL DEFAULT NULL,
	`department_id` BIGINT(20) NULL DEFAULT NULL,
	`idx` INT(11) NULL DEFAULT NULL,
	PRIMARY KEY (`employee_id`),
	INDEX `FK_DEPT` (`department_id`),
	CONSTRAINT `FK_DEPT` FOREIGN KEY (`department_id`) REFERENCES `department` (`department_id`)
)
COLLATE='latin1_swedish_ci'
ENGINE=InnoDB
ROW_FORMAT=DEFAULT
AUTO_INCREMENT=33

8.2 Modify Hibernate Model classes

Update Employee.java and Department.java model classes and add the list support. Also note that we are changing the annotations.

File: Department.java

package net.viralpatel.hibernate;

import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.Table;

import org.hibernate.annotations.IndexColumn;

@Entity
@Table(name="DEPARTMENT")
public class Department {

	@Id
	@GeneratedValue
	@Column(name="DEPARTMENT_ID")
	private Long departmentId;
	
	@Column(name="DEPT_NAME")
	private String departmentName;
	
	@OneToMany(cascade={CascadeType.ALL})
	@JoinColumn(name="department_id")
	@IndexColumn(name="idx")
	private List<Employee> employees;

	// Getter and Setter methods
}

Note that in Department entity class, we removed mappedBy clause from @OneToMany. This mark Department as the relationship owner and make it responsible to update foriegn keys and index values.

Also we specified index coulmn using @IndexColumn annotation to specify which column in Employee table we would like to store index in.

File: Employee.java

package net.viralpatel.hibernate;

import java.sql.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;


@Entity
@Table(name="EMPLOYEE")
public class Employee {

	@Id
	@GeneratedValue
	@Column(name="employee_id")
	private Long employeeId;
	
	@Column(name="firstname")
	private String firstname;
	
	@Column(name="lastname")
	private String lastname;
	
	@Column(name="birth_date")
	private Date birthDate;
	
	@Column(name="cell_phone")
	private String cellphone;

	@ManyToOne
	@JoinColumn(name="department_id", 
				insertable=false, updatable=false, 
				nullable=false)
	private Department department;
	
	public Employee() {
		
	}
	
	public Employee(String firstname, String lastname, String phone) {
		this.firstname = firstname;
		this.lastname = lastname;
		this.birthDate = new Date(System.currentTimeMillis());
		this.cellphone = phone;
	}

	// Getter and Setter methods
}	

8.3 Execute List example

File: Department.java

package net.viralpatel.hibernate;

import java.util.ArrayList;

import org.hibernate.Session;
import org.hibernate.SessionFactory;

public class Main {

	@SuppressWarnings("unchecked")
	public static void main(String[] args) {

		SessionFactory sf = HibernateUtil.getSessionFactory();
		Session session = sf.openSession();
		session.beginTransaction();
		
		Department department = new Department();
		department.setDepartmentName("Sales");

		Employee emp1 = new Employee("Nina", "Mayers", "111");
		Employee emp2 = new Employee("Tony", "Almeida", "222");
		
		department.setEmployees(new ArrayList<Employee>());
		department.getEmployees().add(emp1);
		department.getEmployees().add(emp2);
		
		session.save(department);

		session.getTransaction().commit();
		session.close();
	}
}

Output:

Hibernate: insert into DEPARTMENT (DEPT_NAME) values (?)
Hibernate: insert into EMPLOYEE (birth_date, cell_phone, firstname, lastname) values (?, ?, ?, ?)
Hibernate: insert into EMPLOYEE (birth_date, cell_phone, firstname, lastname) values (?, ?, ?, ?)
Hibernate: update EMPLOYEE set department_id=?, idx=? where employee_id=?
Hibernate: update EMPLOYEE set department_id=?, idx=? where employee_id=?


one-to-many-list-result


Download Source Code

Hibernate-One-To-Many-Annotation-Set.zip (8 KB)
Hibernate-One-To-Many-Annotation-List.zip (8 KB)



61 Comments

  • suresh 29 May, 2014, 22:38

    Smallestsweetest exaple ine the world love u sir

  • Salim 8 July, 2014, 12:47

    Hi Viral and all,

    can someone please resolve my problem of mapping one to many relationship between a primary key and a composite key.
    http://stackoverflow.com/questions/24609861/hibernate-mapping-one-to-many-relation-ship-between-primary-key-and-composite-ke

  • zeinab 16 July, 2014, 16:28

    ur post saved my day,
    thank u

  • swekha 18 September, 2014, 23:18

    what is the use of mappedby??if we will not specify then what will happen???

  • ali 10 November, 2014, 2:21

    Hi
    How save foreign key in employee table?
    thanks

  • mi 17 November, 2014, 14:36

    why is it that when you update the non owning entity .. then update the owning entity, the update on the non owning entity will not take effect ??
    is it because of the cascadetypes??
    what would be the appropriate cascade type for this kind of problem??
    should i use merge not update ??
    how ??

Leave a Reply

Your email address will not be published. Required fields are marked *

Note

To post source code in comment, use [code language] [/code] tag, for example:

  • [code java] Java source code here [/code]
  • [code html] HTML here [/code]

Current ye@r *