JPA Many To Many Mapping

So far we discussed the One To One Mapping and One To Many Mappings in JPA.in this chapter we are discussing the Many To Many mapping in JPA. In a Many To Many relationship  there  are  source objects and each source  object contains a collection of child objects.And each child object can  be the child of any number of source objects .

JPA Many To Many Mapping

In this chapter we are discussing the concept with an example.Consider and example of  a social network user and the various user groups  in the   social network application.Each user can  become  the member in any number of groups.Each group contains any number of users. The Many To Many mapping is doing either by the @ManyToMany annotation or by the many-to-many XML element.

To implement Many To Many relationship there should a dedicated table for source objects and another table for child objects.In addition , there should be an intermediate table  for  doing the mapping. So lets create the tables first.We are using the same ‘jpasampledb‘ database we created earlier.

Queries for creating tables

1)Create table for  storing user details

CREATE TABLE USER_TABLE( `USER_ID` INT NOT NULL, `USER_NAME` VARCHAR(50) NOT NULL, `FIRST_NAME` VARCHAR(50) NOT NULL,`LAST_NAME` VARCHAR(50) NOT NULL, PRIMARY KEY (`USER_ID`) );

2)Create table for storing group details

CREATE TABLE GROUPS_TABLE( `GROUP_ID` INT NOT NULL, `GROUP_NAME` VARCHAR(50) NOT NULL, `GROUP_TYPE` VARCHAR(50) NOT NULL, PRIMARY KEY (`GROUP_ID`) );

3)Create intermediate table for storing the mapping details

CREATE TABLE USERGROUPS_TABLE( `USER_ID` INT NOT NULL, `GROUP_ID` INT NOT NULL);

Now let us look into the coding side.We have  two entity classes , User.java and Group.java. Each User object contains a number of Group objects. Each individual can become the child object of any other User object.

We are using the same workspace we set up earlier. Also OpenJPA is using as provider.

1)Uni Directional Many To Many Relation

Let us discuss the unidirectional Many To Many relationship. So each User object can have any number of Group objects. The same Group object may become  the child of any number of other User objects.In the case of unidirectional relation , the child object does not have any reference to source objects.

User.java

import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;

@Entity(name = "user")
@Table(name = "user_table")
public class User implements Serializable {

private static final long serialVersionUID = 1L;
private int userId;
private String userName;
private String firstName;
private String lastName;
private Set userGroups = new HashSet();

public User() {

}

public User(String userName, String firstName, String lastName,Setgroups) {
this.userName = userName;
this.firstName = firstName;
this.lastName = lastName;
this.userGroups = groups;
}

@Id
@Column(name = "USER_ID")
@GeneratedValue(strategy = GenerationType.AUTO)
public int getUserId() {
return userId;
}

public void setUserId(int userId) {
this.userId = userId;
}

@Column(name = "USER_NAME")
public String getUserName() {
return userName;
}

public void setUserName(String userName) {
this.userName = userName;
}

@Column(name = "FIRST_NAME")
public String getFirstName() {
return firstName;
}

public void setFirstName(String firstName) {
this.firstName = firstName;
}

@Column(name = "LAST_NAME")
public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

@ManyToMany
@JoinTable(name = "USERGROUPS_TABLE", joinColumns = { @JoinColumn(name = "USER_ID", referencedColumnName = "USER_ID") }, inverseJoinColumns = { @JoinColumn(name = "GROUP_ID", referencedColumnName = "GROUP_ID") })
public Set getUserGroups() {
return userGroups;
}

public void setUserGroups(Set userGroups) {
this.userGroups = userGroups;
}

public String toString() {
return "[ UserId = " + getUserId() + " ; User Name = " + getUserName()
+ " ; First Name = " + getFirstName() + " ; Last Name = "
+ getLastName() + "; Groups : " + getUserGroups() + " ]";
}

}

The mapping is done by using the @ManyToMany annotation. The intermediate table details are mapped by using the @JoinTable annotation.

