Hibernate Many To Many XML Mapping Tutorial

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
.
many-to-many-relationship-diagram

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`)
)

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>

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
}

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
}

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;
	}
}

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>

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>

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>

7. Review Project Structure

hibernate-many-to-many-example-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();
    }
}

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 (?, ?)

many-to-many-hibernate-output-table

Download Source Code

Hibernate-many-to-many-set-xml.zip (9 KB)

Get our Articles via Email. Enter your email address.

You may also like...

25 Comments

  1. Tomasz says:

    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?

  2. sravankumar says:

    Hi, i got this error, while executing the above example, can you please help me.

    75 [main] INFO org.hibernate.cfg.Configuration - Configuration resource: /hibernate.cfg.xml
    
    org.hibernate.HibernateException: Missing table: EMPLOYEE_MEETING
    	at org.hibernate.cfg.Configuration.validateSchema(Configuration.java:1127)
    
    Caused by: org.hibernate.HibernateException: Missing table: EMPLOYEE_MEETING
    

    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

    • Viral Patel says:

      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.

  3. Rajesh says:

    Nice article Viral…
    Keep on posting in simplified manner. It really help the beginers to understand actual scenarions… Thanks …

  4. suresh says:

    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

    • tecfinder says:

      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

  5. tecfinder says:

    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”

  6. Mayur Mistry says:

    Nice article. Simple to understand.

  7. Ingo says:

    How to make the Meeting class as owner in this case?

  8. Kevin says:

    Hi,

    I think your mapping in the Meeting.hbm.xml is wrong. It should be:

  9. david says:

    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!

  10. Just soneone says:

    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 …

  11. Swaroop says:

    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

  12. pushpendra says:

    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`))

  13. pushpendra says:

    error spotted in your meeting.hbm.xml

    please check key column and and many to many column

    <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>
    

    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`))

    • tecfinder says:

      Pushpendra,

      One more correction, class attribute should contain “Employee” value instead of “Meeting” in many to many tag

  14. shailesh says:

    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”

  15. venkatadri says:

    Hi patel,

    it is very really good article for hibernate beginners, thanx a lot.

  16. VIkky says:

    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.

  17. Dharmesh Mistry says:

    Really,Very Good Explanation in simplified Manner.
    Best for Beginners to learn from the scratch,
    Keep posting its really help us.

  18. Naoufal Bentoumi says:

    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,

  19. dam ct says:

    how to select inner join 2 table

  20. Đức Quynh says:

    Please fix 2 xml mapping file at many-to-many stuff.
    The right configuration must be like this:

    Employee.hbm.xml
    <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="net.hibernate.tutorial.model.Meeting"></many-to-many>
    		</set>
    
    Meeting.hbm.xml
    <set name="employees" table="EMPLOYEE_MEETING" inverse="true"
    			lazy="true" fetch="select">
    			<key column="MEETING_ID"/>
    			<many-to-many column="EMPLOYEE_ID"
    				class="net.hibernate.tutorial.model.Employee" />
    		</set>
    
    
  21. Nice example Viral.

  22. naveen says:

    nice example

Leave a Reply

Your email address will not be published. Required fields are marked *