我有2个实体,一个是学生,另一个是课程。它们以多对多双向方式相互连接。我正在执行如下代码所示的操作。
@NoArgsConstructor
@AllArgsConstructor
@Entity
@SuperBuilder
@Table(name = "_student")
@DiscriminatorValue("ent_Student")
public class Student extends User{
@JsonIgnore
@ManyToMany(cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
})
@JoinTable(name = "students_courses",
joinColumns = @JoinColumn(name = "student_id"),
inverseJoinColumns = @JoinColumn(name = "course_id")
)
private Set<Course> courses;
public Set<Course> getCourses() {
if(courses == null) {
courses = new HashSet<Course>();
}
return courses;
}
public void setCourses(Set<Course> courses) {
this.courses = courses;
}
}
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "_course")
public class Course {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
public Integer id;
@Column(unique = true)
public String courseName;
@JsonIgnore
@ManyToMany(mappedBy = "courses")
public Set<Student> students;
//getter setters
}
我有这样的@Service方法
public AssignCourseToStudentResponse assignCourseToStudent2(Integer id,Integer id2) {
AssignCourseToStudentResponse resp;
Optional<Student> st = student_Repo.findById(id);
Optional<Course> course = course_repo.findById(id2);
Student s = st.orElseThrow();
s.getCourses().add(course.get());
student_Repo.save(st.get());
Optional<Course> courseDebug = course_repo.findById(id2);
Iterator<Student> it= courseDebug.get().students.iterator();
while(it.hasNext()) {
System.out.println(it.next());
if(it.hasNext() == false) {
break;
}
}
resp = AssignCourseToStudentResponse.builder().message(ResponseMessage.SUCCESSFUL).build();
return resp;
}
当我执行上面服务部分中编写的代码时
它已按我的预期添加到表中。
但我真正的问题是,如果我从学生中删除cascade.persist,它会像以前一样将学生和课程添加到中间表中,并在它们之间创建关系。
那么,这种情况下persist的目的是什么,hibernate在后台做了什么?我的另一个问题是,我刚刚将一门课程添加到学生对象中,当我直接保存学生时,我可以从课程实体访问我的学生对象,当我仅保存学生对象时,我的学生是如何添加到课程对象中的学生集合?这不是级联的情况吗?或者在这种情况下休眠是如何工作的?我正在努力学习软件。您能更详细地解释一下吗?谢谢你
我没想到当cascade.persist不存在时数据会被添加到中间表中
cascade
注释中的@ManyToMany
属性是一个配置选项,允许将级联属性中指定的操作应用于两个关联的实体。换句话说,对您的实体进行的操作也将在关系的另一方进行。
CascadeType.PERSIST
意味着如果我们保存 Student
实体,那么所有 Course
实体也应该被保存。此级联操作从 Student
对象流向相关的 Course
对象。但是,由于 Student
和 Course
之间的关系是在单独的关联表 (students_courses
) 中管理的,因此 CascadeType.Persist
在 @ManyToMany
的上下文中没有任何意义。
这是因为
Course
实体并非真正由设置中的 Student
实体“拥有”。它们独立存在,并且只是相关联的。添加 Course
并不意味着在数据库中保存新的 Course
。
当您执行
s.getCourses().add(course.get());
并使用 student_Repo.save(st.get());
保存学生时,您实际上并未保留新的 Course
,而是标记了现有 Student
和 Course
之间的关联,这反映在连接表(students_courses
).
现在,如果您检查
Course
实体,您就能够获取关联的 Student
,因为您使用 Course
再次从数据库中获取了 course_repo.findById(id2);
。然后,Hibernate 也会从 students_courses
中获取关系,当您将 Course
添加到 Student
中时,该关系已被更新。
总之,
Cascade.Persist
在ManyToMany
关系的上下文中没有什么意义,因为持久化实体并不意味着持久化关系,并且添加的课程是关联实体,而不是Student
的直接部分。当您保存 Student
时,关系的实际持久性就会发生。
当您在
students_courses
上调用 save()
时,Hibernate 会通过将关联保存在 Student
表中来处理此问题,并在您检索 Course
时获取关联,给人一种错觉,就好像它已被级联一样。
记住,
CascadeType.PERSIST
在@OneToMany
和@OneToOne
关系中更为重要,在这些关系中,持久化实体可能需要持久化其子/后代。在 @ManyToMany
关系中,它不会持久化关联实体,只是在连接表中标记它们的关联。
我希望这有助于澄清概念!