Group.java

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity(name = "Group")
@Table(name = "GROUPS_TABLE")
public class Group implements Serializable {

private static final long serialVersionUID = 1L;

private int groupId;
private String groupName;
private String groupType;

public Group() {

}

public Group(String groupName, String groupType) {
this.groupName = groupName;
this.groupType = groupType;

}

@Id
@Column(name = "GROUP_ID")
@GeneratedValue(strategy = GenerationType.AUTO)
public int getGroupId() {
return groupId;
}

public void setGroupId(int groupId) {
this.groupId = groupId;
}

@Column(name = "GROUP_NAME")
public String getGroupName() {
return groupName;
}

public void setGroupName(String groupName) {
this.groupName = groupName;
}

@Column(name = "GROUP_TYPE")
public String getGroupType() {
return groupType;
}

public void setGroupType(String groupType) {
this.groupType = groupType;
}

public int hashCode() {
return 10;
}

public boolean equals(Object group) {
boolean status = false;
if (getGroupName().equalsIgnoreCase(((Group) group).getGroupName())) {
status = true;
}
return status;
}

public String toString() {
return " [ " + " Group Id = " + getGroupId() + " ; Group Name = "
+ getGroupName() + " ;Group Type = " + getGroupType() + " ] ";
}

}

Lets see the persistence.xml

persistence.xml

org.apache.openjpa.persistence.PersistenceProviderImpl com.jpa.many2many.entities.User
com.jpa.many2many.entities.Group

Now lets see the main class . It simply creates a number of User and Group objects . Then associating the User and  Group objects and persisting to database. Then fetching from database.

Many2ManySample.java

import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaQuery;

import com.jpa.many2many.entities.Group;
import com.jpa.many2many.entities.User;

public class Many2ManySample {

public Many2ManySample() {

}

public void insertUserGroups() {
EntityManagerFactory entityManagerFactory = Persistence
.createEntityManagerFactory("OpenJPASample");
EntityManager entitymanager = entityManagerFactory
.createEntityManager();
if (null != entitymanager) {
Group group1 = new Group("Group1", "Friends");
Group group2 = new Group("Group2", "Friends");
Group group3 = new Group("Group3", "Relatives");
EntityTransaction transaction = entitymanager.getTransaction();
transaction.begin();
entitymanager.persist(group1);
entitymanager.persist(group2);
entitymanager.persist(group3);
transaction.commit();

}
}

public void insertUsers() {
EntityManagerFactory entityManagerFactory = Persistence
.createEntityManagerFactory("OpenJPASample");
EntityManager entitymanager = entityManagerFactory
.createEntityManager();
if (null != entitymanager) {
EntityTransaction readTransaction = entitymanager.getTransaction();
readTransaction.begin();
CriteriaQuery criteria = entitymanager.getCriteriaBuilder()
.createQuery(Group.class);
criteria.select(criteria.from(Group.class));
List list = entitymanager.createQuery(criteria)
.getResultList();
Iterator iterator = list.iterator();
Set groups1 = new HashSet();
Set groups2 = new HashSet();
while (iterator.hasNext()) {
Group group = (Group) iterator.next();
if (group.getGroupName().equalsIgnoreCase("Group1")) {
groups1.add(group);
} else if (group.getGroupName().equalsIgnoreCase("Group2")) {
groups1.add(group);
groups2.add(group);
} else if (group.getGroupName().equalsIgnoreCase("Group3")) {
groups2.add(group);
}
}
readTransaction.commit();
User user1 = new User("sams", "Samuel", "Johnson", groups1);
User user2 = new User("mike", "Mike", "William", groups2);
EntityTransaction insertTransaction = entitymanager
.getTransaction();
insertTransaction.begin();
entitymanager.persist(user1);
entitymanager.persist(user2);
insertTransaction.commit();
System.out.println("User details succesfully inserted");

}
}

public void fetchUsers() {
EntityManagerFactory entityManagerFactory = Persistence
.createEntityManagerFactory("OpenJPASample");
EntityManager entitymanager = entityManagerFactory
.createEntityManager();
if (null != entitymanager) {
EntityTransaction readTransaction = entitymanager.getTransaction();
readTransaction.begin();
Query query = entitymanager
.createQuery("select user FROM user user");
List list = query.getResultList();
Iterator iterator = list.iterator();
System.out.println("User Details fetched from database : ");
while (iterator.hasNext()) {
User user = (User) iterator.next();
System.out.println(user);
}

readTransaction.commit();
System.out.println("User Details over..");
}
}

public static void main(String[] args) {
Many2ManySample sample = new Many2ManySample();
sample.insertUserGroups();
sample.insertUsers();
sample.fetchUsers();
}

}

Output

User details succesfully inserted

User Details fetched from database :

[ UserId = 3701 ; User Name = sams ; First Name = Samuel ; Last Name = Johnson;  Groups : [ [  Group Id  = 3651 ; Group Name = Group1 ;Group Type =  Friends ] ,  [  Group Id  = 3652 ; Group Name = Group2 ;Group Type =  Friends ] ] ]

