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
source to share
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
source to share
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)
source to share