简体   繁体   中英

Why the Cascading persist does not work with many-to-many relationship in Hibernate?

Please look at the following code snippets from 2 classes (Entities) Student and Course

public class Student {
   ...

   @ManyToMany(cascade = CascadeType.ALL)
   @JoinTable(name = "course_student",
        joinColumns = @JoinColumn(name="student_id"),
        inverseJoinColumns = @JoinColumn(name="course_id")
   )
   private List<Course> courses;
   
   ...
}
    
public class Course {
   ...
   
   @ManyToMany(cascade = CascadeType.ALL)
   @JoinTable(name = "course_student",
        joinColumns = @JoinColumn(name="course_id"),
        inverseJoinColumns = @JoinColumn(name="student_id")
   )
   private List<Student> students;
   
   ...
}

and the driver code is as follows

try {
    session.beginTransaction();
    Course course = new Course("Ukulele master class");
    Student student1 = new Student("Jishnu","M V","jishnumv@gmail.com");
    Student student2 = new Student("Praveen","M V","praveenmv@gmail.com");
    course.add(student1);
    course.add(student2);
    session.save(course);
    session.getTransaction().commit();
} 

When I run this code I am getting the following exception

Exception in thread "main" java.lang.IllegalStateException: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.jithin.entity.Student

So my doubt is. persisting the Course does not persist the associated Student(s) even though we have mentioned CascadeType.ALL in the Course class. So why the cascading is not working in the case of many to many?

nb: When I saved both the student objects using session.save() , before saving the course object. There were no exception.

  1. You should use @JoinTable annotation only on the owning side of the @ManyToMany association.
@Entity
public class Student {

    @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
    @JoinTable(name = "course_student",
            joinColumns = @JoinColumn(name="student_id"),
            inverseJoinColumns = @JoinColumn(name="course_id"))
    private List<Course> courses;
   
   // ...
}

@Entity
public class Course {

    @ManyToMany(mappedBy = "courses", cascade = {CascadeType.PERSIST, CascadeType.MERGE})
    private List<Student> students;

   // ...
}
  1. If you use bidirectional association you should take care about synchronization of both sides of association. So, you should add the following methods to your Course entity:
public class Course {

  public void addStudent(Student student) {
     students.add(student);
     student.getCourses().add( this );
  }

  public void removeStudent(Student student) {
     students.remove(student);
     student.getCourses().remove( this );
  }

  // ...
}

and then you will be able to do something like this:

Course course = new Course("Ukulele master class");
Student student1 = new Student("Jishnu","M V","jishnumv@gmail.com");
Student student2 = new Student("Praveen","M V","praveenmv@gmail.com");
course.addStudent(student1);
course.addStudent(student2);
session.save(course);
  1. As it mentioned in the documentation :

For @ManyToMany associations, the REMOVE entity state transition doesn't make sense to be cascaded because it will propagate beyond the link table. Since the other side might be referenced by other entities on the parent-side, the automatic removal might end up in a ConstraintViolationException .

That is why you should avoid to use cascade = CascadeType.ALL in the @ManyToMany .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM