Hibernate ManyToMany删除的对象将通过级联重新保存

use*_*382 1 java eclipse hibernate jpa

出于某种原因,我无法删除属于多对多关系的对象.我收到以下错误:

Exception in thread "main" org.hibernate.ObjectDeletedException: deleted object would be re-saved by cascade (remove deleted object from associations): [edu.cs157b.hibernate.AppointmentRequest#11]
    at org.hibernate.internal.SessionImpl.forceFlush(SessionImpl.java:1232)
Run Code Online (Sandbox Code Playgroud)

这是我的三个类,它们映射了多对多的关系.从本质上讲,Doctor有很多Patients通过AppointmentRequest,反之亦然.这是课程

医生

package edu.cs157b.hibernate;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.*;

@Entity
@Table(name="DOCTOR_INFO")
@NamedQueries (
    {
        @NamedQuery(name = "Doctor.getAll", query = "from Doctor"),
        @NamedQuery(name = "Doctor.findByName", query = "from Doctor where name = :name")
    }
)
public class Doctor implements Person {

    private int id;
    private String name;
    private Specialty specialty;
    private List<AppointmentRequest> appointmentRequests = new ArrayList<AppointmentRequest>();

    @Id
    @GeneratedValue
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    @Column(unique=true)
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @ManyToOne (fetch = FetchType.EAGER, cascade= CascadeType.PERSIST) 
    @JoinColumn(name="specialty_id") 
    public Specialty getSpecialty() {
        return specialty;
    }

    public void setSpecialty(Specialty specialty) {
        this.specialty = specialty;
    }

    @OneToMany(mappedBy="doctor", targetEntity = AppointmentRequest.class, 
             fetch=FetchType.EAGER, orphanRemoval=true, cascade= CascadeType.ALL) 
    public List<AppointmentRequest> getAppointmentRequests() {
        return this.appointmentRequests;
    }

    public void setAppointmentRequests(List<AppointmentRequest> appointmentRequests) {
        this.appointmentRequests = appointmentRequests;
    }

    @Transient
    public List<Patient> getPatients() {
        List<Patient> patients = new ArrayList<Patient>();

        for(AppointmentRequest appointment:appointmentRequests) {
            patients.add(appointment.getPatient());
        }
        return patients;
    }
}
Run Code Online (Sandbox Code Playgroud)

患者

package edu.cs157b.hibernate;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.*;

@Entity
@Table(name="PATIENT_INFO")
@NamedQueries (
    {
        @NamedQuery(name = "Patient.getAll", query = "from Patient"),
        @NamedQuery(name = "Patient.findByName", query = "from Patient where name = :name")
    }
)
public class Patient implements Person {

    private int id;
    private String name;
    private String medical_record;
    private List<AppointmentRequest> appointmentRequests = new ArrayList<AppointmentRequest>();

    public String getMedical_record() {
        return medical_record;
    }

    public void setMedical_record(String medical_record) {
        this.medical_record = medical_record;
    }

    @Id
    @GeneratedValue
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    @Column(unique=true)
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @OneToMany(mappedBy="patient", targetEntity = AppointmentRequest.class, 
             fetch=FetchType.EAGER, orphanRemoval=true, cascade= CascadeType.ALL) 
    public List<AppointmentRequest> getAppointmentRequests() {
        return this.appointmentRequests;
    }

    public void setAppointmentRequests(List<AppointmentRequest> appointmentRequests) {
        this.appointmentRequests = appointmentRequests;
    }

    @Transient
    public List<Doctor> getDoctors() {
        List<Doctor> doctors = new ArrayList<Doctor>();

        for(AppointmentRequest appointment:appointmentRequests) {
            doctors.add(appointment.getDoctor());
        }
        return doctors;
    }
}
Run Code Online (Sandbox Code Playgroud)

ApppointmentRequest

package edu.cs157b.hibernate;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.TimeZone;

import javax.persistence.*;

import org.hibernate.annotations.Type;

import java.util.List;

@Entity
@Table(name="APPOINTMENT_REQUEST")
@NamedQueries (
    {
        @NamedQuery(name = "AppointmentRequest.getAll", query = "from AppointmentRequest"),
        @NamedQuery(name = "AppointmentRequest.findByDoctorId", query = "from AppointmentRequest where doctor_id = :doctor_id"),
        @NamedQuery(name = "AppointmentRequest.findByPatientId", query = "from AppointmentRequest where patient_id = :patient_id"),
        @NamedQuery(name = "AppointmentRequest.findByID", query = "from AppointmentRequest where id = :id")
    }
)
public class AppointmentRequest {

    private int id;

    private Doctor doctor;
    private Patient patient;
    private boolean fulfilled = false;
    private Calendar time;
    private final SimpleDateFormat timestampFormat = new SimpleDateFormat("MM/dd/yyyy h a");


    public Calendar getTime() {
        return time;
    }

    @Transient
    public String getFormattedTime() {  
        String result = timestampFormat.format(time.getTime());
        return result;
    }

    public void setTime(Calendar time) {
        this.time = time;
    }
    public boolean isFulfilled() {
        return fulfilled;
    }
    public void setFulfilled(boolean fulfilled) {
        this.fulfilled = fulfilled;
    }
    @Id
    @GeneratedValue
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }

    @ManyToOne (fetch = FetchType.EAGER, cascade= CascadeType.PERSIST) 
    @JoinColumn(name="doctor_id") 
    public Doctor getDoctor() {
        return doctor;
    }

    public void setDoctor(Doctor doctor) {
        this.doctor = doctor;
    }

    @ManyToOne (fetch = FetchType.EAGER, cascade= CascadeType.PERSIST) 
    @JoinColumn(name="patient_id") 
    public Patient getPatient() {
        return patient;
    }

    public void setPatient(Patient patient) {
        this.patient = patient;
    }    

}
Run Code Online (Sandbox Code Playgroud)

