【问题标题】:Why the Cascading persist does not work with many-to-many relationship in Hibernate?为什么级联持久化不适用于 Hibernate 中的多对多关系?
【发布时间】:2020-11-05 13:02:44
【问题描述】:

请看以下代码 sn-ps 来自 2 个类(实体)StudentCourse

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;
   
   ...
}

驱动代码如下

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();
} 

当我运行此代码时,出现以下异常

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

所以我的怀疑是。 虽然我们在 Course 类中提到了 CascadeType.ALL,但保留课程并不会保留关联的学生。那么为什么级联在多对多的情况下不起作用?

nb:当我使用session.save() 保存两个学生对象时,在保存课程对象之前。也不例外。

【问题讨论】:

  • 你说的Nb是什么意思:当我也救了学生时,代码完美运行。
  • 我的意思是,在保存课程对象之前,我使用 session.save() 保存了两个学生对象。也不例外。
  • @jithinMV 你在Course里面有courses
  • @SternK 对不起,代码编辑不正确。现在已更新。感谢您指出这一点。

标签: java hibernate orm many-to-many cascade


【解决方案1】:
  1. 您应该仅在 @ManyToMany 关联的拥有方使用 @JoinTable 注释。
@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. 如果使用双向关联,则应注意关联双方的同步。因此,您应该将以下方法添加到您的 Course 实体中:
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 );
  }

  // ...
}

然后你就可以做这样的事情了:

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. 正如documentation 中提到的:

对于@ManyToMany 关联,REMOVE 实体状态转换没有意义级联,因为它会传播到链接表之外。由于另一端可能被父端的其他实体引用,因此自动删除可能以ConstraintViolationException 结束。

这就是为什么您应该避免在@ManyToMany 中使用cascade = CascadeType.ALL

【讨论】:

  • 感谢您的解释。我试过你提到的代码。但它只是将课程保存到数据库中,但仍然没有通过级联保存课程的关联学生。
  • 它应该可以工作。您是否使用了与我的回答中提到的完全相同的映射,包括辅助方法?
  • 是的,我做到了。我在浏览同一个问题时发现了一些东西。当我们使用 session.save() 时,级联发生在休眠状态下的 SAVE_UPDATE 级联类型中。不在 JPA 内。所以要么我们必须添加 CascadeType。 ALL 并使用 session.save() 或者我们必须使用 session.persist() 代替。无论如何,前一个不是一个好习惯,因为正如你所说,它还将包括 REMOVE,所以我们可以使用后一个,它也很有效
  • 可能取决于休眠版本。我已经用进一步的session.save(course) 测试了这个映射,它适用于 Hibernate 5.4.10.Final
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-11-28
  • 2016-05-03
  • 2011-03-13
  • 2017-01-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多