A Guide to JPA with Hibernate (Relationship Mappings) — OneToOne , OneToMany , ManyToOne , ManyToMany

Introduction

What we will cover here:

Our Example

Relationships

One-to-Many/Many-to-One

@Entity
public class Teacher {
private String firstName;
private String lastName;
}
@Entity
public class Course {
private String title;
}
@OneToMany
private List<Course> courses;
@OneToMany
@JoinColumn(name = "TEACHER_ID", referencedColumnName = "ID")
private List<Course> courses;
insert into TEACHER(ID, LASTNAME, FIRSTNAME) values(1, 'Doe', 'Jane');insert into COURSE(ID, TEACHER_ID, TITLE) values(1, 1, 'Java 101');
insert into COURSE(ID, TEACHER_ID, TITLE) values(2, 1, 'SQL 101');
insert into COURSE(ID, TEACHER_ID, TITLE) values(3, 1, 'JPA 101');
Teacher foundTeacher = entityManager.find(Teacher.class, 1L);assertThat(foundTeacher.id()).isEqualTo(1L);
assertThat(foundTeacher.lastName()).isEqualTo("Doe");
assertThat(foundTeacher.firstName()).isEqualTo("Jane");
assertThat(foundTeacher.courses())
.extracting(Course::title)
.containsExactly("Java 101", "SQL 101", "JPA 101");

Owning Side and Bidirectionality

@ManyToOne
@JoinColumn(name = "TEACHER_ID", referencedColumnName = "ID")
private Teacher teacher;
Course foundCourse = entityManager.find(Course.class, 1L);assertThat(foundCourse.id()).isEqualTo(1L);
assertThat(foundCourse.title()).isEqualTo("Java 101");
assertThat(foundCourse.teacher().lastName()).isEqualTo("Doe");
assertThat(foundCourse.teacher().firstName()).isEqualTo("Jane");
@Entity
public class Teacher {
// ...
@OneToMany(mappedBy = "teacher")
private List<Course> courses;
}
@Entity
public class Course {
// ...

@ManyToOne
@JoinColumn(name = "TEACHER_ID", referencedColumnName = "ID")
private Teacher teacher;
}

Eager vs Lazy Loading

@OneToMany(mappedBy = "teacher", fetch = FetchType.EAGER)
private List<Course> courses;
@ManyToOne(fetch = FetchType.LAZY)
private Teacher teacher;

Optionality

Course course = new Course("C# 101");
entityManager.persist(course);
@ManyToOne(optional = false)
@JoinColumn(name = "TEACHER_ID", referencedColumnName = "ID")
private Teacher teacher;
Course course = new Course("C# 101");
assertThrows(Exception.class, () -> entityManager.persist(course));
Teacher teacher = new Teacher();
teacher.setLastName("Doe");
teacher.setFirstName("Will");
Course course = new Course("C# 101");
course.setTeacher(teacher);
entityManager.persist(course);
javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist: com.fdpro.clients.stackabuse.jpa.domain.Course
Teacher teacher = new Teacher();
teacher.setLastName("Doe");
teacher.setFirstName("Will");
entityManager.persist(teacher);
Course course = new Course("C# 101");
course.setTeacher(teacher);
entityManager.persist(course);
entityManager.flush();

Cascading Operations

@ManyToOne(optional = false, cascade = CascadeType.PERSIST)
@JoinColumn(name = "TEACHER_ID", referencedColumnName = "ID")
private Teacher teacher;

One-to-One

@Entity
public class CourseMaterial {
@Id
private Long id;
private String url;
}
@OneToOne(optional = false)
@JoinColumn(name = "COURSE_ID", referencedColumnName = "ID")
private Course course;
@OneToOne(mappedBy = "course")
private CourseMaterial material;

Many-to-Many

@ManyToMany
@JoinTable(
name = "STUDENTS_COURSES",
joinColumns = @JoinColumn(name = "COURSE_ID", referencedColumnName = "ID"),
inverseJoinColumns = @JoinColumn(name = "STUDENT_ID", referencedColumnName = "ID")
)
private List<Student> students;
Student johnDoe = new Student();
johnDoe.setFirstName("John");
johnDoe.setLastName("Doe");
johnDoe.setBirthDateAsLocalDate(LocalDate.of(2000, FEBRUARY, 18));
johnDoe.setGender(MALE);
johnDoe.setWantsNewsletter(true);
johnDoe.setAddress(new Address("Baker Street", "221B", "London"));
entityManager.persist(johnDoe);
Student willDoe = new Student();
willDoe.setFirstName("Will");
willDoe.setLastName("Doe");
willDoe.setBirthDateAsLocalDate(LocalDate.of(2001, APRIL, 4));
willDoe.setGender(MALE);
willDoe.setWantsNewsletter(false);
willDoe.setAddress(new Address("Washington Avenue", "23", "Oxford"));
entityManager.persist(willDoe);
Teacher teacher = new Teacher();
teacher.setFirstName("Jane");
teacher.setLastName("Doe");
entityManager.persist(teacher);
Course javaCourse = new Course("Java 101");
javaCourse.setTeacher(teacher);
entityManager.persist(javaCourse);
Course sqlCourse = new Course("SQL 101");
sqlCourse.setTeacher(teacher);
entityManager.persist(sqlCourse);
public class Course {    private List<Student> students = new ArrayList<>();    public void addStudent(Student student) {
this.students.add(student);
}
}
Course javaCourse = new Course("Java 101");
javaCourse.setTeacher(teacher);
javaCourse.addStudent(johnDoe);
javaCourse.addStudent(willDoe);
entityManager.persist(javaCourse);
Course sqlCourse = new Course("SQL 101");
sqlCourse.setTeacher(teacher);
sqlCourse.addStudent(johnDoe);
entityManager.persist(sqlCourse);
Course courseWithMultipleStudents = entityManager.find(Course.class, 1L);assertThat(courseWithMultipleStudents).isNotNull();
assertThat(courseWithMultipleStudents.students())
.hasSize(2)
.extracting(Student::firstName)
.containsExactly("John", "Will");

Conclusion

Software Developer