Hibernate 3.6.10 does not perform cascading delete via OneToMany JoinTable

I've been messing around with this and looking for it for 4 days and I am going crazy with how Hibernate annotations work with JPA annotations. I have two very simple objects:

Student

package com.vaannila.student;

import java.util.HashSet;
import java.util.Set;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.OneToMany;

import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.CascadeType;

@Entity
public class Student {

    @Id
    @GeneratedValue
    private long studentId;
    private String studentName;
    @OneToMany(orphanRemoval = true)
    @Cascade(CascadeType.ALL)
    @JoinTable(name = "STUDENT_PHONE", joinColumns = { @JoinColumn(name = "STUDENT_ID") }, inverseJoinColumns = { @JoinColumn(name = "PHONE_ID") })
    private Set<Phone> studentPhoneNumbers = new HashSet<Phone>(0);

    public Student() {
    }

    public Student(String studentName, Set<Phone> studentPhoneNumbers) {
        this.studentName = studentName;
        this.studentPhoneNumbers = studentPhoneNumbers;
    }

    public long getStudentId() {
        return this.studentId;
    }

    public void setStudentId(long studentId) {
        this.studentId = studentId;
    }

    public String getStudentName() {
        return this.studentName;
    }

    public void setStudentName(String studentName) {
        this.studentName = studentName;
    }

    public Set<Phone> getStudentPhoneNumbers() {
        return this.studentPhoneNumbers;
    }

    public void setStudentPhoneNumbers(Set<Phone> studentPhoneNumbers) {
        this.studentPhoneNumbers = studentPhoneNumbers;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + (int) (studentId ^ (studentId >>> 32));
        result = prime * result + ((studentName == null) ? 0 : studentName.hashCode());
        result = prime * result + ((studentPhoneNumbers == null) ? 0 : studentPhoneNumbers.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null) return false;
        if (getClass() != obj.getClass()) return false;
        Student other = (Student) obj;
        if (studentId != other.studentId) return false;
        if (studentName == null) {
            if (other.studentName != null) return false;
        }
        else if (!studentName.equals(other.studentName)) return false;
        if (studentPhoneNumbers == null) {
            if (other.studentPhoneNumbers != null) return false;
        }
        else if (!studentPhoneNumbers.equals(other.studentPhoneNumbers)) return false;
        return true;
    }

}

      

Telephone

package com.vaannila.student;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class Phone {

    @Id
    @GeneratedValue
    private long phoneId;
    private String phoneType;
    private String phoneNumber;

    public Phone() {
    }

    public Phone(String phoneType, String phoneNumber) {
        this.phoneType = phoneType;
        this.phoneNumber = phoneNumber;
    }

    public long getPhoneId() {
        return this.phoneId;
    }

    public void setPhoneId(long phoneId) {
        this.phoneId = phoneId;
    }

    public String getPhoneType() {
        return this.phoneType;
    }

    public void setPhoneType(String phoneType) {
        this.phoneType = phoneType;
    }

    public String getPhoneNumber() {
        return this.phoneNumber;
    }

    public void setPhoneNumber(String phoneNumber) {
        this.phoneNumber = phoneNumber;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + (int) (phoneId ^ (phoneId >>> 32));
        result = prime * result + ((phoneNumber == null) ? 0 : phoneNumber.hashCode());
        result = prime * result + ((phoneType == null) ? 0 : phoneType.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null) return false;
        if (getClass() != obj.getClass()) return false;
        Phone other = (Phone) obj;
        if (phoneId != other.phoneId) return false;
        if (phoneNumber == null) {
            if (other.phoneNumber != null) return false;
        }
        else if (!phoneNumber.equals(other.phoneNumber)) return false;
        if (phoneType == null) {
            if (other.phoneType != null) return false;
        }
        else if (!phoneType.equals(other.phoneType)) return false;
        return true;
    }

}

      

I have enclosed all the code here so you can see where the import comes from. I think there is a problem. Important . I use JoinTable

as it is recommended to use Hibernate Docs

Ok! Now I create Student

with two phone numbers and store it correctly in the database. This creates the following:

student

 studentid | studentname
-----------+-------------
         2 | foo
(1 rows)

      

student_phone

 student_id | phone_id
------------+---------
          2 |        3
          2 |        4
(2 rows)

      

telephone

 phoneid | phonenumber | phonetyp
---------+-------------+---------
       4 | 9889343423  | mobile
       3 | 32354353    | house
(2 rows)

      

That's the problem. If I remove one of the phone numbers (mobile) on the client side and send the detached student object to the server and do the update, hibernate to the following:

Hibernate: update Student set studentName=? where studentId=?
Hibernate: update Phone set phoneNumber=?, phoneType=? where phoneId=?
Hibernate: delete from STUDENT_PHONE where STUDENT_ID=?
Hibernate: insert into STUDENT_PHONE (STUDENT_ID, PHONE_ID) values (?, ?)

      

As you can see, it just deletes the connection table entry, but it does not delete the phone table entry itself. So now the tables look like this:

student

 studentid | studentname
-----------+-------------
         2 | foo
(1 rows)

      

student_phone

 student_id | phone_id
------------+---------
          2 |        3
(1 rows)

      

telephone

 phoneid | phonenumber | phonetyp
---------+-------------+---------
       4 | 9889343423  | mobile
       3 | 32354353    | house
(2 rows)

      

Question: Is this normal behavior? Even if cascading and orphans are set to true? How can I achieve this Hibernate also delete the phone number in the phone table?

UPDATE I am using PostgreSQL

+3


source to share


2 answers


After further working with Hibernate, I finally realize that I was not performing the functions correctly equals

and hashCode

causing some problems with the Hibernate Generated Sequence on CRUD operations. The problem is described (and solved) in this excellent article (must be read, in my opinion)



Regards

+3


source


It looks a lot like

http://www.mkyong.com/hibernate/cascade-jpa-hibernate-annotation-common-mistake/



You are mixing JPA and hibernate annotations. I would stick with one (preferably JPA, but blog posts stuck to hibernate) and have relationships this way:

@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)

      

+1


source







All Articles