hibernate

Hibernate Many-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 Many-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) and Hibernate One-to-Many Relationship Example (XML Mapping and Annotation)

Make sure the project structure looks like this:

project-structure

2. Many-to-Many relationships

Imagine that you have tables student and class in a database. You want every student to be able to work on multiple class, and each class can be followed by many students. So, student has many-to-many relationship with class. In relational databases the above relationship is imlemented using a third table that somehow connects the aforementioned two tables that participate in the ralationship.

Let’s see the MySQL scripts that creates these tables.

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

CREATE TABLE `class` (
  `CLASS_ID` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
  `TITLE` VARCHAR(10) NOT NULL,
  `SEMESTER` VARCHAR(255) NOT NULL,
  UNIQUE KEY `UNI_TITLE` (`TITLE`),
  PRIMARY KEY (`CLASS_ID`)
)

CREATE TABLE  `student_class` (
  `ID_STUDENT` INT(10) UNSIGNED NOT NULL,
  `CLASS_ID` INT(10) UNSIGNED NOT NULL,
  PRIMARY KEY (`ID_STUDENT`,`CLASS_ID`),
  CONSTRAINT `FK_CLASS_ID` FOREIGN KEY (`CLASS_ID`) REFERENCES `class` (`CLASS_ID`),
  CONSTRAINT `FK_ID_STUDENT` FOREIGN KEY (`ID_STUDENT`) REFERENCES `student` (`STUDENT_ID`)
) ENGINE=InnoDB 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

And this is the complete database scheema that we’ve created during these tutorials:

complete-database-scheema

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 <Class> studentClasses = new HashSet<Class>(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<Class> getStudentClasses() {
		return studentClasses;
	}

	public void setStudentClasses(Set<Class> studentClasses) {
		this.studentClasses = studentClasses;
	}	
}

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

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

Class.java:

package com.javacodegeeks.enterprise.hibernate;

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

public class Class implements java.io.Serializable{

	private Integer classID;

	private String title;
	private String semester;

	private Set<Student> assignedStudents = new HashSet<Student>(0);

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

	public Integer getClassID() {
		return classID;
	}

	public void setClassID(Integer classID) {
		this.classID = classID;
	}

	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 Set<Student> getAssignedStudents() {
		return assignedStudents;
	}

	public void setAssignedStudents(Set<Student> assignedStudents) {
		this.assignedStudents = assignedStudents;
	}

	private static final long serialVersionUID = 1L;

}

The above class will be mapped, of course to “class” table. Note that Class class holds a  HashSet of  Student instances. This means that many student can work on one classe. All in all, the above classes state that many students can work on many classes.

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="studentClasses" table="student_class"  inverse="false" lazy="true" fetch="select" cascade="all">

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

             <many-to-many entity-name="com.javacodegeeks.enterprise.hibernate.Class">
                <column name="CLASS_ID" not-null="true" />
            </many-to-many>

        </set>

    </class>
</hibernate-mapping>

Here we describe that the  Student  class has a many-to-many relationship with Class. We also use inverse  = false attribute . inverse attribute is always present in one-to-many and many-to-many relationship and it denotes which side is responsible for the relationship. It’s default value is false. In our case Student class is the relationship owner. That means that the Student class is responsible to handle the “connection” between these two classes (Student and Class). Thus, if you want to add a new “couple” to the relationship, you have to add a Student instance to the Classe‘s assignedStudents set. And again, if you want to delete a ”couple” of the relationship you have to null (or simply remove) the student isntance from the assignedStudents set. 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.

