Welcome to the Hibernate Tutorial Series. In previous tutorial we saw how to implement One to Many Annotation mapping as well as XML mapping. In this tutorial we will understand How to implement Bi-directional Many-to-Many relationship in Hibernate using XML mappings.
First let us see the formal definition of Many-to-Many relationship:
Many-To-Many Relationship:
A logical data relationship in which the value of one data element can exist in combination with many values of another data element, and vice versa.
Let us see how to implement Many-to-Many relationship in Hibernate using XML mapping.
1. Create Database
For this example, we will MySQL database. We are using Employee-Meeting relationship as a many to many relationship example. Each Employee can attain more than one meetings and each meetings can have more than one employee.
CREATE TABLE `employee` (
`employee_id` BIGINT(10) NOT NULL AUTO_INCREMENT,
`firstname` VARCHAR(50) NULL DEFAULT NULL,
`lastname` VARCHAR(50) NULL DEFAULT NULL,
PRIMARY KEY (`employee_id`)
)
CREATE TABLE `meeting` (
`meeting_id` BIGINT(20) NOT NULL AUTO_INCREMENT,
`subject` VARCHAR(50) NOT NULL,
`meeting_date` DATE NOT NULL,
PRIMARY KEY (`meeting_id`)
)
CREATE TABLE `employee_meeting` (
`employee_id` BIGINT(20) NOT NULL,
`meeting_id` BIGINT(20) NOT NULL,
PRIMARY KEY (`employee_id`, `meeting_id`),
INDEX `FK_MEETING` (`meeting_id`),
CONSTRAINT `FK_EMPLOYEE` FOREIGN KEY (`employee_id`) REFERENCES `employee` (`employee_id`),
CONSTRAINT `FK_MEETING` FOREIGN KEY (`meeting_id`) REFERENCES `meeting` (`meeting_id`)
)
Code language: SQL (Structured Query Language) (sql)
2. Hibernate Maven Dependency
We are using Maven for dependency management. Copy following in the pom.xml.
File: 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>net.viralpatel.hibernate</groupId>
<artifactId>HibernateHelloWorldXML</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>HibernateHelloWorldXML</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.10</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate</artifactId>
<version>3.2.6.ga</version>
</dependency>
</dependencies>
</project>
Code language: HTML, XML (xml)
3. Hibernate Model Class
Employee and Department model classes are created which maps to the corresponding database tables.
File: Employee.java
package net.viralpatel.hibernate;
import java.util.HashSet;
import java.util.Set;
public class Employee {
private Long employeeId;
private String firstname;
private String lastname;
private Set<Meeting> meetings = new HashSet<Meeting>();
public Employee() {
}
public Employee(String firstname, String lastname) {
this.firstname = firstname;
this.lastname = lastname;
}
// Getter and Setter methods
}
Code language: Java (java)
File: Meeting.java
package net.viralpatel.hibernate;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
public class Meeting {
private Long meetingId;
private String subject;
private Date meetingDate;
private Set<Employee> employees = new HashSet<Employee>();
public Meeting(String subject) {
this.subject = subject;
this.meetingDate = new Date();
}
// Getter and Setter methods
}
Code language: Java (java)
4. Hibernate Utility Class
To access Hibernate API, we will create a wrapper utility class which provides us with SessionFactory.
File: HibernateUtil.java
package net.viralpatel.hibernate;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class HibernateUtil {
private static final SessionFactory sessionFactory = buildSessionFactory();
private static SessionFactory buildSessionFactory() {
try {
// Create the SessionFactory from hibernate.cfg.xml
return new Configuration().configure().buildSessionFactory();
} catch (Throwable ex) {
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
}
Code language: Java (java)
5. Hibernate Mapping XML (hbm)
Following are the hibernate mapping files for each enitity Employee and Department.
File: Employee.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="net.viralpatel.hibernate">
<class name="Employee" table="EMPLOYEE">
<id name="employeeId" column="EMPLOYEE_ID">
<generator class="native" />
</id>
<property name="firstname" />
<property name="lastname" column="lastname" />
<set name="meetings" table="EMPLOYEE_MEETING"
inverse="false" lazy="true" fetch="select" cascade="all">
<key column="EMPLOYEE_ID" />
<many-to-many column="MEETING_ID" class="Meeting" />
</set>
</class>
</hibernate-mapping>
Code language: HTML, XML (xml)
File: Meeting.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="net.viralpatel.hibernate">
<class name="Meeting" table="MEETING">
<id name="meetingId" type="java.lang.Long"
column="MEETING_ID">
<generator class="native" />
</id>
<property name="subject" column="SUBJECT" />
<property name="meetingDate" type="date" column="MEETING_DATE" />
<set name="employees" table="EMPLOYEE_MEETING"
inverse="true" lazy="true" fetch="select">
<key column="EMPLOYEE_ID" />
<many-to-many column="MEETING_ID" class="Meeting" />
</set>
</class>
</hibernate-mapping>
Code language: HTML, XML (xml)
One thing is worth noting here is that we have mentioned keyword inverse=”true” in Meeting
class which makes Employee
as relationship owner. Thus Employee model takes care of updating referential keys in dependent models.
6. Hibernate Configuration File
Add following hibernate.cfg.xml file in your project.
File: 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="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql://localhost:3306/tutorial</property>
<property name="connection.username">root</property>
<property name="connection.password"></property>
<property name="connection.pool_size">1</property>
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="current_session_context_class">thread</property>
<property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>
<property name="show_sql">true</property>
<property name="hbm2ddl.auto">validate</property>
<mapping resource="net/viralpatel/hibernate/Employee.hbm.xml"/>
<mapping resource="net/viralpatel/hibernate/Meeting.hbm.xml"/>
</session-factory>
</hibernate-configuration>
Code language: HTML, XML (xml)
7. Review Project Structure
Note that we have used SET to map meetings with employee and vice versa. A <set> is similar to except that it can only store unique objects. That means no duplicate elements can be contained in a set. When you add the same element to a set for second time, it will replace the old one. A set is unordered by default but we can ask it to be sorted. The corresponding type of a <set> in Java is java.util.Set
.
Execute example
File: Main.java
package net.viralpatel.hibernate;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
public class Main {
public static void main(String[] args) {
SessionFactory sf = HibernateUtil.getSessionFactory();
Session session = sf.openSession();
session.beginTransaction();
Meeting meeting1 = new Meeting("Quaterly Sales meeting");
Meeting meeting2 = new Meeting("Weekly Status meeting");
Employee employee1 = new Employee("Sergey", "Brin");
Employee employee2 = new Employee("Larry", "Page");
employee1.getMeetings().add(meeting1);
employee1.getMeetings().add(meeting2);
employee2.getMeetings().add(meeting1);
session.save(employee1);
session.save(employee2);
session.getTransaction().commit();
session.close();
}
}
Code language: Java (java)
Output:
Hibernate: insert into EMPLOYEE (firstname, lastname) values (?, ?)
Hibernate: insert into MEETING (SUBJECT, MEETING_DATE) values (?, ?)
Hibernate: insert into MEETING (SUBJECT, MEETING_DATE) values (?, ?)
Hibernate: insert into EMPLOYEE (firstname, lastname) values (?, ?)
Hibernate: insert into EMPLOYEE_MEETING (EMPLOYEE_ID, MEETING_ID) values (?, ?)
Hibernate: insert into EMPLOYEE_MEETING (EMPLOYEE_ID, MEETING_ID) values (?, ?)
Hibernate: insert into EMPLOYEE_MEETING (EMPLOYEE_ID, MEETING_ID) values (?, ?)
Code language: SQL (Structured Query Language) (sql)
Download Source Code
Hibernate-many-to-many-set-xml.zip (9 KB)
Hi there! I mapped my tables based on above. They works I can load or save. But I can’t delete. When i tried to say something like this:
Employee e = (Employee) session.load(Employee.class,1);
session.delete(e);
I get the SEVERE: Cannot delete or update a parent row: a foreign key constraint fails error.
I tried to implements PreDeleteEventListener and delete the child(Employee_Metting) before delete a Employee row, but another exception is throwed – the Employee_Metting is not mapped. Must I map this table? Or could I do this without it?
Hi, i got this error, while executing the above example, can you please help me.
here my doubt is : whether we have to create table “EMPLOYEE_MEETING” or hibernate will create it. i want clarity in it. thanks for ur reply
Hi,
In this case, table Employee_Meeting is not created by Hibernate. You have to explicitly do this in your database. This is because in our hibernate.cfg.xml file, we have define value for key hibernate.hbm2ddl.auto to validate. Change it to create-drop or create to let Hibernate create schema for you. Read this article for more details.
Hope this helps.
Nice article Viral…
Keep on posting in simplified manner. It really help the beginers to understand actual scenarions… Thanks …
Hi,
after running this example the employee-meeting containg only three rows of values.but it should contain the four rows of values.why because the employee and meeting tables conatins 2 rows of values in each table.can you clarify
Hi Suresh,
Please see the main() method. Employee1 is associated with two meeting object and Employee2 is associated with one meeting object so total number of associations is 3. This is the reason why we have 3 records in Employee_Meeting table
Hi Viral,
Cascade=”all” is meaningless in the above case as in many to many association we can have multiple parents and an instance of object can’t be deleted if one parent is deleted.It should be “save-update”
Nice article. Simple to understand.
How to make the Meeting class as owner in this case?
Hi,
I think your mapping in the Meeting.hbm.xml is wrong. It should be:
Thanks for the good tutorial. After having executed the example, I manually inserted another record into the employee table, and than updated the code in the main method with the following code (I was trying to read the many to many relationship records).
List empList = session.createQuery(“from Employee”).list();
if (empList!=null && empList.size()>0) {
for (Employee employee: empList) {
System.out.println(“\n employee.getLastname(): ” + employee.getLastname());
Set empMeetings = employee.getMeetings();
if (empMeetings!=null) {
if (empMeetings.size()==0) {
System.out.println(“meeting subject for ” + employee.getLastname() + ” is 0″);
} else {
Iterator mIterator = empMeetings.iterator();
while (mIterator.hasNext()) {
Meeting m = mIterator.next();
System.out.println(“meeting subject for ” + employee.getLastname() + “: ” + m.getSubject());
}
}
} else {
System.out.println(“meeting subject for ” + employee.getLastname() + ” is null”);
}
}
}
The output was as the following. From the output, we can find out that 4 queries were executed to get all employees with the meetings.
emp.getLastname(): Brin
Hibernate: select meetings0_.EMPLOYEE_ID as EMPLOYEE1_1_, meetings0_.MEETING_ID as MEETING2_1_, meeting1_.MEETING_ID as MEETING1_2_0_, meeting1_.SUBJECT as SUBJECT2_0_, meeting1_.MEETING_DATE as MEETING3_2_0_ from EMPLOYEE_MEETING meetings0_ left outer join MEETING meeting1_ on meetings0_.MEETING_ID=meeting1_.MEETING_ID where meetings0_.EMPLOYEE_ID=?
meeting subject for Brin: Weekly Status meeting
meeting subject for Brin: Quaterly Sales meeting
emp.getLastname(): Page
Hibernate: select meetings0_.EMPLOYEE_ID as EMPLOYEE1_1_, meetings0_.MEETING_ID as MEETING2_1_, meeting1_.MEETING_ID as MEETING1_2_0_, meeting1_.SUBJECT as SUBJECT2_0_, meeting1_.MEETING_DATE as MEETING3_2_0_ from EMPLOYEE_MEETING meetings0_ left outer join MEETING meeting1_ on meetings0_.MEETING_ID=meeting1_.MEETING_ID where meetings0_.EMPLOYEE_ID=?
meeting subject for Page: Quaterly Sales meeting
emp.getLastname(): last_name1
Hibernate: select meetings0_.EMPLOYEE_ID as EMPLOYEE1_1_, meetings0_.MEETING_ID as MEETING2_1_, meeting1_.MEETING_ID as MEETING1_2_0_, meeting1_.SUBJECT as SUBJECT2_0_, meeting1_.MEETING_DATE as MEETING3_2_0_ from EMPLOYEE_MEETING meetings0_ left outer join MEETING meeting1_ on meetings0_.MEETING_ID=meeting1_.MEETING_ID where meetings0_.EMPLOYEE_ID=?
meeting subject for last_name1 is 0
Is it possible to get all employees with the meetings by executing a single query? If so, can you point out the direction about how to do it?
Thank you!
http://stackoverflow.com/questions/15560856/nhibernate-bidirectional-many-to-many-mapping-list-bag
But I would like to give you a hint. Try to change the approach and introduce the intermediate object. See the NHibernate documentation: Chapter 24. Best Practices. Quick summary:
Don’t use exotic association mappings.
Good usecases for a real many-to-many associations are rare. Most of the time you need additional information stored in the “link table”. In this case, it is much better to use two one-to-many associations to an intermediate link class. In fact, we think that most associations are one-to-many and many-to-one, you should be careful when using any other association style and ask yourself if it is really neccessary.
And I would say, that this is (could be) the case, because you really need the Order …
Hi
When I created the same scenario in my project, I got the following exception when I’m trying to delete a record from one of the base tables involved in the Many-to-Many mapping.
Exception:
Caused by: net.sf.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session
Please suggest what am I supposed to do.
Thanks
Cannot add or update a child row: a foreign key constraint fails (`test`.`employee_meeting`, CONSTRAINT `FK_EMPLOYEE` FOREIGN KEY (`employee_id`) REFERENCES `employee` (`employee_id`))
error spotted in your meeting.hbm.xml
please check key column and and many to many column
still despite all the fact that still getting same error
Cannot add or update a child row: a foreign key constraint fails (`test`.`employee_meeting`, CONSTRAINT `FK_EMPLOYEE` FOREIGN KEY (`employee_id`) REFERENCES `employee` (`employee_id`))
Pushpendra,
One more correction, class attribute should contain “Employee” value instead of “Meeting” in many to many tag
Is there any way to update Just employee information? If I have two different UIs, one for Employee and one for associating Employee to meetings, then if I just want to save employee information (with meetings null) then it deletes all the meetings for that employee. Is there any way to avoid this delete? I’m using cascade=”save-update”
Hi patel,
it is very really good article for hibernate beginners, thanx a lot.
How about if we have a column STATUS in table EMPLOYEE_MEETING. Status can be ‘accepted, ‘declined’, ‘tentative’. Stuck with this quite for some time. Please help. In my module I need to update this status for each user for every meeting.
Really,Very Good Explanation in simplified Manner.
Best for Beginners to learn from the scratch,
Keep posting its really help us.
Hi,
I really appreciate your blog post on how to implement relations.
Actually i’m working on project that use dynamic model with “entity-name” we convert POJO classes to a map, and hibernate using xml file persist this map.
Know i can persist One-One and Many-to-one relationship but i’m not able to persist a MANY-TO-MANY.
Unfortunately i can’t found any documentation or code sample either in hibernate suite tests about that.
Can you please help me if you know how to do it or guide me to certain resources.
Thanks,
how to select inner join 2 table
Please fix 2 xml mapping file at many-to-many stuff.
The right configuration must be like this:
Nice example Viral.
nice example