Hibernate Many-to-Many Association Annotations Example
- Details
- Written by Nam Ha Minh
- Last Updated on 30 November 2023   |   Print Email
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:
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):
- Hibernate 4.2.7.SP1
- JDK 7
- Eclipse IDE 4.3 (Kepler)
- Maven 3
- MySQL Community Server 5.5.23
- MySQL Connector Java driver 5.1.26
Table of content:
- Creating Database and Tables
- Creating Maven-Eclipse Project
- Using Annotations for Many-to-Many Association
- Writing Hibernate Configuration File
- 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:
2. Creating Maven-Eclipse Project
In Eclipse IDE, create a Maven project named HibernateMany2ManyAnnotationsExample with the following 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:
Result in the users table:
Result in the join table users_groups:
If you prefer learning via video, I recommend you to watch the following video::
Related Hibernate Many-to-Many Tutorials:
- Hibernate Many-to-Many Association with Extra Columns in Join Table Example
- Hibernate Many-to-Many XML Mapping Example
Other Hibernate Tutorials:
- Java Hibernate JPA Annotations Tutorial for Beginners
- Hibernate One-to-One Association on Primary Key Annotations Example
- Hibernate One-to-Many Association Annotations Example
- Hibernate Enum Type Mapping Example
- Hibernate Binary Data and BLOB Mapping Example
- Hibernate Query Language (HQL) Example
- Java Hibernate Reverse Engineering Tutorial with Eclipse and MySQL
- Hibernate Basics - 3 ways to delete an entity from the datastore
Comments
Maximum call stack size exceeded
good