[ UserId = 3702 ; User Name = mike ; First Name = Mike ; Last Name = William;  Groups : [ [  Group Id  = 3652 ; Group Name = Group2 ;Group Type =  Friends ] ,  [  Group Id  = 3653 ; Group Name = Group3 ;Group Type =  Relatives ] ] ]

User Details over..

The screenshot of database tables are given below.

Contents in user_table

USER_ID         USER_NAME     FIRST_NAME     LAST_NAME

——-                  ———                 ———-                 ———–

   3601                sams                        Samuel                     Johnson

   3602               mike                         Mike                         William   

Contents in groups_table

GROUP_ID                     GROUP_NAME                    GROUP_TYPE

——–                                   ———-                                   ———–

    3651                                    Group1                                     Friends

    3652                                   Group2                                     Friends

    3653                                   Group3                                     Relatives

Contents in usergroups_table

USER_ID                        GROUP_ID

——-                                  ———-

   3701                                   3652

   3701                                   3651

   3702                                  3653

   3702                                  3652

2)Bi Directional Many To Many Relation

Now let us consider the  bi directional many to many mapping.There are slight changes in the entity classes and main class.

User.java

import java.io.Serializable;
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.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;

@Entity(name = "user")
@Table(name = "user_table")
public class User implements Serializable {

private static final long serialVersionUID = 1L;
private int userId;
private String userName;
private String firstName;
private String lastName;
private Set userGroups = new HashSet();

public User() {

}

public User(String userName, String firstName, String lastName) {
this.userName = userName;
this.firstName = firstName;
this.lastName = lastName;

}

@Id
@Column(name = "USER_ID")
@GeneratedValue(strategy = GenerationType.AUTO)
public int getUserId() {
return userId;
}

public void setUserId(int userId) {
this.userId = userId;
}

@Column(name = "USER_NAME")
public String getUserName() {
return userName;
}

public void setUserName(String userName) {
this.userName = userName;
}

@Column(name = "FIRST_NAME")
public String getFirstName() {
return firstName;
}

public void setFirstName(String firstName) {
this.firstName = firstName;
}

@Column(name = "LAST_NAME")
public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name = "USERGROUPS_TABLE", joinColumns = { @JoinColumn(name = "USER_ID", referencedColumnName = "USER_ID") }, inverseJoinColumns = { @JoinColumn(name = "GROUP_ID", referencedColumnName = "GROUP_ID") })
public Set getUserGroups() {
return userGroups;
}

public void setUserGroups(Set userGroups) {
this.userGroups = userGroups;
}

public void addToGroups(Group group) {
this.userGroups.add(group);
group.addUser(this);
}

public int hashCode() {
return 10;
}

public boolean equals(Object object) {
User user = (User) object;
boolean equality = false;
if (getFirstName().equalsIgnoreCase(user.getFirstName())
&& getLastName().equalsIgnoreCase(user.getLastName())) {
equality = true;
}
return equality;

}

public String toString() {
String userGroups = "";
for (Group group : getUserGroups()) {
userGroups = userGroups + "Group Name = " + group.getGroupName()
+ " ; ";
}
return "[ UserId = " + getUserId() + " ; User Name = " + getUserName()
+ " ; First Name = " + getFirstName() + " ; Last Name = "
+ getLastName() + "User Groups : " + userGroups + " ]";
}

}

Group.java

import java.io.Serializable;
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.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.Table;

@Entity(name = "Group")
@Table(name = "GROUPS_TABLE")
public class Group implements Serializable {

private static final long serialVersionUID = 1L;

private int groupId;
private String groupName;
private String groupType;
private Set users = new HashSet();

public Group() {

}

public Group(String groupName, String groupType) {
this.groupName = groupName;
this.groupType = groupType;

}

@Id
@Column(name = "GROUP_ID")
@GeneratedValue(strategy = GenerationType.AUTO)
public int getGroupId() {
return groupId;
}

public void setGroupId(int groupId) {
this.groupId = groupId;
}

@Column(name = "GROUP_NAME")
public String getGroupName() {
return groupName;
}

public void setGroupName(String groupName) {
this.groupName = groupName;
}

@Column(name = "GROUP_TYPE")
public String getGroupType() {
return groupType;
}

public void setGroupType(String groupType) {
this.groupType = groupType;
}

public int hashCode() {
return 10;
}

public boolean equals(Object group) {
boolean status = false;
if (getGroupName().equalsIgnoreCase(((Group) group).getGroupName())) {
status = true;
}
return status;
}

@ManyToMany(mappedBy = "userGroups", cascade = CascadeType.ALL)
public Set getUsers() {
return users;
}

public void setUsers(Set users) {
this.users = users;
}

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

public String toString() {
String userDetails = "";
for (User user : getUsers()) {
userDetails = userDetails + " User Name = "
+ user.getUserName()+ " ; ";
}
return " [ " + " Group Id = " + getGroupId() + " ; Group Name = "
+ getGroupName() + " ;Group Type = " + getGroupType()
+ " ; Member Details : " + userDetails + " ] ";
}

}

