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



由于某些原因,我无法删除属于多对多关系的对象。我得到以下错误:

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)

以下是我的三个类,它们映射了多对多关系。从本质上讲,Doctor具有许多PatientsAppointmentRequest&反之亦然以下是的分类

医生

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

患者

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

应用程序请求

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

医生删除方法

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();
}
}

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

这就像有东西通过弹性橡胶绑在窗户上,然后戳它,希望它会掉下来。Hibernate很聪明,它可以让你免于做无意义的事情,它告诉你该做什么

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

在执行appointment.setDoctor(null);时,它将从集合中删除对象(仅在java中,因为您没有显式或隐式更新appointment)。您在doctor上有CascadeType.PERSIST,这意味着当hibernate要提交事务时,它会发现appointment与您刚刚删除的doctor有关联从表中删除doctor,hibernate必须去创建相同的doctor因为您没有告诉他在appointment中进行适当的更改,因为他遵循您设置的所有实体规则。既然hibernate很聪明,他知道这一点,他会为你破例说不要做矛盾修辞法,做正确的事。

现在有不止一个解决方案,我可以想到这里

  1. AppointmentRequest中的getDoctor()上使用cascade={CascadeType.PERSIST,CascadeType.REMOVE}cascade=CascadeType.ALL

  2. 如hibernate文档中所述

    在@ManyToOne或@ManyToMany协会。Cascade通常对@OneToOne和@一对多关联。

    getDoctor中删除级联

  3. 由于您在指定了cascadegetDoctor()上有FetchType.EAGER,所以我解释hibernate的行为有点复杂,但在他们使用FetchType.LAZY解决的这个问题中,我不确定它是否适用于您。

  4. 你可以在所有有这个医生的AppointmentRequest上做session.saveOrUpdate(appointment),然后进行session.delete(doctor);

希望这能解决你的问题。

最新更新