Engineering Full Stack Apps with Java and JavaScript
When mapping two entities, we annotate both classes as @Entity and the mapping type is specified as @OneToOne, @OneToMany, @ManyToOne or @ManyToMany over the declaration of one entity class in the other entity class. In Many-To-Many mapping, we refer to a collection/list of the entity from another entity and vice versa; and used @ManyToOne annotation on declarations of both entity collections.
We will assume that each course can have many students (many to many), and each student can do many course(many to many). We will create two entity classes, Course and Student and do a many to many mapping from course to student, and again many to many mapping from student to course. If you are not following the tutorials in order, refer to the article http://javajee.com/your-first-hibernate-program for the basic setup including hibernate configuration file. You should replace the user class in the configuration file with both course and student classes as:
<!-- Names the annotated entity class -->
<mapping class="com.javajee.hiberex.dto.Student"/>
<mapping class="com.javajee.hiberex.dto.Course"/>
Else you will get org.hibernate.MappingException: Unknown entity:…
Case 1: Many-To-Many
We will have a collection of Courses in Student, annotated using @ManyToMany:
@Entity
public class Student {
@Id @GeneratedValue
private int studId;
private String studName;
@ManyToMany
private Collection<Course> courses=new ArrayList<>();
//Getters and setters not shown here; create them manually or generate in eclipse.
}
We will have a collection of Student in Course, annotated using @ManyToMany:
@Entity
public class Course {
@Id @GeneratedValue
private int courseId;
private String courseName;
@ManyToMany
private Collection<Student> studs=new ArrayList<>();
//Getters and setters not shown here; create them manually or generate in eclipse.
}
The modified test class (CourseTest) is as follows:
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import com.javajee.hiberex.dto.Course;
import com.javajee.hiberex.dto.Student;
public class CourseTest {
public static void main(String[] args) {
Course course = new Course();
course.setCourseName("C1");
Student stud1 = new Student();
stud1.setStudName("Stud1");
stud1.getCourses().add(course);
course.getStuds().add(stud1);
Student stud2 = new Student();
stud2.setStudName("Stud2");
stud2.getCourses().add(course);
course.getStuds().add(stud2);
Configuration configuration = new Configuration();
configuration.configure();
ServiceRegistry serviceRegistry = new ServiceRegistryBuilder()
.applySettings(configuration.getProperties()).buildServiceRegistry();
SessionFactory sessionFactory = configuration.buildSessionFactory(serviceRegistry);
Session session = sessionFactory.openSession();
session.beginTransaction();
session.save(stud1);
session.save(stud2);
session.save(course);
session.getTransaction().commit();
}
}
If you execute the test class, queries executed by hibernate during its execution (as seen in console) is as follows:
Hibernate: insert into Student (studName) values (?)
Hibernate: insert into Student (studName) values (?)
Hibernate: insert into Course (courseName) values (?)
Hibernate: insert into Student_Course (Student_studId, courses_courseId) values (?, ?)
Hibernate: insert into Student_Course (Student_studId, courses_courseId) values (?, ?)
Hibernate: insert into Course_Student (Course_courseId, studs_studId) values (?, ?)
Hibernate: insert into Course_Student (Course_courseId, studs_studId) values (?, ?)
Case 2: Many-To-Many with mappedBy
In case of Many-To-One mapping, we cannot eliminate the mapping table; however in the above output there are two mapping tables. This is because when hibernate see one entity class with Many-To-One mapping, it create a mapping table and when it see the second entity class with Many-To-One mapping, it again create another mapping table. Since both mapping table are actually the same, we can tell hibernate not to create two mapping tables, but only one.
You have to use mappedBy attribute with either of the entity tables in a Many-To-Many relationship and specify the reference variable name for the current entity in the referenced entity. But we should only place the mappedBy for one of them; if you place mappedBy for both entities, you will get exception: org.hibernate.AnnotationException: Illegal use of mappedBy on both sides of the relationship:…
In our example, we have to specify mappedBy for the @ManyToMany annotation of Course class and specify the reference variable name used for the Course class in the Student class, which is course.
Our new course class is:
@Entity
public class Course {
@Id @GeneratedValue
private int courseId;
private String courseName;
@ManyToMany(mappedBy="courses")
private Collection<Student> studs=new ArrayList<>();
//Getters and setters not shown here; create them manually or generate in eclipse.
}
If you execute the same test class, queries executed by hibernate during its execution (as seen in console) is as follows:
Hibernate: insert into Student (studName) values (?)
Hibernate: insert into Student (studName) values (?)
Hibernate: insert into Course (courseName) values (?)
Hibernate: insert into Student_Course (studs_studId, courses_courseId) values (?, ?)
Hibernate: insert into Student_Course (studs_studId, courses_courseId) values (?, ?)