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.

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:


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


43

Anda dapat mencoba ini:

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


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.


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 .


4

Anda dapat mencoba ini:

@OneToOne(cascade = CascadeType.REFRESH) 

atau

@OneToMany(cascade = CascadeType.REFRESH)

3
@Entity 
class Employee {
     @OneToOne(orphanRemoval=true)
     private Address address;
}

Lihat di sini .

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.