In this tutorial, we will learn about how to use Hibernate Many-To-Many Bidirectional mapping using annotation based configuration.
In Many-To-Many association, an extra table is used (known as Joined table) whose primary key is the combination of primary key of both the associated tables.In other words there is a foreign key association between the joined table and the associated tables.
Schema Setup of ManyToMany bidirectional is same as of ManyToMany Unidirectional. We are discussing an example of Student and Subject relationship. A student can enroll for multiple subject. And a subject can have multiple students enrolled for it. We are considering Bidirectional mapping, means both side navigation is possible. Let’s get going.
Following technologies being used:
Let’s begin.
Open MySQL terminal / workbench terminal and execute following MySQL script :
create table STUDENT ( student_id BIGINT NOT NULL AUTO_INCREMENT, first_name VARCHAR(30) NOT NULL, last_name VARCHAR(30) NOT NULL, PRIMARY KEY (student_id) ); create table SUBJECT ( subject_id BIGINT NOT NULL AUTO_INCREMENT, name VARCHAR(30) NOT NULL, PRIMARY KEY (subject_id) ); CREATE TABLE STUDENT_SUBJECT ( student_id BIGINT NOT NULL, subject_id BIGINT NOT NULL, PRIMARY KEY (student_id, subject_id), CONSTRAINT FK_STUDENT FOREIGN KEY (student_id) REFERENCES STUDENT (student_id), CONSTRAINT FK_SUBJECT FOREIGN KEY (subject_id) REFERENCES SUBJECT (subject_id) );
[Note that this schema is exactly same as the one for ManyToManyUniDirectional. NO CHANGE].
Here we have first created the main tables STUDENT & SUBJECT. then we have created a joined table STUDENT_SUBJECT whose primary key is the combination of primary keys of STUDENT & SUBJECT.
Please visit MySQL installation on Local PC in case you are finding difficulties in setting up MySQL locally.
Following will be the final project structure:
Following is the updated minimalistic pom.xml
<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>com.websystique.hibernate</groupId> <artifactId>ManyToManyBiDirectional</artifactId> <version>1.0.0</version> <packaging>jar</packaging> <name>ManyToManyBiDirectional</name> <properties> <hibernate.version>4.3.6.Final</hibernate.version> <mysql.connector.version>5.1.31</mysql.connector.version> </properties> <dependencies> <!-- Hibernate --> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>${hibernate.version}</version> </dependency> <!-- MySQL --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql.connector.version}</version> </dependency> </dependencies> <build> <pluginManagement> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.2</version> <configuration> <source>1.6</source> <target>1.6</target> </configuration> </plugin> </plugins> </pluginManagement> </build> </project>
On saving pom.xml with above content, Eclipse will download all the dependencies.
Model class Student & Subject are simple POJO class which is annotated with JPA annotations to map it to a database tables(created in step 1).
package com.websystique.hibernate.model; import java.util.ArrayList; import java.util.List; 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 = "STUDENT") public class Student { @Id @GeneratedValue @Column(name = "STUDENT_ID") private long id; @Column(name = "FIRST_NAME") private String firstName; @Column(name = "LAST_NAME") private String lastName; @ManyToMany(cascade = CascadeType.ALL) @JoinTable(name = "STUDENT_SUBJECT", joinColumns = { @JoinColumn(name = "STUDENT_ID") }, inverseJoinColumns = { @JoinColumn(name = "SUBJECT_ID") }) private List<Subject> subjects = new ArrayList<Subject>(); public Student() { } public Student(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } public long getId() { return id; } public void setId(long id) { this.id = id; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public List<Subject> getSubjects() { return subjects; } public void setSubjects(List<Subject> subjects) { this.subjects = subjects; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + (int) (id ^ (id >>> 32)); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (!(obj instanceof Student)) return false; Student other = (Student) obj; if (id != other.id) return false; return true; } @Override public String toString() { return "Student [id=" + id + ", firstName=" + firstName + ", lastName=" + lastName + "]"; } }
package com.websystique.hibernate.model; import java.util.ArrayList; import java.util.List; 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 = "SUBJECT") public class Subject { @Id @GeneratedValue @Column(name = "SUBJECT_ID") private long id; @Column(name = "NAME") private String name; @ManyToMany(mappedBy="subjects") private List<Student> students = new ArrayList<Student>(); public Subject(){ } public Subject(String name){ this.name = name; } public long getId() { return id; } public void setId(long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public List<Student> getStudents() { return students; } public void setStudents(List<Student> students) { this.students = students; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + (int) (id ^ (id >>> 32)); result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (!(obj instanceof Subject)) return false; Subject other = (Subject) obj; if (id != other.id) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } @Override public String toString() { return "Subject [id=" + id + ", name=" + name + "]"; } }
Only change in this relationship( ManyToMany Bidirectional) and ManyToMany Unidirectional is that, in the Subject class we have added following property.
@ManyToMany(mappedBy="subjects") private List<Student> students = new ArrayList<Student>();
Nothing else changes.We added this property in Subject class to make the relationship bidirectional.You can now navigate from Subject to Student.mappedBy
attribute tells that this is the inverse side of relationship which is managed by “subjects” property of Student annotated with @JoinColumn
.
If you prefer to recall (as in Unidirectional) , owner side of relationship is defined in Student class as follows
@ManyToMany(cascade = CascadeType.ALL) @JoinTable(name = "STUDENT_SUBJECT", joinColumns = { @JoinColumn(name = "STUDENT_ID") }, inverseJoinColumns = { @JoinColumn(name = "SUBJECT_ID") }) private List<Subject> subjects = new ArrayList<Subject>();
@ManyToMany
indicates that there is a Many-to-Many relationship between Student and subject. A Student can enroll for multiple subjects, and a subject can have multiple students enrolled.Notice cascade = CascadeType.ALL
, with cascading while persisting (update/delete) Student tuples, subjects tuples will also be persisted (updated/deleted).
@JoinTable
indicates that there is a link table which joins two tables via containing there keys.This annotation is mainly used on the owning side of the relationship.joinColumns
refers to the column name of owning side(STUDENT_ID of STUDENT), and inverseJoinColumns
refers to the column of inverse side of relationship(SUBJECT_ID of SUBJECT).Primary key of this joined table is combination of STUDENT_ID & SUBJECT_ID.
One important remark : In case of *Many* association, always override hashcode and equals method which are looked by hibernate when holding entities into collections.
We need to inform hibernate about how to connect to database, which database dialect we will be using so that hibernate can generate the instruction specific to that database.
We define all these information in hibernate.cfg.xml
. Create this file with below content and save it in src/main/resources
folder.
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-configuration SYSTEM "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property> <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property> <property name="hibernate.connection.username">myuser</property> <property name="hibernate.connection.password">mypassword</property> <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/websystique</property> <property name="show_sql">true</property> <property name="format_sql">false</property> <mapping class="com.websystique.hibernate.model.Student"/> <mapping class="com.websystique.hibernate.model.Subject"/> </session-factory> </hibernate-configuration>
This class is well-known in hibernate community, and used for configuring hibernate on startup and managing session factory.
package com.websystique.hibernate; import org.hibernate.SessionFactory; import org.hibernate.cfg.AnnotationConfiguration; public class HibernateUtil { private static final SessionFactory sessionFactory; static{ try{ sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory(); }catch (Throwable ex) { System.err.println("Session Factory could not be created." + ex); throw new ExceptionInInitializerError(ex); } } public static SessionFactory getSessionFactory() { return sessionFactory; } }
package com.websystique.hibernate; import org.hibernate.Session; import com.websystique.hibernate.model.Student; import com.websystique.hibernate.model.Subject; public class HibernateStandAlone { public static void main(String[] args) { Student student1 = new Student("Sam","Disilva"); Student student2 = new Student("Joshua", "Brill"); Subject subject1 = new Subject("Economics"); Subject subject2 = new Subject("Politics"); Subject subject3 = new Subject("Foreign Affairs"); //Student1 have 3 subjects student1.getSubjects().add(subject1); student1.getSubjects().add(subject2); student1.getSubjects().add(subject3); //Student2 have 2 subjects student2.getSubjects().add(subject1); student2.getSubjects().add(subject2); Session session = HibernateUtil.getSessionFactory().openSession(); session.beginTransaction(); session.persist(student1); session.persist(student2); session.getTransaction().commit(); session.close(); } }
Here you can see that we have set the subjects property of Student class, and just persisted Student objects.Thanks to Cascade attribute, Subject tubles will be persisted automatically. And due to the annotation mapping we have done above in student class, a new tuple will be created in joined table (STUDENT_SUBJECT) for each combination of student & subject item we dealt with in main program.
Execute above class as Java application. You will see following output
Hibernate: insert into STUDENT (FIRST_NAME, LAST_NAME) values (?, ?) Hibernate: insert into SUBJECT (NAME) values (?) Hibernate: insert into SUBJECT (NAME) values (?) Hibernate: insert into SUBJECT (NAME) values (?) Hibernate: insert into STUDENT (FIRST_NAME, LAST_NAME) values (?, ?) Hibernate: insert into STUDENT_SUBJECT (STUDENT_ID, SUBJECT_ID) values (?, ?) Hibernate: insert into STUDENT_SUBJECT (STUDENT_ID, SUBJECT_ID) values (?, ?) Hibernate: insert into STUDENT_SUBJECT (STUDENT_ID, SUBJECT_ID) values (?, ?) Hibernate: insert into STUDENT_SUBJECT (STUDENT_ID, SUBJECT_ID) values (?, ?) Hibernate: insert into STUDENT_SUBJECT (STUDENT_ID, SUBJECT_ID) values (?, ?)
Below is the snapshot of MySQL database after execution of above program.
That’s it.
If you like tutorials on this site, why not take a step further and connect me on Facebook , Google Plus & Twitter as well? I would love to hear your thoughts on these articles, it will help improve further our learning process.
In this post we will be developing a full-blown CRUD application using Spring Boot, AngularJS, Spring Data, JPA/Hibernate and MySQL,…
Spring Boot complements Spring REST support by providing default dependencies/converters out of the box. Writing RESTful services in Spring Boot…
Being able to start the application as standalone jar is great, but sometimes it might not be possible to run…
Spring framework has taken the software development industry by storm. Dependency Injection, rock solid MVC framework, Transaction management, messaging support,…
Let's secure our Spring REST API using OAuth2 this time, a simple guide showing what is required to secure a…
This post shows how an AngularJS application can consume a REST API which is secured with Basic authentication using Spring…
View Comments
For more information and examples checkout below link
https://codedeal.wixsite.com/codedeal/single-post/Hibernate-One-To-One-Relationship-Using-Annotations-Bidirectional
hi,
how to get all list subject id on controller with spring ?
@RequestMapping(value = "/save", method = RequestMethod.POST)
public ModelAndView addUser(@ModelAttribute("student") Student student, RedirectAttributes redirectAttributes,Model model) {
//TODO Harus dapat value Vendor
//DocumentsServices
Student student = studentService.getstudent(id);
Subject subject = subjectService.getsubject(id);
userService.addUser(user);
//Save value ID Folder dari Document Service
ModelAndView modelAndView = new ModelAndView("redirect:/admin/role/object");
return modelAndView;
}
Hi Kurniawan, i would recommend this post to understand how it could be done.
i am new learn hibernate, how about many to many without abstractDao and enum UserProfileType ?
Hi Kurniawan, it's not hibernate specific but pure Java, so you can remove the inheritance and put the stuff right there in your class, remove any property you don't want to include.
yup i try to remove UserProfileType, and i change all property "type" with "name".
but why not show in form:select ?
Step 5
instead of
Thanks Marc, Indeed it was a typo, just corrected it. BTW, the download itself is fine.