Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Many to Many hibernate inverse side ignored

Hi am reading the hibernate documentation.

http://docs.jboss.org/hibernate/annotations/3.5/reference/en/html/entity.html

A many-to-many association is defined logically using the @ManyToMany annotation. You also have to describe the association table and the join conditions using the @JoinTable annotation. If the association is bidirectional, one side has to be the owner and one side has to be the inverse end (ie. it will be ignored when updating the relationship values in the association table):

I understand everything but the last

(ie. it will be ignored when updating the relationship values in the association table).

What does this mean? Example?

like image 858
pethel Avatar asked Sep 23 '12 13:09

pethel


2 Answers

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.

like image 62
JB Nizet Avatar answered Oct 17 '22 04:10

JB Nizet


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;
}
like image 39
termil0r Avatar answered Oct 17 '22 03:10

termil0r