Hibernate 多对多级联删除
Posted
技术标签:
【中文标题】Hibernate 多对多级联删除【英文标题】:Hibernate many-to-many cascading delete 【发布时间】:2013-01-13 04:42:51 【问题描述】:我的数据库中有 3 个表:Students
、Courses
和 Students_Courses
学生可以有多个课程,课程可以有多个学生。 Students
和 Courses
之间存在多对多关系。
我的Courses
表中添加了我的项目和课程的 3 个案例。
User_Courses
中创建新行 - 这也是预期的行为。
(c) 当我试图删除学生时,它正在删除Students
和Students_Courses
中的相应记录,但它也删除了不需要的Courses
记录。即使我在课程中没有任何用户,我也希望课程在那里。
下面是我的表格和注释类代码。
CREATE TABLE `Students` (
`StudentID` INT(11) NOT NULL AUTO_INCREMENT,
`StudentName` VARCHAR(50) NOT NULL
PRIMARY KEY (`StudentID`)
)
CREATE TABLE `Courses` (
`CourseID` INT(11) NOT NULL AUTO_INCREMENT,
`CourseName` VARCHAR(50) NOT NULL
PRIMARY KEY (`CourseID`)
)
CREATE TABLE `Student_Courses` (
`StudentId` INT(10) NOT NULL DEFAULT '0',
`CourseID` INT(10) NOT NULL DEFAULT '0',
PRIMARY KEY (`StudentId`, `CourseID`),
INDEX `FK__courses` (`CourseID`),
INDEX `StudentId` (`StudentId`),
CONSTRAINT `FK__courses` FOREIGN KEY (`CourseID`) REFERENCES `courses` (`CourseID`) ON DELETE NO ACTION,
CONSTRAINT `FK_students` FOREIGN KEY (`StudentId`) REFERENCES `students` (`StudentId`)
)
这是Hibernate生成的Java代码:
@Entity
@Table(name = "Students")
public class Students implements java.io.Serializable
private Integer StudentID;
private String Students;
private Set<Courses> Courseses = new HashSet<Courses>(0);
public Students()
@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "StudentID", unique = true, nullable = false)
public Integer getStudentID()
return this.StudentID;
public void setStudentID(Integer StudentID)
this.StudentID = StudentID;
@Column(name = "Students", nullable = false, length = 50)
public String getCampaign()
return this.Students;
public void setCampaign(String Students)
this.Students = Students;
@ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JoinTable(name = "Student_Courses", joinColumns =
@JoinColumn(name = "StudentId", nullable = false, updatable = false), inverseJoinColumns =
@JoinColumn(name = "CourseID", nullable = false, updatable = false))
public Set<Courses> getCourseses()
return this.Courseses;
public void setCourseses(Set<Courses> Courseses)
this.Courseses = Courseses;
@Entity
@Table(name = "Courses")
public class Courses implements java.io.Serializable
private Integer CourseID;
private String CourseName;
private Set<Students> Studentses = new HashSet<Students>(0);
public Courses()
@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "CourseID", unique = true, nullable = false)
public Integer getCourseID()
return this.CourseID;
public void setCourseID(Integer CourseID)
this.CourseID = CourseID;
@Column(name = "CourseName", nullable = false, length = 100)
public String getCourseName()
return this.CourseName;
public void setCourseName(String CourseName)
this.CourseName = CourseName;
@ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "Courseses")
public Set<Students> getStudentses()
return this.Studentses;
public void setStudentses(Set<Students> Studentses)
this.Studentses = Studentses;
我怎样才能实现我所描述的?我在网上找不到任何合理的文档。
【问题讨论】:
FetchType.Lazy
是@ManyToMany
注解的默认值;所以你可以删除它。
【参考方案1】:
我在类似的场景中找到了正确的映射(并使用带有广泛案例的 JUnit 对其进行了测试)。我不认为我会发布测试代码,因为适应这个例子需要很长时间。无论如何,关键是:
注释不使用mappedBy
属性,使用连接列
列出可能的CascadeTypes
,不包括REMOVE
在 OP 的例子中
@ManyToMany(fetch = FetchType.LAZY,
cascade =
CascadeType.DETACH,
CascadeType.MERGE,
CascadeType.REFRESH,
CascadeType.PERSIST
,
targetEntity = Course.class)
@JoinTable(name = "XTB_STUDENTS_COURSES",
inverseJoinColumns = @JoinColumn(name = "COURSE_ID",
nullable = false,
updatable = false),
joinColumns = @JoinColumn(name = "STUDENT_ID",
nullable = false,
updatable = false),
foreignKey = @ForeignKey(ConstraintMode.CONSTRAINT),
inverseForeignKey = @ForeignKey(ConstraintMode.CONSTRAINT))
private final Set<Course> courses = new HashSet<>();
@ManyToMany(fetch = FetchType.LAZY,
cascade =
CascadeType.DETACH,
CascadeType.MERGE,
CascadeType.REFRESH,
CascadeType.PERSIST
,
targetEntity = Student.class)
@JoinTable(name = "XTB_STUDENTS_COURSES",
joinColumns = @JoinColumn(name = "COURSE_ID",
nullable = false,
updatable = false),
inverseJoinColumns = @JoinColumn(name = "STUDENT_ID",
nullable = false,
updatable = false),
foreignKey = @ForeignKey(ConstraintMode.CONSTRAINT),
inverseForeignKey = @ForeignKey(ConstraintMode.CONSTRAINT))
private final Set<Student> students = new HashSet<>();
广泛的 JUnit 测试证实:
我可以完美地向学生添加课程,反之亦然 如果我从学生那里删除课程,该课程不会被删除 反之亦然 如果我删除了一个学生,所有课程都会被分离,但它们仍会(对其他学生)保留在数据库中 反之亦然【讨论】:
如果所有的Students
都被删除了怎么办,在这种情况下是删除Courses
还是只是从关联的(student_courses
)表中删除记录?
意思是不删除空的课程。如果您想强制要求课程必须至少有一名学生,您可以在删除最后一名学生后立即删除课程【参考方案2】:
根据您告诉我的内容,您不想在 Student 的 getCourseses 方法上使用 cascade=CascadeType.ALL。请记住,Hibernate 级联与数据库级联不同。即使您没有任何级联,Hibernate 也会删除 Students_Courses 记录。
考虑 Hibernate 级联的最佳方式是,如果您对一个实体调用一个操作并且该操作列在级联列表中,那么该操作将在所有子实体上调用。
例如,当您对 Student 调用 delete 时,由于 delete 位于 Courses 的级联列表中,Hibernate 将对该学生引用的每个 Course 实体调用 delete。这就是您看到课程记录消失的原因。
不用担心数据库级联,Hibernate 会自己处理这些问题。
【讨论】:
是的,使用cascade=ALL
不是最好的解决方案。但是,答案没有提供删除桥接记录而不是课程记录的正确答案。还有I'm having the same problem, too
:-(
如果要删除桥接记录,则需要从拥有方的集合中删除实体。拥有方是没有 mappedBy 属性的一方。在上面的示例中,您希望从相应的 Students 实例中删除相应的 Courses 实例。
太好了,我还发现正确的映射涉及列出支持的级联类型。就我而言,我将它们全部列出,但删除【参考方案3】:
您只需要在 Student 类中删除 cascade = CascadeType.ALL 只在 Courses 类中不需要更改
并添加以下代码 cascade = CascadeType.PERSIST,CascadeType.MERGE,CascadeType.DETACH..
这意味着在删除所有者类记录时不会删除非所有者记录。
在此之后,在删除时它只会从 Student 表和 student_course 中删除。 课程表数据保持不变。
【讨论】:
以上是关于Hibernate 多对多级联删除的主要内容,如果未能解决你的问题,请参考以下文章