hibernate

Hibernate One-to-Many Relationship Example (XML Mapping and Annotation)

In this example we are going to see how to map classes to databases tables which have one-to-many relationships. We are going to see the mapping both with XML Mapping and with Annotations.

So these are the tools we are going to use on a Windows 7 platform:

  • JDK 1.7
  • Maven 3.0.5
  • Hibernate 3.6.3.Final
  • MySQL JDBC driver 5.1.9
  • Eclipse 4.2 Juno

Mapping the Classes using XML Mapping

1. Download the project from the previous tutorial

Take a close look at Hibernate 3 with Maven 2 and MySQL 5 Example (XML Mapping and Annotation) tutorial to learn how to create a project with Maven and to create the basic structure of the project. This is the project we are going to use here. So follow the steps to create the project.. You should also check Hibernate One-to-One Relationship Example (XML Mapping and Annotation).

Make sure the project structure looks like this:

project-structure

2. One-to-Many relationships

Imagine that you have tables student and projects in a database. You want every student to be able to work on multiple projects, but each individual project can be assigned to only one student. So, student has one-to-many relationship with projects, and projects has many-to-one relationship  with student.

Let’s see the MySQL scripts that create the aforementioned tables.

DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
  `STUDENT_ID` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
  `STUDENT_NAME` VARCHAR(10) NOT NULL,
  `STUDENT_AGE` VARCHAR(20) NOT NULL,
  PRIMARY KEY (`STUDENT_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

DROP TABLE IF EXISTS `projects`;
CREATE TABLE  `projects` (
  `PROJECT_ID` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
  `TITLE` VARCHAR(100) NOT NULL,
  `SEMESTER` VARCHAR(100) NOT NULL,
  `STUDENT_ID` INT(10) UNSIGNED NOT NULL,
  PRIMARY KEY (`PROJECT_ID`),
  UNIQUE KEY `UNI_TITLE` (`TITLE`),
  KEY `FK_STUDENT_TRANSACTION_STUDENT_ID` (`STUDENT_ID`),
  CONSTRAINT `FK_STUDENT_TRANSACTION_STUDENT_ID` FOREIGN KEY (`STUDENT_ID`) 
  REFERENCES `student` (`STUDENT_ID`)  ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=36 DEFAULT CHARSET=utf8;

This is a simple diagram of the tables, that shows this relationship, created with MySQL Workbech (which I personally like a lot).

diagram

As you can see “projects” holds “STUDENT_ID“, which is the primary key (and therefore the identifier) of the “student” table. “STUDENT_ID” is also the a key of  “projects“, and it is a foreign key to the ”student” table as well, but it is not a primary key to “projects“. And that’s what connected these tables with the one-to-many relationship. It means that, for every student tuple in  ”student” table, there will be many corresponding tuples in “projects“ . But each project will be unique and only one student can be assigned to it.

3. Edit pom.xml

Make sure pom.xml contains these dependencies:

pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.javacodegeeks.enterprise.hibernate</groupId>
  <artifactId>HibernateMySQLExample</artifactId>
  <packaging>jar</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>HibernateMySQLExample</name>
  <url>http://maven.apache.org</url>
  <!-- JBoss repository for Hibernate -->
	<repositories>
		<repository>
			<id>JBoss repository</id>
			<url>http://repository.jboss.org/nexus/content/groups/public/</url>
		</repository>
	</repositories>

	<dependencies>

		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.8.2</version>
			<scope>test</scope>
		</dependency>

		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>jcl-over-slf4j</artifactId>
			<version>1.7.0</version>
			<scope>runtime</scope>
		</dependency>

		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>1.7.0</version>
			<scope>runtime</scope>
		</dependency>

		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-log4j12</artifactId>
			<version>1.7.0</version>
			<scope>runtime</scope>
		</dependency>

		<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>1.2.14</version>
		</dependency>

		<!-- MySQL database driver -->

		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.9</version>
		</dependency>

		<!-- Hibernate framework -->

		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-core</artifactId>
			<version>3.6.3.Final</version>
		</dependency>

		<dependency>
			<groupId>javassist</groupId>
			<artifactId>javassist</artifactId>
			<version>3.12.1.GA</version>
		</dependency>

	</dependencies>

</project>

3. Create the classes

So, these are the classes that will be mapped to database tables:

You have to update Student.java file to this:

Student.java:

package com.javacodegeeks.enterprise.hibernate;

import java.util.HashSet;
import java.util.Set;

public class Student implements java.io.Serializable {

	private static final long serialVersionUID = 1L;

	private Integer studentId;
	private String  studentName;
	private String  studentAge;
	private Set<Project>  studentProjects = new HashSet<Project>(0);

	public Student() {
	}

	public Student(String studentName, String studentAge) {
		this.studentName = studentName;
		this.studentAge = studentAge;
	}

	public Integer getStudentId() {
		return studentId;
	}

	public void setStudentId(Integer studentId) {
		this.studentId = studentId;
	}

	public String getStudentName() {
		return studentName;
	}

	public void setStudentName(String studentName) {
		this.studentName = studentName;
	}

	public String getStudentAge() {
		return studentAge;
	}

	public void setStudentAge(String studentAge) {
		this.studentAge = studentAge;
	}

	public Set<Project> getStudentProjects() {
		return studentProjects;
	}

	public void setStudentProjects(Set<Project> studentProjects) {
		this.studentProjects = studentProjects;
	}

}

The above class will be mapped, of course to “student” table. Note that Student class holds a  HashSet of Project instances. This means that one student can work on many projects.

Now you have to create a new Project class. Go to the Package explorer and find the main source package of our project:

project-structure

Right Click -> New -> Class:

new-class

Project.java:

package com.javacodegeeks.enterprise.hibernate;

public class Project implements java.io.Serializable{

	private Integer projectId;

	private String title;
	private String semester;

	private Student student;

	public Project(String title, String semester){
		this.title = title;
		this.semester = semester;
	}

	public Integer getProjectId() {
		return projectId;
	}

	public void setProjectId(Integer projectId) {
		this.projectId = projectId;
	}

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public String getSemester() {
		return semester;
	}

	public void setSemester(String semester) {
		this.semester = semester;
	}

	public Student getStudent() {
		return student;
	}

	public void setStudent(Student student) {
		this.student = student;
	}

	private static final long serialVersionUID = 1L;
}

Note that the above class holds a Student instance.

4. XML Mapping files

Go to /src/main/resources/com/javacodegeeks/enterprise/hibernate folder and create the following xml files:

Student.hbm.xml:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>
    <class name="com.javacodegeeks.enterprise.hibernate.Student" table="student" catalog="tutorials">
        <id name="studentId" type="java.lang.Integer">
            <column name="STUDENT_ID" />
            <generator class="identity" />
        </id>
        <property name="studentName" type="string">
            <column name="STUDENT_NAME" length="10" not-null="true" unique="true" />
        </property>
        <property name="studentAge" type="string">
            <column name="STUDENT_Age" length="20" not-null="true" unique="true" />
        </property>

        <set name="studentProjects" table="projects"  inverse="true" lazy="true" fetch="select">

            <key>
                <column name="STUDENT_ID" not-null="true" />
            </key>

            <one-to-many class="com.javacodegeeks.enterprise.hibernate.Project" />

        </set>

    </class>
</hibernate-mapping>

Here we describe that  Student  has a one-to-many relationship with Project. We also use inverse  = true attribute . inverse attribute is always present in one-to-many and many-to-many relationship and it denotes which side is responsible to for the relationship. It’s default value is false. In our case Project is the relationship owner. That means that the Project class is responsible to handle the “connection” between these two classes (Student and Project).  If you want to add a new “couple” to the relationship, you have to add a Student instance to the Project's student attribute. And again, if you want to delete a “couple” of the relationship you have to null the student attribute of the Project class. lazy = true attribute, is a hint to the persistence provider runtime that data should be fetched when it is first accessed. If no fetch strategy is declared, then lazy is used by default. On the other hand, EAGER strategy is a requirement on the persistence provider runtime that data must be fecthed when asked and not only when accessed.

Project.hbm.xml:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 25 April 2011 7:52:33 PM by Hibernate Tools 3.4.0.CR1 -->
<hibernate-mapping>
	<class name="com.javacodegeeks.enterprise.hibernate.Project" table="projects" 	catalog="tutorials">

		<id name="projectId" type="java.lang.Integer">
			<column name="PROJECT_ID" />
			 <generator class="identity" />
		</id>

		<property name="title" type="string">
			<column name="TITLE" length="100" not-null="true" unique = "true" />
		</property>

		<property name="semester" type="string">
			<column name="SEMESTER" length="100"  />
		</property>

		<many-to-one name="student" class="com.javacodegeeks.enterprise.hibernate.Student" fetch="select">
                    <column name="STUDENT_ID" not-null="true" />
                </many-to-one>
	</class>
</hibernate-mapping>

5. Update Hibernate configuration file:

Go to the package explorer and find hibernate.cfg.xml file and paste the following code:

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>
		<property name="hibernate.bytecode.use_reflection_optimizer">false</property>
		<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
		<property name="hibernate.connection.username">root</property>
		<property name="hibernate.connection.password"></property>
		<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/tutorials</property>
		<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
		<property name="show_sql">true</property>
		<mapping resource="com/javacodegeeks/enterprise/hibernate/Student.hbm.xml"></mapping>
		<mapping resource="com/javacodegeeks/enterprise/hibernate/Project.hbm.xml"></mapping>
	</session-factory>
</hibernate-configuration>

This is pretty much the same as the previous tutorial. We simply added the mapping relationships of the two classes.

6. Code the Application.

Go to App.java file and paste the following code:

App.java:

package com.javacodegeeks.enterprise.hibernate;

import org.hibernate.Session;

import com.javacodegeeks.enterprise.hibernate.utils.HibernateUtil;

public class App 
{
    public static void main( String[] args )
    {

        Session session = HibernateUtil.getSessionFactory().openSession();

        session.beginTransaction();
        Student student = new Student();

        student.setStudentName("Jeremy");
        student.setStudentAge("45");

        Project project1 = new Project("Secure Systems","Spring");
        project1.setStudent(student);

        student.getStudentProjects().add(project1);

        Project project2 = new Project("Databases","Spring");
        project2.setStudent(student);

        student.getStudentProjects().add(project2);

        session.save(student);
        session.save(project1);
        session.save(project2);

        session.getTransaction().commit();
        System.out.println("Great! Student was saved");

    }
}

The above code has some notable parts. First of all we obtain a Session from the SessionFactory instance of our HibernateUtils class. Then we start a transaction with the database. We simply create one instance of Student and two instances of Project. Then we add the projects to the student’s set. And then, we save to the Session all the class instances and finally commit the transaction. Upon transaction commit the Hibernate session is flushed/synchronized with the database. So the newly created Student intance residing in the Session is persisted to the database.

7. Run the Application

This is the output of the program:

log4j:WARN No appenders could be found for logger (org.hibernate.type.BasicTypeRegistry).
log4j:WARN Please initialize the log4j system properly.
Hibernate: insert into tutorials.student (STUDENT_NAME, STUDENT_Age) values (?, ?)
Hibernate: insert into tutorials.projects (TITLE, SEMESTER, STUDENT_ID) values (?, ?, ?)
Hibernate: insert into tutorials.projects (TITLE, SEMESTER, STUDENT_ID) values (?, ?, ?)
Great! Student was saved

This was an example on Hibernate One-to-Many Relationship using XML Mapping. Download the Eclipse project of this part : HibernateOneToManyExample.zip.

Mapping the Classes using Annotations

For this part we just have to do some updates to the previous project. The main difference is that we are not going to use Student.hbm.xml and Project.hbm.xml to map the classes to the corresponding tables in the database. We will use special annotations in the java classes that will dictate the mapping.

1. Delete Student.hbm.xml and Project.hbm.xml

We don’t need them any more.

2. Update the pom.xml file to include Hibernate Annotation library

Since Hibernate version 3.6, the annotation framework is included into the hibernate-core.jar module, so no update for us.

But if you face any problems updated the pom.xml file to include these libraries as well:

<dependency>
	<groupId>hibernate-annotations</groupId>
	<artifactId>hibernate-annotations</artifactId>
	<version>3.6.3.Final</version>
</dependency>

<dependency>
	<groupId>hibernate-commons-annotations</groupId>
	<artifactId>hibernate-commons-annotations</artifactId>
	<version>3.6.3.Final</version>
</dependency>

And then run mvn eclipse:eclipse to download the necessary jars and update the classpath of your project.

3. Update the classes to include Hibernate Annotations

This is how the annotated Student.java file should look like:

Student.java:

package com.javacodegeeks.enterprise.hibernate;

import java.util.HashSet;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import static javax.persistence.GenerationType.IDENTITY;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@Entity
@Table(name = "student", catalog = "tutorials")
public class Student implements java.io.Serializable {

	private static final long serialVersionUID = 1L;

	private Integer studentId;
	private String  studentName;
	private String  studentAge;
	private Set<Project> studentProjects = new HashSet<Project>(0);

	public Student() {
	}

	public Student(String studentName, String studentAge) {
		this.studentName = studentName;
		this.studentAge = studentAge;
	}

	@Id
	@GeneratedValue(strategy = IDENTITY)
	@Column(name = "STUDENT_ID", unique = true, nullable = false)
	public Integer getStudentId() {
		return studentId;
	}

	public void setStudentId(Integer studentId) {
		this.studentId = studentId;
	}

	@Column(name = "STUDENT_NAME", nullable = false, length = 10)
	public String getStudentName() {
		return studentName;
	}

	public void setStudentName(String studentName) {
		this.studentName = studentName;
	}

	@Column(name = "STUDENT_AGE", nullable = false, length = 20)
	public String getStudentAge() {
		return studentAge;
	}

	public void setStudentAge(String studentAge) {
		this.studentAge = studentAge;
	}

	@OneToMany(fetch = FetchType.LAZY, mappedBy = "student")
	public Set<Project> getStudentProjects() {
		return studentProjects;
	}

	public void setStudentProjects(Set<Project> studentProjects) {
		this.studentProjects = studentProjects;
	}

}

Project.java:

package com.javacodegeeks.enterprise.hibernate;

import static javax.persistence.GenerationType.IDENTITY;

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

@Entity
@Table(name = "projects", catalog = "tutorials", 
uniqueConstraints = @UniqueConstraint(columnNames = "TITLE"))
public class Project implements java.io.Serializable{

	private Integer projectId;

	private String title;
	private String semester;

	private Student student;

	public Project(String title, String semester){
		this.title = title;
		this.semester = semester;
	}

	@Id
	@GeneratedValue(strategy = IDENTITY)
	@Column(name = "PROJECT_ID", unique = true, nullable = false)
	public Integer getProjectId() {
		return projectId;
	}

	public void setProjectId(Integer projectId) {
		this.projectId = projectId;
	}

	@Column(name = "TITLE", nullable = false, length = 100, unique = true)
	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	@Column(name = "SEMESTER", nullable = false, length = 100)
	public String getSemester() {
		return semester;
	}

	public void setSemester(String semester) {
		this.semester = semester;
	}

	@ManyToOne(fetch = FetchType.LAZY)
	@JoinColumn(name = "STUDENT_ID", nullable = false)
	public Student getStudent() {
		return student;
	}

	public void setStudent(Student student) {
		this.student = student;
	}

	private static final long serialVersionUID = 1L;
}

These are the basic things you need to know about Hibernate annotations :

  • @Entity : used to mark the specific class as a Hibenrate entity class that will be mapped to a database table.
  • @Table : used to specify the database table that this class is mapped to. If @Table annotation is not specified, the class name will be considered as the table name.
  • @Id : used to specify the attribute that corresponds to the primary key of the database table.
  • @GeneratedValue : used to specify the primary key generation strategy and used for auto generated ids (e.g auto increment in this example).
  • @Column : used to specify the the column to which a field will be mapped. If it is not specified the attribute name and type will be considered as the column name and type respectively.
  • @OneToMany : used to define a one-to-many relationship with the Class of that specific attribute. The fetch = FetchType.LAZY attribute is a hint to the persistence provider runtime that data should be fetched when it is first accessed. If no fetch strategy is declared, then lazy is used by default.On the other hand, EAGER strategy is a requirement on the persistence provider runtime that data must be fecthed when asked and not only when accessed. When we have 2 linked classes, one of them has to be the relationship owner. The mappedBy = "student" attributes denotes that the Project class is responsible to handle the “connection” between these two classes (Student and Project) and is the owner of the relationship (as we described before).
  • @GenericGenerator : generator annotation describing any kind of Hibernate generator in a detyped manner. The strategy (in StudentInformation.java) has value foreign because the attribute is a foreign key to student table
  • @JoinColumn :  declaires the join column. The name parameter declares the column in the targeted entity that will be used to the join.

4. Update Hibernate configuration file:

Go to the package explorer and find hibernate.cfg.xml file and paste the following code:

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>
		<property name="hibernate.bytecode.use_reflection_optimizer">false</property>
		<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
		<property name="hibernate.connection.username">root</property>
		<property name="hibernate.connection.password"></property>
		<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/tutorials</property>
		<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
		<property name="show_sql">true</property>
		<mapping class="com.javacodegeeks.enterprise.hibernate.Student"></mapping>
		<mapping class="com.javacodegeeks.enterprise.hibernate/Project"></mapping>
	</session-factory>
</hibernate-configuration>

5. Run the application

You don’t have to change anything to App.java. Simply, run the program.

This is the output of the program:

log4j:WARN No appenders could be found for logger (org.hibernate.type.BasicTypeRegistry).
log4j:WARN Please initialize the log4j system properly.
Hibernate: insert into tutorials.student (STUDENT_NAME, STUDENT_Age) values (?, ?)
Hibernate: insert into tutorials.projects (TITLE, SEMESTER, STUDENT_ID) values (?, ?, ?)
Hibernate: insert into tutorials.projects (TITLE, SEMESTER, STUDENT_ID) values (?, ?, ?)
Great! Student was saved

This was an example on Hibernate One-to-Many Relationship using Annotations. Download the Eclipse project of this part : HibernateOneToManyAnnotations.zip.

Nikos Maravitsas

Nikos has graduated from the Department of Informatics and Telecommunications of The National and Kapodistrian University of Athens. During his studies he discovered his interests about software development and he has successfully completed numerous assignments in a variety of fields. Currently, his main interests are system’s security, parallel systems, artificial intelligence, operating systems, system programming, telecommunications, web applications, human – machine interaction and mobile development.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button