ManyToMany2DSample.java

import java.util.Iterator;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaQuery;

import com.jpa.many2many.entities.Group;
import com.jpa.many2many.entities.User;

public class ManyToMany2DSample {

public ManyToMany2DSample() {

}

public void insertUsers() {
EntityManagerFactory entityManagerFactory = Persistence
.createEntityManagerFactory("OpenJPASample");
EntityManager entitymanager = entityManagerFactory
.createEntityManager();
if (null != entitymanager) {
Group group1 = new Group("Group1", "Friends");
Group group2 = new Group("Group2", "Friends");
Group group3 = new Group("Group3", "Relatives");
User user1 = new User("sams", "Samuel", "Johnson");
User user2 = new User("mike", "Mike", "William");
user1.addToGroups(group1);
user1.addToGroups(group2);
user2.addToGroups(group2);
user2.addToGroups(group3);
EntityTransaction transaction = entitymanager.getTransaction();
transaction.begin();
entitymanager.persist(user1);
entitymanager.persist(user2);
transaction.commit();

}
}

public void fetchUsers() {
EntityManagerFactory entityManagerFactory = Persistence
.createEntityManagerFactory("OpenJPASample");
EntityManager entitymanager = entityManagerFactory
.createEntityManager();
if (null != entitymanager) {
EntityTransaction readTransaction = entitymanager.getTransaction();
readTransaction.begin();
Query query = entitymanager
.createQuery("select user FROM user user");
List list = query.getResultList();
Iterator iterator = list.iterator();
System.out.println("User Details fetched from database : ");
while (iterator.hasNext()) {
User user = (User) iterator.next();
System.out.println(user);
}

readTransaction.commit();
System.out.println("User Details over..");
}
}

public void fetchGroups() {
EntityManagerFactory entityManagerFactory = Persistence
.createEntityManagerFactory("OpenJPASample");
EntityManager entitymanager = entityManagerFactory
.createEntityManager();
if (null != entitymanager) {
EntityTransaction readTransaction = entitymanager.getTransaction();
readTransaction.begin();
CriteriaQuery criteria = entitymanager.getCriteriaBuilder()
.createQuery(Group.class);
criteria.select(criteria.from(Group.class));
List list = entitymanager.createQuery(criteria)
.getResultList();
Iterator iterator = list.iterator();
System.out.println("Group Details fetched from database : ");
while (iterator.hasNext()) {
Group group = (Group) iterator.next();
System.out.println(group);
}
readTransaction.commit();
System.out.println("Group Details over..");
}
}

public static void main(String[] args) {
ManyToMany2DSample sample = new ManyToMany2DSample();
sample.insertUsers();
sample.fetchUsers();
sample.fetchGroups();
}
}

Output

User Details fetched from database :

[ UserId = 4051 ; User Name = sams ; First Name = Samuel ; Last Name = JohnsonUser Groups : Group Name = Group1 ; Group Name = Group2 ;  ]

[ UserId = 4052 ; User Name = mike ; First Name = Mike ; Last Name = WilliamUser Groups : Group Name = Group2 ; Group Name = Group3 ;  ]

User Details over..

Group Details fetched from database :

 [  Group Id  = 4101 ; Group Name = Group2 ;Group Type =  Friends ; Member Details  :  User Name = mike  ;  User Name = sams  ;  ]

 [  Group Id  = 4102 ; Group Name = Group1 ;Group Type =  Friends ; Member Details  :  User Name = sams  ;  ]

 [  Group Id  = 4103 ; Group Name = Group3 ;Group Type =  Relatives ; Member Details  :  User Name = mike  ;  ]

Group Details over..

See Related  Topics

JPA Overview

JPA Example – Insert/Read

find method in JPA example

JPA Update Example

JPA Delete EXample

JPQL

JPQL Update Query Example

JPQL Delete Query

Caching

Overview to caching in JPA

Locking in JPA

Data  Locking in JPA

JPA Mapping Schemes

One To One Mapping in JPA

One To Many mapping in JPA

Many To One Mapping in JPA