データベースに3つのテーブルがあります:Students
、Courses
、Students_Courses
学生は複数のコースを持つことができ、コースは複数の学生を持つことができます。 Students
とCourses
の間には多対多の関係があります。
Courses
テーブルにプロジェクトとコースを追加した3つのケースがあります。
User_Courses
に新しい行が作成されます-再び、予想される動作です。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;
}
}
説明したことをどのように達成できますか?ウェブ上で妥当なドキュメントを見つけることができませんでした。
同様のシナリオで正しいマッピングを見つけました(そして大規模なケースで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テストにより、次のことが確認されました。
StudentのgetCoursesesメソッドでcascade = CascadeType.ALLを使用したくないと言ったことに基づきます。 Hibernateカスケードはデータベースカスケードと同じではないことに注意してください。カスケードがない場合でも、HibernateはStudents_Coursesレコードを削除します。
Hibernateカスケードを考える最良の方法は、エンティティで操作を呼び出し、その操作がカスケードリストにリストされている場合、その操作はすべての子エンティティで呼び出されることです。
たとえば、Studentでdeleteを呼び出すと、削除はコースのカスケードリストにあるため、Hibernateはその学生が参照する各コースエンティティでdeleteを呼び出します。そのため、コースの記録が消えているのが見えます。
データベースのカスケードを心配する必要はありません。Hibernateがそれらを独自に処理します。
削除する必要があるだけですcascade = CascadeType.ALL inStudentクラスのみCoursesクラス
そして、以下のコードを追加しますcascade = {CascadeType.PERSIST、CascadeType.MERGE、CascadeType.DETACH} ..
所有者クラスのレコードを削除している間、非所有者のレコードは削除されません。
この後、削除すると、Studentテーブルとstudent_courseからのみ削除されます。コーステーブルのデータは変わりません。