Hibernate Many-To-Many Bidirectional (Annotation)

In this tutorial, we will learn about how to use Hibernate Many-To-Many Bidirectional mapping using annotation based configuration.

In Many-To-Many association, an extra table is used (known as Joined table) whose primary key is the combination of primary key of both the associated tables.In other words there is a foreign key association between the joined table and the associated tables.

ManyToManyBiDirectional_img1
Schema Setup of ManyToMany bidirectional is same as of ManyToMany Unidirectional. We are discussing an example of Student and Subject relationship. A student can enroll for multiple subject. And a subject can have multiple students enrolled for it. We are considering Bidirectional mapping, means both side navigation is possible. Let’s get going.


Following technologies being used:

  • Hibernate 4.3.6.Final
  • MySQL Server 5.6
  • Maven 3.1.1
  • JDK 1.6
  • Eclipse JUNO Service Release 2

Let’s begin.

Step 1: Create required Database Tables

Open MySQL terminal / workbench terminal and execute following MySQL script :

create table STUDENT (
   student_id BIGINT NOT NULL AUTO_INCREMENT,
   first_name VARCHAR(30) NOT NULL,
   last_name  VARCHAR(30) NOT NULL,
   PRIMARY KEY (student_id)
);


create table SUBJECT (
   subject_id BIGINT NOT NULL AUTO_INCREMENT,
   name VARCHAR(30) NOT NULL,
   PRIMARY KEY (subject_id)
);


CREATE TABLE STUDENT_SUBJECT (
    student_id BIGINT NOT NULL,
    subject_id BIGINT NOT NULL,
    PRIMARY KEY (student_id, subject_id),
    CONSTRAINT FK_STUDENT FOREIGN KEY (student_id) REFERENCES STUDENT (student_id),
    CONSTRAINT FK_SUBJECT FOREIGN KEY (subject_id) REFERENCES SUBJECT (subject_id)
);

[Note that this schema is exactly same as the one for ManyToManyUniDirectional. NO CHANGE].
Here we have first created the main tables STUDENT & SUBJECT. then we have created a joined table STUDENT_SUBJECT whose primary key is the combination of primary keys of STUDENT & SUBJECT.
Please visit MySQL installation on Local PC in case you are finding difficulties in setting up MySQL locally.

Step 2: Create project directory structure

Following will be the final project structure:

ManyToManyBiDirectional_img2

Step 3: Update pom.xml to include required Hibernate and MySQL dependency

Following is the updated minimalistic 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/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.websystique.hibernate</groupId>
	<artifactId>ManyToManyBiDirectional</artifactId>
	<version>1.0.0</version>
	<packaging>jar</packaging>

	<name>ManyToManyBiDirectional</name>

	<properties>
		<hibernate.version>4.3.6.Final</hibernate.version>
		<mysql.connector.version>5.1.31</mysql.connector.version>
	</properties>

	<dependencies>
		<!-- Hibernate -->
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-core</artifactId>
			<version>${hibernate.version}</version>
		</dependency>

		<!-- MySQL -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>${mysql.connector.version}</version>
		</dependency>
	</dependencies>
	<build>
		<pluginManagement>
			<plugins>
				<plugin>
					<groupId>org.apache.maven.plugins</groupId>
					<artifactId>maven-compiler-plugin</artifactId>
					<version>3.2</version>
					<configuration>
						<source>1.6</source>
						<target>1.6</target>
					</configuration>
				</plugin>
			</plugins>
		</pluginManagement>
	</build>

</project>

On saving pom.xml with above content, Eclipse will download all the dependencies.

Step 4: Create Model classes

Model class Student & Subject are simple POJO class which is annotated with JPA annotations to map it to a database tables(created in step 1).

package com.websystique.hibernate.model;

import java.util.ArrayList;
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.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;

@Entity
@Table(name = "STUDENT")
public class Student {

