JPA OneToMany tidak menghapus anak

158

Saya memiliki masalah dengan @OneToManypemetaan sederhana antara orang tua dan entitas anak. Semua berfungsi dengan baik, hanya saja catatan anak tidak dihapus ketika saya menghapusnya dari koleksi.

Orang tua:

@Entity
public class Parent {
    @Id
    @Column(name = "ID")
    private Long id;

    @OneToMany(cascade = {CascadeType.ALL}, mappedBy = "parent")
    private Set<Child> childs = new HashSet<Child>();

 ...
}

Anak:

@Entity
public class Child {
    @Id
    @Column(name = "ID")
    private Long id;

    @ManyToOne(cascade = CascadeType.ALL)
    @JoinColumn(name="PARENTID", nullable = false)
    private Parent parent;

  ...
}

Jika sekarang saya menghapus dan child dari Set childs, itu tidak bisa dihapus dari database. Saya mencoba membatalkan child.parentreferensi, tetapi tidak berhasil.

Entitas digunakan dalam aplikasi web, penghapusan terjadi sebagai bagian dari permintaan Ajax. Saya tidak memiliki daftar anak yang dihapus ketika tombol simpan ditekan, jadi saya tidak bisa menghapusnya secara implisit.

bert
sumber

Jawaban:

253

Perilaku JPA benar (artinya sesuai spesifikasi ): objek tidak dihapus hanya karena Anda telah menghapusnya dari koleksi OneToMany. Ada ekstensi khusus vendor yang melakukan itu tetapi JPA asli tidak memenuhi untuk itu.

Sebagian ini karena JPA tidak benar-benar tahu apakah harus menghapus sesuatu yang dihapus dari koleksi. Dalam istilah pemodelan objek, ini adalah perbedaan antara komposisi dan "agregasi *.

Dalam komposisi , entitas anak tidak memiliki keberadaan tanpa orangtua. Contoh klasik adalah antara Rumah dan Kamar. Hapus Rumah dan Kamar pergi juga.

Agregasi adalah jenis asosiasi yang lebih longgar dan ditandai oleh Kursus dan Siswa. Hapus Kursus dan Pelajar masih ada (mungkin di Kursus lain).

Jadi, Anda perlu menggunakan ekstensi khusus vendor untuk memaksakan perilaku ini (jika tersedia) atau menghapus anak secara eksplisit DAN menghapusnya dari koleksi orang tua.

Saya menyadari:

cletus
sumber
terima kasih penjelasannya. jadi seperti yang saya takutkan. (Saya melakukan beberapa pencarian / membaca sebelum saya bertanya, hanya ingin diselamatkan). entah bagaimana saya mulai menyesali keputusan untuk menggunakan JPA API dan nit Hibernate secara langsung .. Saya akan mencoba pointer Chandra Patni dan menggunakan tipe kaskade hibernate delete_orphan.
bert
Saya punya pertanyaan serupa tentang ini. Akan berbaik hati melihat posting ini di sini, tolong? stackoverflow.com/questions/4569857/…
Thang Pham
78
Dengan JPA 2.0, Anda sekarang dapat menggunakan opsi orphanRemoval = true
itsadok
2
Penjelasan awal yang bagus dan saran yang baik tentang orphanRemoval. Tidak tahu JPA tidak memperhitungkan jenis penghapusan ini. Nuansa antara apa yang saya ketahui tentang Hibernate dan apa yang sebenarnya dilakukan JPA bisa sangat menjengkelkan.
sma
Dijelaskan dengan baik dengan mengungkap perbedaan antara Komposisi dan Agregasi!
Felipe Leão
73

Selain jawaban cletus, JPA 2.0 , final sejak Desember 2010, memperkenalkan orphanRemovalatribut pada @OneToManyanotasi. Untuk lebih jelasnya lihat entri blog ini .

Perhatikan bahwa karena spek ini relatif baru, tidak semua penyedia JPA 1 memiliki implementasi JPA 2 final. Misalnya, rilis Hibernate 3.5.0-Beta-2 belum mendukung atribut ini.

Louis Jacomet
sumber
entri blog - tautan rusak.
Steffi
1
Dan keajaiban mesin wayback
Louis Jacomet
43

Anda dapat mencoba ini:

@OneToOne(orphanRemoval=true) or @OneToMany(orphanRemoval=true).

Maciel Bombonato
sumber
2
Terima kasih. Namun pertanyaannya adalah kembali dari JPA 1 kali. Dan opsi ini tidak tersedia kembali dari.
bert
9
baik untuk mengetahui namun, bagi kita yang mencari solusi untuk ini di masa JPA2 :)
alex440
20

Seperti yang dijelaskan, tidak mungkin untuk melakukan apa yang saya inginkan dengan JPA, jadi saya menggunakan anotasi hibernate.cascade, dengan ini, kode yang relevan di kelas Induk sekarang terlihat seperti ini:

@OneToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH}, mappedBy = "parent")
@Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE,
            org.hibernate.annotations.CascadeType.DELETE,
            org.hibernate.annotations.CascadeType.MERGE,
            org.hibernate.annotations.CascadeType.PERSIST,
            org.hibernate.annotations.CascadeType.DELETE_ORPHAN})
private Set<Child> childs = new HashSet<Child>();

Saya tidak bisa menggunakan 'SEMUA' karena ini akan menghapus induknya juga.

bert
sumber
4

Di sini kaskade, dalam konteks penghapusan, berarti bahwa anak-anak dihapus jika Anda menghapus orang tua. Bukan asosiasi. Jika Anda menggunakan Hibernate sebagai penyedia JPA Anda, Anda dapat melakukannya menggunakan kaskade khusus hibernasi .

Chandra Patni
sumber
4

Anda dapat mencoba ini:

@OneToOne(cascade = CascadeType.REFRESH) 

atau

@OneToMany(cascade = CascadeType.REFRESH)
Amit Ghorpade
sumber
3
@Entity 
class Employee {
     @OneToOne(orphanRemoval=true)
     private Address address;
}

Lihat di sini .

Chang
sumber