Hibernate Many To Many Annotation Mapping Tutorial

Welcome to the Hibernate Tutorial Series. In previous tutorial we saw how to implement Many to Many relationship using XML mapping. In this tutorial we will modify the source code from previous Many To Many XML mapping tutorial and add JPA/Annotation support to it.

Let us see how to implement Many-to-Many relationship in Hibernate using Annotation.

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. Project Setup

Download the source code: Hibernate-many-to-many-set-xml.zip (9 KB) and import the project in Eclipse. We will update the source code.

3. Update Maven Dependency

File: pom.xml

<?xml version="1.0" encoding="UTF-8"?><project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>HibernateCache</groupId>
  <artifactId>HibernateCache</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <description></description>
  <dependencies>
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>ejb3-persistence</artifactId>
      <version>1.0.1.GA</version>
    </dependency>
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-annotations</artifactId>
      <version>3.3.1.GA</version>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.10</version>
    </dependency>
  </dependencies>
</project>

3. Remove Hibernate Mapping (hbm) Files

We are not going to use hibernate mapping files or hbm files as we will map the model using Java 5 Annotations. Delete the files employee.hbm.xml and meeting.hbm.xml.

4. Update Hibernate Model Class

We will update Employee and Meeting model classes and add Annotations to map them with database table.

File: Employee.java

package net.viralpatel.hibernate;

import java.util.HashSet;
import java.util.Set;

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="EMPLOYEE")
public class Employee {
	
	@Id
	@Column(name="EMPLOYEE_ID")
	@GeneratedValue
	private Long employeeId;
	
	@Column(name="FIRSTNAME")
	private String firstname;
	
	@Column(name="LASTNAME")
	private String lastname;
	
	@ManyToMany(cascade = {CascadeType.ALL})
	@JoinTable(name="EMPLOYEE_MEETING", 
				[email protected](name="EMPLOYEE_ID")}, 
				[email protected](name="MEETING_ID")})
	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;

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="MEETING")
public class Meeting {

	@Id
	@Column(name="MEETING_ID")
	@GeneratedValue
	private Long meetingId;

	@Column(name="SUBJECT")
	private String subject;
	
	@Column(name="MEETING_DATE")
	private Date meetingDate;
	
	@ManyToMany(mappedBy="meetings")
	private Set<Employee> employees = new HashSet<Employee>();
	
	public Meeting(String subject) {
		this.subject = subject;
		this.meetingDate = new Date();
	}
	
	// Getter and Setter methods
}

Let us understand the annotations we used here to map Many to many relationship.

@ManyToMany – Is used to create many-to-many relationship between Employee and Meeting entities. If the Collection is defined using generics to specify the element type, the associated target entity class does not need to be specified; otherwise it must be specified. Every many-to-many association has two sides, the owning side and the non-owning, or inverse, side. The join table is specified on the owning side. If the association is bidirectional, either side may be designated as the owning side.

Note that in above entity classes, Employee is defined as relationship owner as @JoinColumn is define in Employee class and mappedBy is specified in Meeting class.

@JoinTable – Is used to define the join table (link table) for many-to-many relationship. It is specified on the owning side of a many-to-many association, or in a unidirectional one-to-many association. In this case the join table is EMPLOYEE_MEETING.

If the JoinTable annotation is missing, the default values of the annotation elements apply. The name of the join table is assumed to be the table names of the associated primary tables concatenated together (owning side first) using an underscore.

@JoinColumn – Is used to define the join column (linking column) in both main tables.

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.

5. Hibernate Utility Class

In previous example, the Hibernate Utility class uses Hibernate’s org.hibernate.cfg.Configuration class to generate SessionFactory. For this example we will replace this class with org.hibernate.cfg.AnnotationConfiguration class. Following is the code for HibernateUtil.java class.

File: HibernateUtil.java

package net.viralpatel.hibernate;
 
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;
 
public class HibernateUtil {
 
