级联移除关系,不带级联移除目标



我在Student_Teacher表(无实体)中有一个学生和老师之间的多对多关系。

Student:       Teacher(owning-side):   Student_Teacher
1= Tim         50= Mrs. Foo            1=   1   50
2= Ann         51= Mr. Bar             2=   1   51
3=   2   50
4=   2   51

正如您在上面看到的,每个学生目前都与每个老师有关

现在我喜欢删除 Ann,我喜欢使用数据库的级联技术从Student_Teacher表中删除条目,但我既不喜欢删除其他学生、老师或其他关系。

这是我在学生实体中拥有的:

@ManyToMany(mappedBy="students")
public Set<Teacher> getTeachers() {
return teachers;
}

这是我在教师实体中所拥有的:

@ManyToMany
@JoinTable(name="Student_Teacher", joinColumns = {
@JoinColumn(name="StudentID", referencedColumnName = "TeacherID", nullable = false)
}, inverseJoinColumns = {
@JoinColumn(name="TeacherID", referencedColumnName = "StudentID", nullable = false)
})
public Set<Student> getStudents() {
return students;
}

现在我喜欢使用数据库的delete cascade功能。我再说一遍:数据库的删除级联功能仅针对Student_Teacher表!

问题:

org.h2.jdbc.JdbcSQLException: Referentielle Integrität verletzt: "FK_43PMYXR2NU005M2VNEB99VX0X: PUBLIC.Student_Teacher FOREIGN KEY(StudentID) REFERENCES PUBLIC.Student(StudentID) (2)"
Referential integrity constraint violation: "FK_43PMYXR2NU005M2VNEB99VX0X: PUBLIC.Student_Teacher FOREIGN KEY(StudentID) REFERENCES PUBLIC.Student(StudentID) (2)"; SQL statement:
delete from "Student" where name='Ann'
at org.h2.message.DbException.getJdbcSQLException(DbException.java:345)
at org.h2.message.DbException.get(DbException.java:179)
at org.h2.message.DbException.get(DbException.java:155)
at org.h2.constraint.ConstraintReferential.checkRow(ConstraintReferential.java:425)

我不能使用的是

@ManyToMany(cascade={CascadeType.REMOVE})

因为纪录片告诉我:

(可选)必须级联到关联目标的操作。

"目标"是老师,所以这个级联会删除老师(我不喜欢删除)。

问题:

如何配置实体以仅使用数据库的级联功能删除 Ann 和关系?

概念验证:

我尝试了另一个功能,我注意到可以像这样配置外键:

@ManyToMany(cascade = { CascadeType.REMOVE })
@JoinTable(name="Student_Teacher", joinColumns = {
@JoinColumn(name="StudentID", referencedColumnName = "TeacherID", nullable = false, foreignKey=@ForeignKey(foreignKeyDefinition="FOREIGN KEY (StudentID) REFERENCES Student ON DELETE NO ACTION"))
}, inverseJoinColumns = {
@JoinColumn(name="TeacherID", referencedColumnName = "StudentID", nullable = false, foreignKey=@ForeignKey(foreignKeyDefinition="FOREIGN KEY (TeacherID) REFERENCES Teacher ON DELETE NO ACTION"))
})
public Set<Student> getStudents() {
return students;
}

问题是:这工作正常,但要触发删除Student_Teacher中的条目,我必须在两边指定@ManyToMany(cascade = { CascadeType.REMOVE })。休眠不解析foreignKeyDefinition,只看到CascadeType.REMOVE并将目标实体(和引用的学生Tim)从缓存中删除,但它们仍在数据库中!!因此,我必须在掉落后于休眠状态,重新阅读符太太和巴尔先生以及学生蒂姆的存在。

现在我喜欢使用数据库的删除级联功能。我 重复:数据库的删除级联功能针对 仅限Student_Teacher桌!

只需在数据库架构级别定义级联删除,数据库就会自动执行此操作。但是,如果关联的拥有端在同一个持久性上下文实例中加载/操作,则持久性上下文显然将处于不一致状态,从而导致管理拥有端时出现问题,因为 Hibernate 无法知道背后做了什么。如果启用了二级缓存,事情会变得更加复杂。

因此,您可以这样做并注意不要在同一会话中加载Teacher,但我不建议这样做,我写这篇文章只是作为对这部分问题的回答。

如何配置实体以仅删除 Ann 和关系 使用数据库的级联功能?

在 JPA/Hibernate 级别上没有这样的配置。映射中的大多数 DDL 声明仅用于自动生成架构,在涉及实体实例生命周期和关联管理时将被忽略。

我不能使用的是

@ManyToMany(cascade={CascadeType.REMOVE})

实体生命周期操作的级联和关联管理是两个完全独立的不同概念。在这里,您考虑了前者,而您需要后者。

您面临的问题是,当Teacher是拥有方时,您希望断开与Student(标有mappedBy的反面)的关联。您可以通过从与其关联的所有教师中删除学生来执行此操作,但这可能会导致加载大量数据(所有关联的教师及其所有学生)。这就是为什么为关联表引入一个单独的实体可能是一个很好的折衷方案,正如@Mark已经建议的那样,正如我在之前关于类似主题的一些答案以及其他一些潜在改进中所建议的那样。

您可以为关系创建一个新的实体TeacherStudent,然后安全地使用 CascadeType.DELETE:

@Entity
public class Student {
@OneToMany(mappedBy="student",cascade={CascadeType.REMOVE})
public Set<TeacherStudent> teacherStudents;
}
@Entity
public class Teacher {
@OneToMany(mappedBy="teacher",cascade={CascadeType.REMOVE})
public Set<TeacherStudent> teacherStudents;
}
@Entity
public class TeacherStudent {
@ManyToOne
public Teacher teacher;
@ManyToOne
public Student student;
}

您必须处理 TeacherStudent 的复合外键。你可以看看 https://stackoverflow.com/a/29116687/3670143。

关于ON DELETE CASCADE的另一个相关线程是 JPA + Hibernate:如何定义具有 ON DELETE CASCADE 的约束

正如你在上面看到的,每个学生都与每个老师有关

关键是当在"每个 A 都与每个 B 相关"的情况下,那么就没有必要有多对多表来保持这种关系。因为在这种情况下,逻辑上 A 和 B 是相互独立的。添加/删除/修改 A 对 B 没有影响,反之亦然。此行为正是您所追求的,因为您希望级联操作在关系表停止:

删除仅针对Student_Teacher表的级联功能!

关系表仅在">每个 A 都与 B 的子集相关"的情况下才有用。

因此,解决您的问题实际上是一个相当不错的问题:将Student_Teacher放下。

由于我们遇到了类似的问题,但最终以另一种方式解决了它,这是我们的解决方案:

我们替换了

@ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL) 

在我们的关系中

@ManyToMany(fetch = FetchType.LAZY, cascade = {
CascadeType.DETACH,
CascadeType.MERGE,
CascadeType.REFRESH,
CascadeType.PERSIST
})

它成功删除了关联,而无需删除链接实体。

最新更新