Welcome to the Hibernate Tutorial Series. In previous tutorial we saw how to implement Many to Many relationship using XML mapping. In this tutorial we will modify the source code from previous Many To Many XML mapping tutorial and add JPA/Annotation support to it.
Let us see how to implement Many-to-Many relationship in Hibernate using Annotation.
For this example, we will MySQL database. We are using Employee-Meeting relationship as a many to many relationship example. Each Employee can attain more than one meetings and each meetings can have more than one employee.
CREATE TABLE `employee` (
`employee_id` BIGINT(10) NOT NULL AUTO_INCREMENT,
`firstname` VARCHAR(50) NULL DEFAULT NULL,
`lastname` VARCHAR(50) NULL DEFAULT NULL,
PRIMARY KEY (`employee_id`)
)
CREATE TABLE `meeting` (
`meeting_id` BIGINT(20) NOT NULL AUTO_INCREMENT,
`subject` VARCHAR(50) NOT NULL,
`meeting_date` DATE NOT NULL,
PRIMARY KEY (`meeting_id`)
)
CREATE TABLE `employee_meeting` (
`employee_id` BIGINT(20) NOT NULL,
`meeting_id` BIGINT(20) NOT NULL,
PRIMARY KEY (`employee_id`, `meeting_id`),
INDEX `FK_MEETING` (`meeting_id`),
CONSTRAINT `FK_EMPLOYEE` FOREIGN KEY (`employee_id`) REFERENCES `employee` (`employee_id`),
CONSTRAINT `FK_MEETING` FOREIGN KEY (`meeting_id`) REFERENCES `meeting` (`meeting_id`)
)
Code language: SQL (Structured Query Language) (sql)
Download the source code: Hibernate-many-to-many-set-xml.zip (9 KB) and import the project in Eclipse. We will update the source code.
File: pom.xml
<?xml version="1.0" encoding="UTF-8"?><project>
<modelVersion>4.0.0</modelVersion>
<groupId>HibernateCache</groupId>
<artifactId>HibernateCache</artifactId>
<version>0.0.1-SNAPSHOT</version>
<description></description>
<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>ejb3-persistence</artifactId>
<version>1.0.1.GA</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-annotations</artifactId>
<version>3.3.1.GA</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.10</version>
</dependency>
</dependencies>
</project>
Code language: HTML, XML (xml)
We are not going to use hibernate mapping files or hbm files as we will map the model using Java 5 Annotations. Delete the files employee.hbm.xml
and meeting.hbm.xml
.
We will update Employee and Meeting model classes and add Annotations to map them with database table.
File: Employee.java
package net.viralpatel.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="EMPLOYEE")
public class Employee {
@Id
@Column(name="EMPLOYEE_ID")
@GeneratedValue
private Long employeeId;
@Column(name="FIRSTNAME")
private String firstname;
@Column(name="LASTNAME")
private String lastname;
@ManyToMany(cascade = {CascadeType.ALL})
@JoinTable(name="EMPLOYEE_MEETING",
joinColumns={@JoinColumn(name="EMPLOYEE_ID")},
inverseJoinColumns={@JoinColumn(name="MEETING_ID")})
private Set<Meeting> meetings = new HashSet<Meeting>();
public Employee() {
}
public Employee(String firstname, String lastname) {
this.firstname = firstname;
this.lastname = lastname;
}
// Getter and Setter methods
}
Code language: Java (java)
File: Meeting.java
package net.viralpatel.hibernate;
import java.util.Date;
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="MEETING")
public class Meeting {
@Id
@Column(name="MEETING_ID")
@GeneratedValue
private Long meetingId;
@Column(name="SUBJECT")
private String subject;
@Column(name="MEETING_DATE")
private Date meetingDate;
@ManyToMany(mappedBy="meetings")
private Set<Employee> employees = new HashSet<Employee>();
public Meeting(String subject) {
this.subject = subject;
this.meetingDate = new Date();
}
// Getter and Setter methods
}
Code language: Java (java)
Let us understand the annotations we used here to map Many to many relationship.
@ManyToMany
– Is used to create many-to-many relationship between Employee
and Meeting
entities. If the Collection is defined using generics to specify the element type, the associated target entity class does not need to be specified; otherwise it must be specified. Every many-to-many association has two sides, the owning side and the non-owning, or inverse, side. The join table is specified on the owning side. If the association is bidirectional, either side may be designated as the owning side.
Note that in above entity classes, Employee is defined as relationship owner as @JoinColumn
is define in Employee
class and mappedBy
is specified in Meeting class.
@JoinTable
– Is used to define the join table (link table) for many-to-many relationship. It is specified on the owning side of a many-to-many association, or in a unidirectional one-to-many association. In this case the join table is EMPLOYEE_MEETING
.
If the JoinTable annotation is missing, the default values of the annotation elements apply. The name of the join table is assumed to be the table names of the associated primary tables concatenated together (owning side first) using an underscore.
@JoinColumn
– Is used to define the join column (linking column) in both main tables.
Note that we have used SET to map meetings with employee and vice versa. A <set> is similar to except that it can only store unique objects. That means no duplicate elements can be contained in a set. When you add the same element to a set for second time, it will replace the old one. A set is unordered by default but we can ask it to be sorted. The corresponding type of a <set> in Java is java.util.Set
.
In previous example, the Hibernate Utility class uses Hibernate’s org.hibernate.cfg.Configuration
class to generate SessionFactory. For this example we will replace this class with org.hibernate.cfg.AnnotationConfiguration
class. Following is the code for HibernateUtil.java
class.
File: HibernateUtil.java
package net.viralpatel.hibernate;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;
public class HibernateUtil {
private static final SessionFactory sessionFactory = buildSessionFactory();
private static SessionFactory buildSessionFactory() {
try {
// Create the SessionFactory from hibernate.cfg.xml
return new AnnotationConfiguration().configure()
.buildSessionFactory();
} catch (Throwable ex) {
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
}
Code language: Java (java)
Edit Hibernate configuration file (hibernate.cfg.xml) and add mappings for Employee and Meeting classes. Following is the final hibernate.cfg.xml file:
File: hibernate.cfg.xml
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/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/tutorial</property>
<property name="connection.username">root</property>
<property name="connection.password"></property>
<property name="connection.pool_size">1</property>
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="current_session_context_class">thread</property>
<property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>
<property name="show_sql">true</property>
<property name="hbm2ddl.auto">validate</property>
<mapping class="net.viralpatel.hibernate.Employee"/>
<mapping class="net.viralpatel.hibernate.Meeting"/>
</session-factory>
</hibernate-configuration>
Code language: HTML, XML (xml)
Execute following Main class to test Many-to-many relational mapping using Annotation.
File: Main.java
package net.viralpatel.hibernate;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
public class Main {
public static void main(String[] args) {
SessionFactory sf = HibernateUtil.getSessionFactory();
Session session = sf.openSession();
session.beginTransaction();
Meeting meeting1 = new Meeting("Quaterly Sales meeting");
Meeting meeting2 = new Meeting("Weekly Status meeting");
Employee employee1 = new Employee("Sergey", "Brin");
Employee employee2 = new Employee("Larry", "Page");
employee1.getMeetings().add(meeting1);
employee1.getMeetings().add(meeting2);
employee2.getMeetings().add(meeting1);
session.save(employee1);
session.save(employee2);
session.getTransaction().commit();
session.close();
}
}
Code language: Java (java)
Output:
Hibernate: insert into EMPLOYEE (firstname, lastname) values (?, ?)
Hibernate: insert into MEETING (SUBJECT, MEETING_DATE) values (?, ?)
Hibernate: insert into MEETING (SUBJECT, MEETING_DATE) values (?, ?)
Hibernate: insert into EMPLOYEE (firstname, lastname) values (?, ?)
Hibernate: insert into EMPLOYEE_MEETING (EMPLOYEE_ID, MEETING_ID) values (?, ?)
Hibernate: insert into EMPLOYEE_MEETING (EMPLOYEE_ID, MEETING_ID) values (?, ?)
Hibernate: insert into EMPLOYEE_MEETING (EMPLOYEE_ID, MEETING_ID) values (?, ?)
Code language: SQL (Structured Query Language) (sql)
Hibernate-many-to-many-annotation.zip (8 KB)
Java URL Encoder/Decoder Example - In this tutorial we will see how to URL encode/decode…
Show Multiple Examples in OpenAPI - OpenAPI (aka Swagger) Specifications has become a defecto standard…
Local WordPress using Docker - Running a local WordPress development environment is crucial for testing…
1. JWT Token Overview JSON Web Token (JWT) is an open standard defines a compact…
GraphQL Subscription provides a great way of building real-time API. In this tutorial we will…
1. Overview Spring Boot Webflux DynamoDB Integration tests - In this tutorial we will see…
View Comments
Thanks a lot. Nice tutorial.
I've done a 'many-to-many' relationship with both sides as a owner. Is this correct?
Hi , Can any body give me an example for fetching the data of both tables using Join table?
How to write the Query?
Thanks in advance,
Chandu Sannamuri
Great article. I had one question though. In my EMPLOYEE_MEETING table I have few other attributes. How do I handle these attributes? These are the columns
EmployeeId
MeetingId
EmployeeResponse
Create something like
EmployeeMeetingEvent
hasOne meeting
hasOne event
all other fields you need
Hi,
When i update the employee_meeting table from employee side, it just replace the old value with the same employee_id and enter the new one. So, is this the behavior of many to many or i did something wrong.
Hi,
I like your tutorial. can you please extend the tutorial with many to many with extra columns. thanks in advance.
Thanks you very much for this tutorial.
Hi all i am also use same procedure in my project. but problem is when i want to insert the data in mapping table for same employee,hibernate 1st delete the exiting relation for same employee then insert the new mapping.means 1st hibernate run the delete query for exit customer then insert mapping employee id and meeting i..
Please anyone help me.
Thanks for this best tutorial
it works fine but when i try to update row (session.update) it give me this exception
Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1; nested exception is org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1
thanks for this tutorial!! but would you please transform this tutorial to a web based application? Thanks!!