    private static final SessionFactory sessionFactory = buildSessionFactory();
 
    private static SessionFactory buildSessionFactory() {
        try {
            // Create the SessionFactory from hibernate.cfg.xml
            return new AnnotationConfiguration().configure()
                    .buildSessionFactory();

        } catch (Throwable ex) {
            System.err.println("Initial SessionFactory creation failed." + ex);
            throw new ExceptionInInitializerError(ex);
        }
    }
 
    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }
}

6. Hibernate Configuration File

Edit Hibernate configuration file (hibernate.cfg.xml) and add mappings for Employee and Meeting classes. Following is the final hibernate.cfg.xml file:

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>
        <!-- Database connection settings -->
        <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 class="net.viralpatel.hibernate.Employee"/>
 	<mapping class="net.viralpatel.hibernate.Meeting"/>
 		 
    </session-factory>
</hibernate-configuration>

7. Review Project Structure

many-to-many-annotation-project-structure

Execute example

Execute following Main class to test Many-to-many relational mapping using Annotation.

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-annotation.zip (8 KB)

Get our Articles via Email. Enter your email address.

You may also like...

35 Comments

  1. Rohit Salunkhe says:

    Thanks a lot. Nice tutorial.

  2. BioD says:

    I’ve done a ‘many-to-many’ relationship with both sides as a owner. Is this correct?

  3. ChandraSekhar S says:

    Hi , Can any body give me an example for fetching the data of both tables using Join table?
    How to write the Query?

    Thanks in advance,
    Chandu Sannamuri

  4. Sachin says:

    Great article. I had one question though. In my EMPLOYEE_MEETING table I have few other attributes. How do I handle these attributes? These are the columns

    EmployeeId
    MeetingId
    EmployeeResponse

    • Riley says:

      Create something like
      EmployeeMeetingEvent
      hasOne meeting
      hasOne event
      all other fields you need

  5. stubborn says:

    Hi,

    When i update the employee_meeting table from employee side, it just replace the old value with the same employee_id and enter the new one. So, is this the behavior of many to many or i did something wrong.

  6. Anil Kumar A says:

    Hi,
    I like your tutorial. can you please extend the tutorial with many to many with extra columns. thanks in advance.

  7. sravan kumar says:

    Thanks you very much for this tutorial.

  8. Ajit samant says:

    Hi all i am also use same procedure in my project. but problem is when i want to insert the data in mapping table for same employee,hibernate 1st delete the exiting relation for same employee then insert the new mapping.means 1st hibernate run the delete query for exit customer then insert mapping employee id and meeting i..

    Please anyone help me.

  9. brahim says:

    Thanks for this best tutorial
    it works fine but when i try to update row (session.update) it give me this exception
    Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1; nested exception is org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1

  10. Jimmy says:

    thanks for this tutorial!! but would you please transform this tutorial to a web based application? Thanks!!

  11. MikeHT says:

    To actually use this I think your Employee class need a Set of Mettings and vice versa. When you save meetings away you must not only add the meetings to the Employee’s Set of meetings, but for each meeting you must add the Employee to it’s list of Employees.

  12. Mark says:

    As, MikeHT said, I believe this example to be erroneous. In a bi-directional many-to-many both sides of the relationship need to be managed.

  13. Ramanujam says:

    Nice one Viral,

    I have similar requirement where i need to associate Budget and ExpenseType, whenever i trying to add a new Budget, there was as update call made to the expenseType, since one of the column in the expenseType table is not null, it is throwing error. But when i change the cascade type to persist from all, it works fine. Can you please throw some light on this.

  14. NaveenReddy Bommu says:

    Nice tutorial.

  15. daniil says:

    very nice! very helpful!

  16. Eduardo says:

    Thanks for the tutorial. I am trying to implement something similar, but where I only want to add records to the EMPLOYEE_MEETING table (Imagine that we have a frozen list of ’employees’ , and a regular set of weekly ‘meetings’ in the company – we would be trying to assign some employees to participate in some meetings – we do *not* want to delete/insert/update any new records into the EMPLOYEES or MEETINGS table). How would we change this example to catter for this?

    Thanks a lot.
    Eduardo

  17. MikeHT says:

    It would be nice to see a discussion of the meaning of ownership in this example and how it affects saving and retrieving from the database.

  18. narayana says:

    Excellent work done..

  19. Heinrich Maier says:

    Absolutely a nice tutorial. High quality and really nice explained. Thanks guy!

  20. Shivdatta says:

    In ManyToMany relation, it would be better if you show deleting a record part.

    I am facing issue in that.

    Regards,
    Shivdatta

  21. Rohini says:

    Nice tutorial.

  22. raju says:

    Hi ,

    Your Tutorials Are Really So Nice.

    I Have A Doubt…..
    shall we make this “ManyToMany” with out using linking table.
    If Yes How?

    Thanks
    raju.

  23. lethal.industry says:

    any idea or tutorial how to do a form with checkboxes in thymeleaf (many-to-many)? because it’s been a week i can’t get out this trouble and i think a form with checkboxes is very common in web development, but i can’t find anything working on the web

  24. lethal.industry says:

    i implemented this solution for a form with checkboxes of a many-to-many relationship.
    It’s not not the best, but it works… i post this so maybe someone can find it usefull:

    http://stackoverflow.com/questions/24673798/spring-mvc-hibernate-a-form-with-checkboxes-for-manytomany-relationship/24701007#24701007

  25. Sandeep says:

    Excellent Example!
    I have a slightly different requirement. Let’s say the Meeting table is a lookup table, i.e; Values cannot be inserted or modified from the application. I have the exact same setup, but, when I add an EmployeeMeeting, lets say for a new employee and existing meeting, how do I need to configure the annotations ? Because for me, if the application sends the Meeting IDs, now, I need to create an employee and their corresponding meeting ids(which already exist in the lookup table). Again, Meeting is kind of a lookup table for me, I populate dynamic checkboxes using that table.

    • sudheer says:

      Were you able to figure this out? I am also having a similar issue now. Please help.

  26. Ganeshwararao Gangada says:

    Hi , I have got clearly understand many to many relation between tables . Thanks a lot.

  27. prasanthcheenu says:

    Hi Virat,
    May i know how you are identifying that employee is a owning side. Because normally owning side is determined when the Entity Employee has a reference to the Enity named Meeting.But here both are having only collection.Could you please clarify this.
    Thank you
    prasanth

  28. parth says:

    How to fetch all the Emp having meetingId 1 using hql?

  29. Sridhar Goranti says:

    Thank you Patel, it was a nice tutorial and its covers a lot. I am sure it will help everyone.

  30. h says:

    nice……

  31. Ratnesh Chandak says:

    How do i enter data in my employee_meeting table when i am adding meeting, shall i have to take employee_id from user, and also create employee_meeting repository to save employee & meeting or shall i take employee_id and meeting_id differently and save them in table through repository

  32. Ahmed Shareef says:

    I have an issue with JPA(Hibernate 4.x ) Many to Many relationship, when I fire a query to fetch records from the tables, for example fetching Employee records from Employee table where meetingIds are in Employee_Meeting table, then it is giving data but after executing the select query it is deleting all the entries from the mapping table (Employee_Meeting). I coded the entities as same as your entities.
    What would be the issue.?
    and Can I implement the Many-to-Many relationship some thing like, One to Many from Employee to Employee_Meeting, One to Many from Meeting to Employee_Meeting, and Employee_Meeting will have Many to One relationship to both Employee and Meeting..?

  33. sanjay says:

    i have problem in many to many for inner join table my table is user,group, user_group
    i have user _group table declarer one own id ….
    so am not getting proper soln so please guid me

Leave a Reply

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