	@Id
	@GeneratedValue
	@Column(name = "STUDENT_ID")
	private long id;

	@Column(name = "FIRST_NAME")
	private String firstName;

	@Column(name = "LAST_NAME")
	private String lastName;

	@ManyToMany(cascade = CascadeType.ALL)
	@JoinTable(name = "STUDENT_SUBJECT", 
		joinColumns = { @JoinColumn(name = "STUDENT_ID") }, 
		inverseJoinColumns = { @JoinColumn(name = "SUBJECT_ID") })
	private List<Subject> subjects = new ArrayList<Subject>();

	public Student() {
	}

	public Student(String firstName, String lastName) {
		this.firstName = firstName;
		this.lastName = lastName;
	}

	public long getId() {
		return id;
	}

	public void setId(long id) {
		this.id = id;
	}

	public String getFirstName() {
		return firstName;
	}

	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}

	public String getLastName() {
		return lastName;
	}

	public void setLastName(String lastName) {
		this.lastName = lastName;
	}

	public List<Subject> getSubjects() {
		return subjects;
	}

	public void setSubjects(List<Subject> subjects) {
		this.subjects = subjects;
	}

	
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + (int) (id ^ (id >>> 32));
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (!(obj instanceof Student))
			return false;
		Student other = (Student) obj;
		if (id != other.id)
			return false;
		return true;
	}

	@Override
	public String toString() {
		return "Student [id=" + id + ", firstName=" + firstName + ", lastName="
				+ lastName + "]";
	}

}

package com.websystique.hibernate.model;

import java.util.ArrayList;
import java.util.List;

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

@Entity
@Table(name = "SUBJECT")
public class Subject {

	@Id
	@GeneratedValue
	@Column(name = "SUBJECT_ID")
	private long id;

	@Column(name = "NAME")
	private String name;
	
	
	@ManyToMany(mappedBy="subjects")
	private List<Student> students = new ArrayList<Student>();
	
	public Subject(){
		
	}
	
	public Subject(String name){
		this.name = name;
	}

	public long getId() {
		return id;
	}

	public void setId(long id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}


	public List<Student> getStudents() {
		return students;
	}

	public void setStudents(List<Student> students) {
		this.students = students;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + (int) (id ^ (id >>> 32));
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}

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

	@Override
	public String toString() {
		return "Subject [id=" + id + ", name=" + name + "]";
	}

}

Only change in this relationship( ManyToMany Bidirectional) and ManyToMany Unidirectional is that, in the Subject class we have added following property.

	@ManyToMany(mappedBy="subjects")
	private List<Student> students = new ArrayList<Student>();

Nothing else changes.We added this property in Subject class to make the relationship bidirectional.You can now navigate from Subject to Student.mappedBy attribute tells that this is the inverse side of relationship which is managed by “subjects” property of Student annotated with @JoinColumn.

If you prefer to recall (as in Unidirectional) , owner side of relationship is defined in Student class as follows

	@ManyToMany(cascade = CascadeType.ALL)
	@JoinTable(name = "STUDENT_SUBJECT", 
		joinColumns = { @JoinColumn(name = "STUDENT_ID") }, 
		inverseJoinColumns = { @JoinColumn(name = "SUBJECT_ID") })
	private List<Subject> subjects = new ArrayList<Subject>();

@ManyToMany indicates that there is a Many-to-Many relationship between Student and subject. A Student can enroll for multiple subjects, and a subject can have multiple students enrolled.Notice cascade = CascadeType.ALL, with cascading while persisting (update/delete) Student tuples, subjects tuples will also be persisted (updated/deleted).

@JoinTable indicates that there is a link table which joins two tables via containing there keys.This annotation is mainly used on the owning side of the relationship.joinColumns refers to the column name of owning side(STUDENT_ID of STUDENT), and inverseJoinColumns refers to the column of inverse side of relationship(SUBJECT_ID of SUBJECT).Primary key of this joined table is combination of STUDENT_ID & SUBJECT_ID.

