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):
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.
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:
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.
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::
Nam Ha Minh 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.
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
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!
Comments
Maximum call stack size exceeded
good