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.
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.
- Spring Boot+AngularJS+Spring Data+Hibernate+MySQL CRUD App
- Spring Boot REST API Tutorial
- Spring Boot WAR deployment example
- Spring Boot Introduction + Hello World Example
- Secure Spring REST API using OAuth2
- Spring 4 MVC+JPA2+Hibernate Many-to-many Example
- AngularJS+Spring Security using Basic Authentication
- Secure Spring REST API using Basic Authentication
- Spring 4 Caching Annotations Tutorial
- Spring 4 Cache Tutorial with EhCache
- Spring 4 Email Template Library Example
- Spring 4 Email With Attachment Tutorial
- Spring 4 Email Integration Tutorial
- Spring MVC 4+JMS+ActiveMQ Integration Example
- Spring 4+JMS+ActiveMQ @JmsLister @EnableJms Example
- Spring 4+JMS+ActiveMQ Integration Example
- Spring MVC 4+AngularJS Example
- Hibernate One-To-One Unidirectional with Shared Primary Key (Annotation)
- Hibernate One To One Annotation Unidirectional with Foreign Key Associations
- Hibernate One-To-One Bidirectional with Shared Primary Key (Annotation)
- Hibernate Many To One Annotation Unidirectional
- Hibernate Many To One Annotation Bidirectional
- Hibernate Many To Many Annotation Unidirectional
- Spring MVC 4+Hibernate 4+MySQL+Maven integration example
- Spring 4 + Hibernate 4 + MySQL+ Maven Integration example (Annotations+XML)
- Spring Security 4 Hello World Annotation+XML Example
- Spring Batch- MultiResourceItemReader & HibernateItemWriter example
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:
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.
That’s it.
Download Source Code
References
If you like tutorials on this site, why not take a step further and connect me on Facebook , Google Plus & Twitter as well? I would love to hear your thoughts on these articles, it will help improve further our learning process.