One important remark : In case of *Many* association, always override hashcode and equals method which are looked by hibernate when holding entities into collections.

Step 5: Create Hibernate configuration file

We need to inform hibernate about how to connect to database, which database dialect we will be using so that hibernate can generate the instruction specific to that database.

We define all these information in hibernate.cfg.xml. Create this file with below content and save it in src/main/resources folder.

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration SYSTEM "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">


<hibernate-configuration>
    <session-factory>
        <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="hibernate.connection.username">myuser</property>
        <property name="hibernate.connection.password">mypassword</property>
        <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/websystique</property>
        <property name="show_sql">true</property>
        <property name="format_sql">false</property>
        <mapping class="com.websystique.hibernate.model.Student"/>
        <mapping class="com.websystique.hibernate.model.Subject"/>
    </session-factory>
</hibernate-configuration>

Step 6: Create Hibernate Utility class

This class is well-known in hibernate community, and used for configuring hibernate on startup and managing session factory.

package com.websystique.hibernate;

import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;

public class HibernateUtil {
	
	private static final SessionFactory sessionFactory;
	
	static{
		try{
			sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory();

		}catch (Throwable ex) {
			System.err.println("Session Factory could not be created." + ex);
			throw new ExceptionInInitializerError(ex);
		}	
	}
	
	public static SessionFactory getSessionFactory() {
		return sessionFactory;
	}
	
}

Step 7: Create executable class to Run and perform operations on Database

package com.websystique.hibernate;

import org.hibernate.Session;

import com.websystique.hibernate.model.Student;
import com.websystique.hibernate.model.Subject;


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

		Student student1 = new Student("Sam","Disilva");
		Student student2 = new Student("Joshua", "Brill");
		
		Subject subject1 = new Subject("Economics");
		Subject subject2 = new Subject("Politics");
		Subject subject3 = new Subject("Foreign Affairs");

		//Student1 have 3 subjects
		student1.getSubjects().add(subject1);
		student1.getSubjects().add(subject2);
		student1.getSubjects().add(subject3);
		
		//Student2 have 2 subjects
		student2.getSubjects().add(subject1);
		student2.getSubjects().add(subject2);

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

		session.persist(student1);
		session.persist(student2);

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

}

Here you can see that we have set the subjects property of Student class, and just persisted Student objects.Thanks to Cascade attribute, Subject tubles will be persisted automatically. And due to the annotation mapping we have done above in student class, a new tuple will be created in joined table (STUDENT_SUBJECT) for each combination of student & subject item we dealt with in main program.

Execute above class as Java application. You will see following output

Hibernate: insert into STUDENT (FIRST_NAME, LAST_NAME) values (?, ?)
Hibernate: insert into SUBJECT (NAME) values (?)
Hibernate: insert into SUBJECT (NAME) values (?)
Hibernate: insert into SUBJECT (NAME) values (?)
Hibernate: insert into STUDENT (FIRST_NAME, LAST_NAME) values (?, ?)
Hibernate: insert into STUDENT_SUBJECT (STUDENT_ID, SUBJECT_ID) values (?, ?)
Hibernate: insert into STUDENT_SUBJECT (STUDENT_ID, SUBJECT_ID) values (?, ?)
Hibernate: insert into STUDENT_SUBJECT (STUDENT_ID, SUBJECT_ID) values (?, ?)
Hibernate: insert into STUDENT_SUBJECT (STUDENT_ID, SUBJECT_ID) values (?, ?)
Hibernate: insert into STUDENT_SUBJECT (STUDENT_ID, SUBJECT_ID) values (?, ?)

Below is the snapshot of MySQL database after execution of above program.

ManyToManyBiDirectional_img3

ManyToManyBiDirectional_img4

ManyToManyBiDirectional_img5

That’s it.

Download Source Code


References