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:
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).
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:
Right Click -> 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. Thefetch = 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. ThemappedBy = "student"
attributes denotes that theProject
class is responsible to handle the “connection” between these two classes (Student
andProject
) 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 (inStudentInformation.java
) has valueforeign
because the attribute is a foreign key tostudent
table@JoinColumn
: declaires the join column. Thename
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.