Class.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.Class" table="class" 	catalog="tutorials">

		<id name="classID" type="java.lang.Integer">
			<column name="CLASS_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>

		  <set name="assignedStudents" table="student_class" inverse="true" lazy="true" fetch="select">
            <key>
                <column name="CLASS_ID" not-null="true" />
            </key>
            <many-to-many entity-name="com.javacodegeeks.enterprise.hibernate.Student">
                <column name="ID_STUDENT" not-null="true" />
            </many-to-many>
        </set>

	</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>
		<property name="format_sql">true</property>
		<mapping resource="com/javacodegeeks/enterprise/hibernate/Student.hbm.xml"></mapping>
		<mapping resource="com/javacodegeeks/enterprise/hibernate/Class.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 student1 = new Student("Jeremny","21");
        Student student2 = new Student("Robert","21");

        Class class1 = new Class("Security","Spring");
        Class class2 = new Class("Databases","Winter");
        Class class3 = new Class("Hibernate","Winter");

        student1.getStudentClasses().add(class1);
        student1.getStudentClasses().add(class2);

        student2.getStudentClasses().add(class1);
        student2.getStudentClasses().add(class2);
        student2.getStudentClasses().add(class3);

        class1.getAssignedStudents().add(student1);
        class1.getAssignedStudents().add(student2);

        class2.getAssignedStudents().add(student1);
        class2.getAssignedStudents().add(student2);

        class3.getAssignedStudents().add(student2);

        session.save(student1);
        session.save(student2);

        session.getTransaction().commit();
        System.out.println("Great! Students were 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 create some Student and Class instances. We add to set of the Class instance, the students that follow that class, and at each student we add the classes that he follows. Then we simply have save the student isntances to the Hibernate Session and commit. 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.class
        (TITLE, SEMESTER) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        tutorials.class
        (TITLE, SEMESTER) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        tutorials.student
        (STUDENT_NAME, STUDENT_Age) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        tutorials.class
        (TITLE, SEMESTER) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        student_class
        (ID_STUDENT, CLASS_ID) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        student_class
        (ID_STUDENT, CLASS_ID) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        student_class
        (ID_STUDENT, CLASS_ID) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        student_class
        (ID_STUDENT, CLASS_ID) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        student_class
        (ID_STUDENT, CLASS_ID) 
    values
        (?, ?)
Great! Students were saved

This was an example on Hibernate Many-to-Many Relationship using XML Mapping. Download the Eclipse project of this part : HibernateManyToMany.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 Class.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 Class.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.

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 static javax.persistence.GenerationType.IDENTITY;

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

import javax.persistence.CascadeType;
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.JoinTable;
import javax.persistence.ManyToMany;
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<Class> studentClasses = new HashSet<Class>(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;
	}

	@ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
	@JoinTable(name = "student_class", catalog = "tutorials", joinColumns = { 
		@JoinColumn(name = "ID_STUDENT", nullable = false, updatable = false) }, 
			inverseJoinColumns = { @JoinColumn(name = "CLASS_ID", 
	   	    nullable = false, updatable = false) })
	public Set<Class> getStudentClasses() {
		return studentClasses;
	}

	public void setStudentClasses(Set<Class> studentClasses) {
		this.studentClasses = studentClasses;
	}

}

Class.java:

package com.javacodegeeks.enterprise.hibernate;

import static javax.persistence.GenerationType.IDENTITY;

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 javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.Table;

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

	private Integer classID;

	private String title;
	private String semester;

	private Set<Student> assignedStudents = new HashSet<Student>(0);

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

	@Id
	@GeneratedValue(strategy = IDENTITY)
	@Column(name = "CLASS_ID", unique = true, nullable = false)
	public Integer getClassID() {
		return classID;
	}

	public void setClassID(Integer classID) {
		this.classID = classID;
	}

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

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

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

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

	@ManyToMany(fetch = FetchType.LAZY, mappedBy = "studentClasses")
	public Set<Student> getAssignedStudents() {
		return assignedStudents;
	}

	public void setAssignedStudents(Set<Student> assignedStudents) {
		this.assignedStudents = assignedStudents;
	}

	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 databse table.
  • @GeneratedValue : used to specify the primary key generation strategy and used for auto genarated 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.
  • @GenericGenerator : generator annotation describing any kind of Hibernate generator in a detyped manner. The strategy (inStudentInformation.java) has value foreign because the attribute is a foreign key to student table
  • @ManyToMany : used to define a many-to-many relationship with the Class and Studen classes. 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 associated classes, one of them has to be the relationship owner. The mappedBy = "studentClasses" attribute denotes that the Student class is responsible to handle the “connection” between these two classes (Student and Class) 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 has value foreign because the attribute is a foreign key to student table
  • @JoinColumn :  declarers the join column. The name parameter declares the column in the targeted entity that will be used to the join.
  • @JoinTable : in many-to-many relationships it declares the join table, that holds the many-to-many relationship associations. This table holds the foreign keys to the associated tables. The joinColumns attribute is used to desrcibe the join columns of the owener of the relationship (Student). The inverseJoinColumns attribute is used to describe the join columns of the target entity (Class).

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>
		<property name="format_sql">true</property>
		<mapping class="com.javacodegeeks.enterprise.hibernate.Student"></mapping>
		<mapping class="com.javacodegeeks.enterprise.hibernate.Class"></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.class
        (TITLE, SEMESTER) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        tutorials.class
        (TITLE, SEMESTER) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        tutorials.student
        (STUDENT_NAME, STUDENT_Age) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        tutorials.class
        (TITLE, SEMESTER) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        student_class
        (ID_STUDENT, CLASS_ID) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        student_class
        (ID_STUDENT, CLASS_ID) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        student_class
        (ID_STUDENT, CLASS_ID) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        student_class
        (ID_STUDENT, CLASS_ID) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        student_class
        (ID_STUDENT, CLASS_ID) 
    values
        (?, ?)
Great! Students were saved

This was an example on Hibernate Many-to-Many Relationship using Annotations. Download the Eclipse project of this part : HibernateManyToManyAnnot.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