1

For some reason I can't delete an object that belongs to a many to many relationship. I get the following error:

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)

Here are my three classes that map the many to many relationship. Essentially, Doctor has many Patients through AppointmentRequest & vice versa. Here are the classes

Doctor

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

Patient

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

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

}

Doctor Delete Method

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();
    }
}
user2158382
  • 4,430
  • 12
  • 55
  • 97

1 Answers1

7

What this exception really means is you are telling Hibernate to remove object from database but at the same time this object still exist (that means still exist in java or database) in mapped collection via Persistent entity which has CascadeType.PERSIST annotated over it.

It's like having something tied through elastic rubber on the window and then poke it hoping it will drop. Hibernate is smart it is saving you from doing meaningless stuff, it tells you what to do

deleted object would be re-saved by cascade (remove deleted object from associations)

Sine you are doing appointment.setDoctor(null); it will remove object from collection (only in java as you are not explicitly or implicitly updating appointment).You have CascadeType.PERSIST on doctor that means when hibernate is going to commit the transaction it will find that appointment has association to doctor you just deleted that means if you remove that doctor from table, hibernate has to go and create same doctor as you have not told him to make appropriate changes in appointment as he follows all the entity rules set by you. Since hibernate is smart he knows this and he will throw a exception for you saying don't be an oxymoron and do the right thing.

Now there are more than one solution that I can think of here

  1. Use cascade={CascadeType.PERSIST,CascadeType.REMOVE} or cascade=CascadeType.ALL on getDoctor() in AppointmentRequest

  2. As mentioned in hibernate document here

    It doesn't usually make sense to enable cascade on a @ManyToOne or @ManyToMany association. Cascade is often useful for @OneToOne and @OneToMany associations.

    remove cascade from getDoctor

  3. Since you have FetchType.EAGER on getDoctor() with cascade specified it is little complicated for me interpret the behaviour of hibernate but in this questions they have solved by using FetchType.LAZY am not sure if it will work out for you.

  4. You can do session.saveOrUpdate(appointment) on all the AppointmentRequest which has this doctor and then go for session.delete(doctor);

Hope you this would solve your problem.

Community
  • 1
  • 1
Yogesh
  • 4,546
  • 2
  • 32
  • 41