医生删除方法

public void deleteDoctor(String doctor_name) {
    Session session = sessionFactory.openSession();
    Doctor doctor = new Doctor();
    try {
        session.beginTransaction();
        Query query = session.getNamedQuery("Doctor.findByName");
        query.setString("name", doctor_name);
        doctor = (Doctor) query.uniqueResult();
        if(doctor == null) {
            throw new NullPointerException();
        }
        List<AppointmentRequest> appointments = doctor.getAppointmentRequests();
        for(AppointmentRequest appointment:appointments) {
            appointment.setDoctor(null);
        }
        session.delete(doctor);
        session.getTransaction().commit();
    }
    finally {
        session.close();
    }
}
Run Code Online (Sandbox Code Playgroud)

Yog*_*esh 7

这个异常的真正含义是你告诉Hibernate从数据库中删除对象,但同时这个对象仍然存在(这意味着仍然存在于java或数据库中)在映射集合中通过Persistent实体CascadeType.PERSIST注释了它.

这就像在窗户上用弹性橡胶捆绑的东西,然后捅它,希望它会下降.Hibernate很聪明,它可以帮助你避免做无意义的事情,它会告诉你该怎么做

已删除的对象将通过级联重新保存(从关联中删除已删除的对象)

正弦你正在做appointment.setDoctor(null);它会删除对象从集合(仅在Java中,你是不明确或隐含更新appointment).你必须CascadeType.PERSISTdoctor这意味着当Hibernate会提交它会发现交易是appointment有关联doctor刚刚删除,这意味着如果你doctor从表中删除它,hibernate必须去创建相同的,doctor因为你没有告诉他进行适当的更改,appointment因为他遵循你设置的所有实体规则.由于hibernate是聪明的,他知道这一点,他会抛出一个例外,说你不要做矛盾,做正确的事.

现在我可以在这里考虑多个解决方案

  1. 使用cascade={CascadeType.PERSIST,CascadeType.REMOVE}cascade=CascadeType.ALLgetDoctor()AppointmentRequest

  2. 由于休眠文件中提到这里

    在@ManyToOne或@ManyToMany关联上启用级联通常没有意义.Cascade通常对@OneToOne和@OneToMany关联有用.

    从中删除级联 getDoctor

  3. 既然你FetchType.EAGERgetDoctor()使用cascade指定为我解释休眠的行为,但它是有点复杂,这个问题他们已经通过解决FetchType.LAZY我不知道这是否会制定出适合您.

  4. 你可以做session.saveOrUpdate(appointment)所有AppointmentRequest有这位医生然后去的人session.delete(doctor);

希望你能解决你的问题.