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):
Table of content:
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:
In Eclipse IDE, create a Maven project named HibernateMany2ManyAnnotationsExample with the following structure:
This project consists of the following files:
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.
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:
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.
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.
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.
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.
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::