In this Hibernate tutorial, we are going to illustrate how to use JPA annotations to map a many-to-many association (both bidirectional and unidirectional) with a sample program developed using Eclipse and Maven. MySQL is used as the test database. Using annotations is an alternate approach (and preferred) to the XML descriptor one: Hibernate Many-to-Many XML Mapping Example.

Let’s recall about a typical many-to-many association:

many to many entity relationship diagram

Here, the multiplicity between the groups and users table is many-to-many, meaning that a group can have many users and vice-versa, a user can belong to many groups. A join table (users_groups) is required to connect the both sides.

To develop the sample program, we use the following pieces of software programs and libraries (you can use newer versions):

Table of content:

  1. Creating Database and Tables
  2. Creating Maven-Eclipse Project
  3. Using Annotations for Many-to-Many Association
  4. Writing Hibernate Configuration File
  5. Writing a Test Program
 

1. Creating Database and Tables

The following MySQL script is used to create a database called usersdb with three tables: groups, users and users_groups (join table):

create database usersdb;

use usersdb;

CREATE TABLE `users` (
  `user_id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(45) NOT NULL,
  `password` varchar(45) NOT NULL,
  `email` varchar(45) NOT NULL,
  PRIMARY KEY (`user_id`)
);


CREATE TABLE `groups` (
  `group_id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(45) NOT NULL,
  PRIMARY KEY (`group_id`)
);

