Many to Many hibernate inverse side ignored

一曲冷凌霜 提交于 2019-11-28 10:06:49

Suppose you have the following entities:

@Entity
public class Student {
    @ManyToMany
    private Set<Course> courses;
    ...
}

@Entity
public class Course {
    @ManyToMany(mappedBy = "courses")
    private Set<Student> students;
    ...
}

The owner side is Student (because it doesn't have the mappedBy attribute). The inverse side is Course ((because it has the mappedBy attribute).

If you do the following:

Course course = session.get(Course.class, 3L);
Student student = session.get(Student.class, 4L);
student.getCourses().add(course);

Hibernate will add an entry for student 4 and course 3 in the join table because you updated the owner side of the association (student.courses).

Whereas if you do the following:

Course course = session.get(Course.class, 3L);
Student student = session.get(Student.class, 4L);
course.getStudents().add(student);

nothing will happen, because uou updated the inverse side of the association (course.students), but neglected to updated the owner side. Hibernate only considers the owner side.

To make it work both ways you need to have two separate relationships between your entities. This can be represented by one join table in database but by default it will be represented by two so you have to explicitly say you want one join table.

I will demonstrate it using previously mentioned model of Student and Course.

@Entity
public class Student {
    @ManyToMany
    @JoinTable(name = "student_course",
               joinColumns = {@JoinColumn(name = "courses_id")},
               inverseJoinColumns = {@JoinColumn(name = "students_id")})
    private Set<Course> courses;
    ...
}

@Entity
public class Course {
    @ManyToMany
    @JoinTable(name = "student_course",
               joinColumns = {@JoinColumn(name = "students_id")},
               inverseJoinColumns = {@JoinColumn(name = "courses_id")})
    private Set<Student> students;
    ...
}

In the above example we have 2 relationships with each side of Student<->Course relationship being owner of one relation. So this solves the problem of saving changes to database only on the owner side since each side is owner of one relation.

But we have to keep in mind one fact that after saving data, relationship collections will NOT be reloaded from database so programmer need to handle relationship collections by himself. By saying this I want to say that the easiest way is to modify setters for relationship collections to rebuild cycle between entities like this:

public void setCourses(Set<Course> courses) {
    for(Course c : courses) {
        if (!c.getStudents().contains(this)) {
            c.getStudents().add(this);
        }
    }
    this.courses = courses;
}

public void setStudents(Set<Student> students) {
    for(Student s : students) {
        if (!s.getCourses().contains(this)){
            s.getCourses().add(this);
        }
    }
    this.students = students;
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!