hibernate

Hibernate Many-to-Many Relationship with Join Table Example

In the previous tutorial, Hibernate Many-to-Many Relationship Example (XML Mapping and Annotation), we saw how use Hibernate in order to work with classes that have many-to-many relationships. I would strongly recommend to read that tutorial before proceeding to this one. For this tutorial we are going to see how to map the classes 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

Remember the database schema from the previous tutorial:

diagram

In this, we have two tables, student and class, associated with many-to-many relationship. We explained that in relational databases the above relationship is implemented using a third table, the join table, that “connects” the aforementioned two tables that participate in the relationship. As you can see, in the join table (student_class)  every tuple consists of a ID_STUDENT (foreign key to student) and CLASS_ID (foreign key to class). This is essential in order to express the many-to-many relationship. But this relationship might have additional characteristics and properties. For example you might want to hold the date the user registered to the class and the grade of the student to that class. So we have to add additional columns to the join table.

This is the script to create the 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;

DROP TABLE IF EXISTS `class`;
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`)
)

DROP TABLE IF EXISTS `student_class`;
CREATE TABLE  `student_class` (
  `ID_STUDENT` INT(10) UNSIGNED NOT NULL,
  `CLASS_ID` INT(10) UNSIGNED NOT NULL,
  `REGISTERED`  DATE 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;

So this is the new diagram:

diagram

For the new schema we need to create more java classes. One class that holds the new columns of the student_class (StudentClass.java) table, and the class that contains the join columns of the join table (StudentClassID.java).

1. Project Structure from previous tutorial

Go to , Hibernate Many-to-Many Relationship Example (XML Mapping and Annotation) and download the code of the Annotation tutorial. Make sure that the project structure is as follows:

project-structure

2. Create the new Java Classes

In the Package Explorer go to src/main/java folder and to com.javacodegeeks.enterprise.hibernate package. Right Click -> New -> Class :

create-new-class

This will create the new StudentClass.java file. Go ahead and create StudentClassID.java file.

Here is the code of the classes:

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.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<StudentClass> studentClasses = new HashSet<StudentClass>(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 = "pk.student", cascade=CascadeType.ALL)
	public Set<StudentClass> getStudentClasses() {
		return studentClasses;
	}

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

In the above code notice that we have a Set of StudentClass instances. StudentClass will represent the join table student_class. We also state that Student class has a one-to-many relationship with StudentClass. This directly associates with the fact that student table has one-to-many relationship with the join table student_class.

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.OneToMany;
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 studentClasses = new HashSet(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;
	}

	@OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.cl")
	public Set getStudentClasses() {
		return studentClasses;
	}

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

	private static final long serialVersionUID = 1L;

}

You will notice the same pattern here. Class has one-to-many relationships with StudentClass, as class table has one-to-many relationship with the join table student_class.

Now, let’s get on to the interesting bit.

StudentClass.java:

package com.javacodegeeks.enterprise.hibernate;

import java.util.Date;

import javax.persistence.AssociationOverride;
import javax.persistence.AssociationOverrides;
import javax.persistence.Column;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;

@Entity
@Table(name = "student_class", catalog = "tutorials")
@AssociationOverrides({
		@AssociationOverride(name = "pk.student", 
			joinColumns = @JoinColumn(name = "ID_STUDENT")),
		@AssociationOverride(name = "pk.cl", 
			joinColumns = @JoinColumn(name = "CLASS_ID")) })
public class StudentClass implements java.io.Serializable {

	private static final long serialVersionUID = 4050660680047579957L;

	private StudentClassID pk = new StudentClassID();
	private Date registered;

	@EmbeddedId
	public StudentClassID getPk() {
		return pk;
	}

	@Transient
	public Student getStudent() {
		return getPk().getStudent();
	}

	public void setStudent(Student student) {
		getPk().setStudent(student);
	}

	@Transient
	public Class getCl() {
		return getPk().getCl();
	}

	public void setCl(Class c) {
		getPk().setCl(c);
	}

	public void setPk(StudentClassID pk) {
		this.pk = pk;
	}

	@Temporal(TemporalType.DATE)
	@Column(name = "REGISTERED", nullable = false, length = 10)
	public Date getRegistered() {
		return registered;
	}

	public void setRegistered(Date registered) {
		this.registered = registered;
	}

	@Override
	public int hashCode() {
		return (getPk() != null ? getPk().hashCode() : 0);
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (!(obj instanceof StudentClass))
			return false;
		StudentClass other = (StudentClass) obj;
		if (pk == null) {
			if (other.pk != null)
				return false;
		} else if (!pk.equals(other.pk))
			return false;
		if (registered == null) {
			if (other.registered != null)
				return false;
		} else if (!registered.equals(other.registered))
			return false;
		return true;
	}
}

StudentClassID.java:

package com.javacodegeeks.enterprise.hibernate;

import javax.persistence.Embeddable;
import javax.persistence.ManyToOne;

@Embeddable
public class StudentClassID implements java.io.Serializable {

	private static final long serialVersionUID = -9120607274421816301L;
	private Student student;
	private Class cl;

	@ManyToOne
	public Student getStudent() {
		return student;
	}

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

	@ManyToOne
	public Class getCl() {
		return cl;
	}

	public void setCl(Class cl) {
		this.cl = cl;
	}

	@Override
	public int hashCode() {
		 int result;
	        result = (student != null ? student.hashCode() : 0);
	        result = 17 * result + (cl != null ? cl.hashCode() : 0);
	        return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (!(obj instanceof StudentClassID))
			return false;
		StudentClassID other = (StudentClassID) obj;
		if (cl == null) {
			if (other.cl != null)
				return false;
		} else if (!cl.equals(other.cl))
			return false;
		if (student == null) {
			if (other.student != null)
				return false;
		} else if (!student.equals(other.student))
			return false;
		return true;
	}
}

StudentClassID is what we call a composite primary key. This composite class is the primary key of StudentClass. To make a composite primary key class you have to declare it as embeddable using @Embeddable annotation. Now, in StudentClass you should have a StudentClassID instance that will represent the primary key of the class. Because StudentClassID is an embeddable class and the primary of StudentClass we use @EmbeddedId annotation in StudentClass.

The next thing to notice is the @AssociationOverrides annotation in StudentClass. We use that annotation when using an embedded composite primary key. It overrides a relationship mapping when the embeddable class is on the owning side of the relationship. When used to override a relationship mapping defined by an embeddable class (including an embeddable class embedded within another embeddable class), @AssociationOverride is applied to the field or property containing the embeddable.

When @AssociationOverride is used to override a relationship mapping from an embeddable class, the name element specifies the referencing relationship field or property within the embeddable class. To override mappings at multiple levels of embedding, a dot (“.”) notation syntax must be used in the name element to indicate an attribute within an embedded attribute. The value of each identifier used with the dot notation is the name of the respective embedded field or property. @JoinColumn declarers the join column. The name parameter declares the column in the targeted entity that will be used to the join.

3. App.java code

App.java:

package com.javacodegeeks.enterprise.hibernate;

import java.util.Date;

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");

        Class class1 = new Class("Math","Spring");
        session.save(class1);      

        StudentClass studentClass = new StudentClass();

        studentClass.setCl(class1);
        studentClass.setStudent(student1);

        Date registered = new Date();
        studentClass.setRegistered(registered); // The new date column

        student1.getStudentClasses().add(studentClass);

        session.save(student1);      

        session.getTransaction().commit();
        System.out.println("Great! Students were saved");

    }
}

In the above code we create a Student and a Class. We save the Class instance to the session. Then we create a new StudentClass and register student1 and class1 to it in order to create a new couple of the relationship. We also add studentClass to the StudentClass set of the Student class. We then save student1 one to the session and commit.

5. Hibernate configuration

<?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>
		<mapping class="com.javacodegeeks.enterprise.hibernate.StudentClass"></mapping>
		<mapping class="com.javacodegeeks.enterprise.hibernate.StudentClassID"></mapping>
	</session-factory>
</hibernate-configuration>

6. Output

Make sure the project structure is as follows:

new-project-structure

This is the output of the programm:

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.class
        (SEMESTER, TITLE) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        tutorials.student
        (STUDENT_AGE, STUDENT_NAME) 
    values
        (?, ?)
Hibernate: 
    select
        studentcla_.CLASS_ID,
        studentcla_.ID_STUDENT,
        studentcla_.REGISTERED as REGISTERED2_ 
    from
        tutorials.student_class studentcla_ 
    where
        studentcla_.CLASS_ID=? 
        and studentcla_.ID_STUDENT=?
Hibernate: 
    insert 
    into
        tutorials.student_class
        (REGISTERED, CLASS_ID, ID_STUDENT) 
    values
        (?, ?, ?)
Great! Students were saved

This was an example on Hibernate Many-to-Many Relationship with Join Table. Download the eclipse project of this tutorial:HibernateManyToManyJoinTable.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.

1 Comment
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Tapash Dutta
Tapash Dutta
6 years ago

How to write an HQL query on that Student_Class table to get number of student registered in a particular month.Please help.

Back to top button