it-swarm.com.de

Hibernate Many-to-Many-Kaskadierendes Löschen

Ich habe 3 Tabellen in meiner Datenbank: Students, Courses und Students_Courses

Studenten können mehrere Kurse haben und Kurse können mehrere Studenten haben. Es gibt eine Viele-zu-Viele-Beziehung zwischen Students und Courses.

Ich habe 3 Fälle für mein Projekt und meine Kurse zu meiner Courses-Tabelle hinzugefügt.

  • (a) Wenn ich einen Benutzer hinzufüge, wird es gut gespeichert. 
  • (b) Wenn ich Kurse für den Studenten hinzufüge, werden neue Zeilen in User_Courses erstellt - wiederum erwartetes Verhalten.
  • (c) Wenn ich versuche, den Studenten zu löschen, werden die entsprechenden Datensätze in Students und Students_Courses gelöscht, es werden jedoch auch Courses-Datensätze gelöscht, was nicht erforderlich ist. Auch wenn ich keinen Benutzer in einem Kurs habe, möchte ich, dass der Kurs dort ist. 

Im Folgenden finden Sie meinen Code für Tabellen und Anmerkungsklassen.

    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`)
)

Dies ist der von Hibernate generierte Java-Code:

@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;
    }

    }

Wie kann ich erreichen, was ich beschrieben habe? Ich konnte keine vernünftige Dokumentation im Internet finden.

20
Sushant Gupta

Ich habe in einem ähnlichen Szenario die richtige Zuordnung gefunden (und diese mit JUnit mit einem umfangreichen Fall getestet). Ich glaube nicht, dass ich Testcode veröffentlichen werde, da es lange dauern würde, sich an dieses Beispiel anzupassen. Wie auch immer, der Schlüssel ist:

  • Verwenden Sie das mappedBy-Attribut nicht für die Anmerkungen. Verwenden Sie Join-Spalten
  • Listen Sie das mögliche CascadeTypes mit Ausnahme von REMOVE auf.

Im Beispiel von 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<>();

Umfangreiche JUnit-Tests haben Folgendes bestätigt:

  • Ich kann den Schülern Kurse hinzufügen und umgekehrt einwandfrei
  • Wenn ich einen Kurs von einem Studenten entferne, wird der Kurs nicht gelöscht
  • Und umgekehrt
  • Wenn ich einen Studenten entferne, werden alle Kurse getrennt, aber sie bleiben (für andere Studenten) in der Datenbank gespeichert
  • Und umgekehrt

Basierend auf dem, was Sie mir gesagt haben, möchten Sie nicht cascade = CascadeType.ALL für die getCourseses-Methode in Student. Beachten Sie, dass Hibernate-Kaskaden nicht mit Datenbankkaskaden identisch sind. Auch wenn Sie keine Kaskaden haben, löscht Hibernate den Datensatz "Students_Courses".

Wenn Sie eine Operation für eine Entität aufrufen und diese Operation in der Kaskadenliste aufgeführt ist, können Sie die Hibernate-Kaskaden am besten für alle untergeordneten Entitäten aufrufen.

Wenn Sie beispielsweise delete bei Student aufrufen, da sich delete in der Kaskadenliste für Kurse befindet, ruft Hibernate delete für jede der von diesem Schüler referenzierten Course-Entitäten auf. Deshalb sehen Sie, dass die Kursprogramme verschwinden.

Machen Sie sich keine Sorgen um Datenbankkaskaden, Hibernate kümmert sich alleine darum.

16
Pace

Sie müssen nur cascade = CascadeType.ALL in der Klasse Student entfernen, nur dass in der Klasse Courses keine Änderungen erforderlich sind

und fügen Sie den folgenden Code hinzu cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.DETACH} ..

Dies bedeutet, dass beim Löschen des Besitzerklassendatensatzes ein Nicht-Besitzerdatensatz nicht gelöscht wird.

Nach dem Löschen wird es nur aus der Student-Tabelle gelöscht, und die Kurstabelle student_course . Bleibt unverändert.

0
Adit choudhary