CREATE TABLE `Users_Groups` (
  `user_id` int(11) NOT NULL,
  `group_id` int(11) NOT NULL,
  PRIMARY KEY (`user_id`,`group_id`),
  KEY `fk_user` (`user_id`),
  KEY `fk_group` (`group_id`),
  CONSTRAINT `fk_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`user_id`),
  CONSTRAINT `fk_group` FOREIGN KEY (`group_id`) REFERENCES `groups` (`group_id`)
);
The usersdb database would have the following structure:



usersdb database structure

 


2. Creating Maven-Eclipse Project

In Eclipse IDE, create a Maven project named HibernateMany2ManyAnnotationsExample with the following structure:

Hibernate Many to Many project structure

This project consists of the following files:

  • Model classes: Group.java and User.java
  • Hibernate XML configuration file: hibernate.cfg.xml
  • Test program: UsersManager.java
  • Maven project: pom.xml
For the JAR files required for Hibernate and MySQL-Java library, update the pom.xml file with the following content.

<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>net.codejava.hibernate</groupId>
  <artifactId>HibernateMany2ManyAnnotationsExample</artifactId>
  <version>1.0</version>
  <name>HibernateMany2ManyAnnotationsExample</name>
  <description>A sample project that demonstrates using JPA annotations to map
   a many-to-many association in Hibernate</description>
  
  <dependencies>
  	<dependency>
  		<groupId>org.hibernate</groupId>
  		<artifactId>hibernate-core</artifactId>
  		<version>4.2.7.SP1</version>
  	</dependency>
  	<dependency>
  		<groupId>mysql</groupId>
  		<artifactId>mysql-connector-java</artifactId>
  		<version>5.1.26</version>
  	</dependency>
  </dependencies>
     
</project>
Here, we specify only two dependencies for Hibernate core and MySQL Connector/J, and the rest will be resolved automatically by Maven, such as Hibernate JPA and Hibernate Commons Annotations.


3. Using Annotations for Many-to-Many Association

To use JPA annotations to map the many-to-many association, create two model classes called Group.java and User.java with the following source code:

Filenet\codejava\hibernate\Group.java:

package net.codejava.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 = "GROUPS")
public class Group {
	private long id;
	private String name;

	private Set<User> users = new HashSet<User>();

	public Group(String name) {
		this.name = name;
	}

	public void addUser(User user) {
		this.users.add(user);
	}

	@Id
	@GeneratedValue
	@Column(name = "GROUP_ID")
	public long getId() {
		return id;
	}

	@ManyToMany(cascade = CascadeType.ALL)
	@JoinTable(
			name = "USERS_GROUPS",
			joinColumns = @JoinColumn(name = "GROUP_ID"),
			inverseJoinColumns = @JoinColumn(name = "USER_ID")
	)
	public Set<User> getUsers() {
		return users;
	}

	// other getters and setters...
}
 

File net\codejava\hibernate\User.java:

package net.codejava.hibernate;

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 = "USERS")
public class User {
	private long id;
	private String username;
	private String password;
	private String email;

	private Set<Group> groups = new HashSet<Group>();

	public User(String username, String password, String email) {
		this.username = username;
		this.password = password;
		this.email = email;
	}

	public void addGroup(Group group) {
		this.groups.add(group);
	}

	@Id
	@GeneratedValue
	@Column(name = "USER_ID")
	public long getId() {
		return id;
	}

	@ManyToMany(mappedBy = "users")
	public Set<Group> getGroups() {
		return groups;
	}

	// other getters and setters...
}
We can see that both the Group and User classes have a collection (Set) of elements of each other, thus this association is bidirectional. Here, the Group is the owner side and the User is the other side.

To map this many-to-many association, these JPA annotations are used: @ManyToMany, @JoinTable and @JoinColumn, besides the basic annotations (@Entity, @Column, @Id, etc) . Let’s look at these annotations on each side closely:

  • On the owner side (Group):
    private Set<User> users = new HashSet<User>();
    
    @ManyToMany(cascade = CascadeType.ALL)
    @JoinTable(
    		name = "USERS_GROUPS",
    		joinColumns = @JoinColumn(name = "GROUP_ID"),
    		inverseJoinColumns = @JoinColumn(name = "USER_ID")
    )
    public Set<User> getUsers() {
    	return users;
    }
    Here, the @JoinTable annotation is used to specify the details of the join table (table name and two join columns - using the @JoinColumn annotation). The cascade attribute of the @ManyToMany annotation is required, so that Hibernate will update the associated users when the group is updated.

  • On the other side (User):
    private Set<Group> groups = new HashSet<Group>();
    
    @ManyToMany(mappedBy = "users")
    public Set<Group> getGroups() {
    	return groups;
    }
    This side is much simpler than the owner side, as we only need to specify the mappedBy attribute of the @ManyToMany annotation. That means this groups collection is mapped by the users collection on the owner side.

 

Implementing a Unidirectional Many-to-Many Association:

To make direction of the association becomes unidirectional, simply remove the groups collection from the User class:

@Entity
@Table(name = "USERS")
public class User {
	private long id;
	private String username;
	private String password;
	private String email;

	public User(String username, String password, String email) {
		this.username = username;
		this.password = password;
		this.email = email;
	}

	@Id
	@GeneratedValue
	@Column(name = "USER_ID")
	public long getId() {
		return id;
	}

	// other getters and setters...
}
I recommend you take this course to learn how many to many relationship is used in a real world project such as e-commerce shopping application.


4. Writing Hibernate Configuration File

Create Hibernate XML configuration file (hibernate.cfg.xml) under the classpath with the following content:

<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://www.hibernate.org/dtd/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/usersdb</property>
    <property name="connection.username">root</property>
    <property name="connection.password">secret</property>
    <property name="dialect">org.hibernate.dialect.MySQLDialect</property>
    <property name="show_sql">true</property>
    
    <mapping class="net.codejava.hibernate.User"/>
    <mapping class="net.codejava.hibernate.Group"/>
      
  </session-factory>
</hibernate-configuration>
Here, we specify database connection settings and the mapping classes to be loaded.


5. Writing a Test Program

So far we have done the association mapping part. To know if the mapping works as expected, let’s write a small program to insert some sample data with the following code (UsersManager.java):

package net.codejava.hibernate;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;

/**
 * A program that demonstrates using JPA annotations to map
 * a bidirectional many-to-many association in Hibernate framework.
 * @author www.codejava.net
 *
 */
public class UsersManager {

	public static void main(String[] args) {
		// loads configuration and mappings
		Configuration configuration = new Configuration().configure();
		ServiceRegistryBuilder registry = new ServiceRegistryBuilder();
		registry.applySettings(configuration.getProperties());
		ServiceRegistry serviceRegistry = registry.buildServiceRegistry();

		// builds a session factory from the service registry
		SessionFactory sessionFactory = configuration
				.buildSessionFactory(serviceRegistry);

		// obtains the session
		Session session = sessionFactory.openSession();
		session.beginTransaction();
		
		Group groupAdmin = new Group("Administrator Group");
		Group groupGuest = new Group("Guest Group");
		
		User user1 = new User("Tom", "tomcat", "tom@codejava.net");
		User user2 = new User("Mary", "mary", "mary@codejava.net");
		
		groupAdmin.addUser(user1);
		groupAdmin.addUser(user2);
		
		groupGuest.addUser(user1);
		
		user1.addGroup(groupAdmin);
		user2.addGroup(groupAdmin);
		user1.addGroup(groupGuest);
		
		session.save(groupAdmin);
		session.save(groupGuest);
		
		session.getTransaction().commit();
		session.close();		
	}

}
Output of the program:

Hibernate: insert into GROUPS (name) values (?)
Hibernate: insert into USERS (email, password, username) values (?, ?, ?)
Hibernate: insert into USERS (email, password, username) values (?, ?, ?)
Hibernate: insert into GROUPS (name) values (?)
Hibernate: insert into USERS_GROUPS (GROUP_ID, USER_ID) values (?, ?)
Hibernate: insert into USERS_GROUPS (GROUP_ID, USER_ID) values (?, ?)
Hibernate: insert into USERS_GROUPS (GROUP_ID, USER_ID) values (?, ?)
Result in the groups table:

records in groups table

Result in the users table:

records in users table

Result in the join table users_groups:

records in users groups table

 

If you prefer learning via video, I recommend you to watch the following video::

 

Related Hibernate Many-to-Many Tutorials:

 

Other Hibernate Tutorials:


About the Author:

is certified Java programmer (SCJP and SCWCD). He began programming with Java back in the days of Java 1.4 and has been passionate about it ever since. You can connect with him on Facebook and watch his Java videos on YouTube.



Attachments:
Download this file (HibernateMany2ManyAnnotationsExample.zip)HibernateMany2ManyAnnotationsExample.zip[Eclipse-Maven project]17 kB

Add comment

   


Comments 

#4Dimas2019-10-09 11:29
Doesnt working ERROR: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'GROUPS (name) values ('Administrator Group')' at line 1
Quote
#3PDez2018-11-05 09:16
Hibernate is too inefficient for join tables. Your example looks good but try this scenario. You have a view with 2 lists, the one on the left of Users not in the Group and the right list for the Users that are associated to the Group. You drag and drop various elements from each list and press save. Let's say all you did was remove a User. What hibernate does is update each User before deleting the one association from the join table. Yuck!
Quote
#2Rajasekhar2018-09-05 04:17
There was an error parsing JSON data

Maximum call stack size exceeded
Quote
#1s2015-10-14 06:18
Quoting s:
sssss

good
Quote