web-dev-qa-db-ja.com

休止状態の多対多のカスケード削除

データベースに3つのテーブルがあります:StudentsCoursesStudents_Courses

学生は複数のコースを持つことができ、コースは複数の学生を持つことができます。 StudentsCoursesの間には多対多の関係があります。

Coursesテーブルにプロジェクトとコースを追加した3つのケースがあります。

  • (a)ユーザーを追加すると、保存されますが、
  • (b)学生のコースを追加すると、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;
    }

    }

説明したことをどのように達成できますか?ウェブ上で妥当なドキュメントを見つけることができませんでした。

22
Sushant Gupta

同様のシナリオで正しいマッピングを見つけました(そして大規模なケースで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がそれらを独自に処理します。

16
Pace

削除する必要があるだけですcascade = CascadeType.ALL inStudentクラスのみCoursesクラス

そして、以下のコードを追加しますcascade = {CascadeType.PERSIST、CascadeType.MERGE、CascadeType.DETACH} ..

所有者クラスのレコードを削除している間、非所有者のレコードは削除されません。

この後、削除すると、Studentテーブルとstudent_courseからのみ削除されます。コーステーブルのデータは変わりません。

0
Adit choudhary