Bagaimana JPA orphanRemoval = true berbeda dari klausa ON DELETE CASCADE DML

184

Saya sedikit bingung tentang orphanRemovalatribut JPA 2.0 .

Saya pikir saya bisa melihatnya diperlukan ketika saya menggunakan alat generasi DB penyedia JPA saya untuk membuat database yang mendasari DDL untuk memiliki ON DELETE CASCADEhubungan khusus.

Namun, jika DB ada dan sudah memiliki ON DELETE CASCADEhubungan, apakah ini tidak cukup untuk menghapus penghapusan secara tepat? Apa yang orphanRemovaldilakukan selainnya?

Bersulang

Markos Fragkakis
sumber

Jawaban:

292

orphanRemovaltidak ada hubungannya dengan ON DELETE CASCADE.

orphanRemovaladalah hal yang sepenuhnya spesifik ORM . Ini menandai entitas "child" yang akan dihapus ketika tidak lagi direferensikan dari entitas "parent", misalnya ketika Anda menghapus entitas child dari koleksi entitas induk yang sesuai.

ON DELETE CASCADEadalah hal yang khusus untuk basis data , menghapus baris "child" dalam database ketika baris "parent" dihapus.

axtavt
sumber
3
Apakah ini berarti mereka memiliki efek aman, tetapi sistem yang berbeda bertanggung jawab untuk mewujudkannya?
Anonymoose
101
Anon, itu tidak memiliki efek yang sama. ON DELETE CASCADE memberi tahu DB untuk menghapus semua catatan anak saat induk dihapus. Itu adalah jika saya menghapus Faktur, maka hapus semua ITEM di Faktur itu. OrphanRemoval memberi tahu ORM bahwa jika saya menghapus objek Item dari kumpulan Item yang termasuk objek Invoice (dalam operasi memori), dan kemudian "menyimpan" Invoice, Item yang dihapus harus dihapus dari DB yang mendasarinya.
garyKeorkunian
2
Jika Anda menggunakan hubungan uni-directional, maka anak yatim akan dihapus secara otomatis meskipun Anda tidak menetapkan orphanRemoval = true
Tim
98

Contoh diambil di sini :

Ketika Employeeobjek entitas dihapus, operasi penghapusan akan mengalir ke Addressobjek entitas yang direferensikan . Dalam hal ini, orphanRemoval=truedan cascade=CascadeType.REMOVEidentik, dan jika orphanRemoval=trueditentukan, CascadeType.REMOVEberlebihan.

Perbedaan antara kedua pengaturan ini adalah pada respons untuk memutuskan hubungan. Misalnya, seperti ketika mengatur bidang alamat ke nullatau ke Addressobjek lain .

  • Jika orphanRemoval=trueditentukan, Addressinstance terputus akan dihapus secara otomatis. Ini berguna untuk membersihkan objek dependen (mis. Address) Yang seharusnya tidak ada tanpa referensi dari objek pemilik (mis Employee.).

  • Jika hanya cascade=CascadeType.REMOVEditentukan, tidak ada tindakan otomatis yang diambil karena memutuskan hubungan bukanlah operasi penghapusan.

Untuk menghindari menggantung referensi sebagai akibat dari penghapusan anak yatim, fitur ini hanya boleh diaktifkan untuk bidang yang menampung benda-benda yang tidak dibagi bersama pribadi.

Saya harap ini membuatnya lebih jelas.

fora
sumber
Setelah membaca jawaban Anda, saya menyadari perbedaan yang tepat antara keduanya dan masalah saya telah terselesaikan. Saya terjebak dalam menghapus entitas anak dari database, jika itu terputus (dihapus) dari koleksi yang ditetapkan dalam entitas induk. Untuk hal yang sama saya menanyakan pertanyaan ' stackoverflow.com/questions/15526440/… '. Cukup tambahkan komentar saya untuk menghubungkan kedua pertanyaan.
Narendra Verma
@forh, silakan tanyakan ke stackoverflow.com/questions/58185249/…
GokulRaj KN
46

Saat Anda menghapus entitas anak dari koleksi Anda juga akan menghapus entitas anak dari DB juga. orphanRemoval juga menyiratkan bahwa Anda tidak dapat mengubah orang tua; jika ada departemen yang memiliki karyawan, begitu Anda memindahkan karyawan itu ke deparment lain, Anda akan secara tidak sengaja memindahkan karyawan tersebut dari DB di flush / commit (mana yang lebih dulu). Moralnya adalah untuk menetapkan OrphanRemoval menjadi true selama Anda yakin bahwa anak-anak dari orang tua itu tidak akan bermigrasi ke orang tua yang berbeda sepanjang keberadaan mereka. Mengaktifkan orphanRemoval juga secara otomatis menambahkan HAPUS ke daftar kaskade.

Onur
sumber
3
Benar tepat ... juga disebut hubungan orangtua / anak "pribadi".
HDave
Itu berarti begitu saya memanggil department.remove(emp);karyawan itu akan dihapus dari tabel emp bahkan tanpa meneleponcommit()
JavaTechnical
18

Pemetaan JPA yang setara untuk DDL ON DELETE CASCADEadalah cascade=CascadeType.REMOVE. Penghapusan anak yatim berarti bahwa entitas dependen dihapus ketika hubungan dengan entitas "induk" mereka dihancurkan. Misalnya jika seorang anak dihapus dari suatu @OneToManyhubungan tanpa secara eksplisit menghapusnya di manajer entitas.

Heri
sumber
1
cascade=CascadeType.REMOVETIDAK setara dengan ON DELETE CASCADE. Pada jangan hapus dalam kode aplikasi dan tidak mempengaruhi pada DDL, lainnya dijalankan dalam DB. Lihat stackoverflow.com/a/19696859/548473
Grigory Kislin
9

Perbedaannya adalah:
- orphanRemoval = true: entitas "Child" dihapus ketika tidak lagi direferensikan (induknya mungkin tidak dihapus).
- CascadeType.REMOVE: "Child" entitas dihapus hanya ketika "Induknya" dihapus.

pengguna3572554
sumber
6

Karena ini adalah pertanyaan yang sangat umum, saya menulis artikel ini , yang menjadi dasar jawaban ini.

Transisi status entitas

JPA menerjemahkan transisi status entitas ke pernyataan SQL, seperti INSERT, UPDATE atau DELETE.

Transisi status entitas JPA

Ketika Anda persistsuatu entitas, Anda menjadwalkan pernyataan INSERT untuk dieksekusi ketika EntityManagermemerah, baik secara otomatis atau manual.

ketika Anda removesuatu entitas, Anda menjadwalkan pernyataan DELETE, yang akan dieksekusi ketika Konteks Persistence memerah.

Transisi status entitas Cascading

Untuk kenyamanan, JPA memungkinkan Anda untuk menyebarkan transisi status entitas dari entitas induk ke entitas anak.

Jadi, jika Anda memiliki Postentitas induk yang memiliki @OneToManykaitan dengan PostCommententitas anak:

Entitas Post dan PostComment

The commentskoleksi dalam Postentitas dipetakan sebagai berikut:

@OneToMany(
    mappedBy = "post", 
    cascade = CascadeType.ALL,
    orphanRemoval = true
)
private List<Comment> comments = new ArrayList<>();

CascadeType.ALL

The cascadeatribut memberitahu penyedia JPA untuk lulus transisi entitas negara dari induk Postentitas untuk semua PostCommententitas yang terkandung dalam commentskoleksi.

Jadi, jika Anda menghapus Postentitas:

Post post = entityManager.find(Post.class, 1L);
assertEquals(2, post.getComments().size());

entityManager.remove(post);

Penyedia JPA akan menghapus PostCommententitas terlebih dahulu, dan ketika semua entitas anak dihapus, itu akan menghapus Postentitas juga:

DELETE FROM post_comment WHERE id = 1
DELETE FROM post_comment WHERE id = 2

DELETE FROM post WHERE id = 1

Penghapusan anak yatim

Ketika Anda mengatur orphanRemovalatribut true, penyedia JPA akan menjadwalkan removeoperasi ketika entitas anak dihapus dari koleksi.

Jadi, dalam kasus kami,

Post post = entityManager.find(Post.class, 1L);
assertEquals(2, post.getComments().size());

PostComment postComment = post.getComments().get(0);
assertEquals(1L, postComment.getId());

post.getComments().remove(postComment);

Penyedia JPA akan menghapus post_commentcatatan terkait karena PostCommententitas tidak lagi dirujuk dalam commentskoleksi:

DELETE FROM post_comment WHERE id = 1

HAPUS CASCADE

The ON DELETE CASCADEdidefinisikan di tingkat FK:

ALTER TABLE post_comment 
ADD CONSTRAINT fk_post_comment_post_id 
FOREIGN KEY (post_id) REFERENCES post 
ON DELETE CASCADE;

Setelah Anda melakukannya, jika Anda menghapus satu postbaris:

DELETE FROM post WHERE id = 1

Semua post_commententitas yang terkait dihapus secara otomatis oleh mesin database. Namun, ini bisa menjadi operasi yang sangat berbahaya jika Anda menghapus entitas root karena kesalahan.

Kesimpulan

Keuntungan dari JPA cascadedan orphanRemovalopsi adalah Anda juga dapat memanfaatkan penguncian optimis untuk mencegah pembaruan yang hilang .

Jika Anda menggunakan mekanisme cascading JPA, Anda tidak perlu menggunakan level DDL ON DELETE CASCADE, yang bisa menjadi operasi yang sangat berbahaya jika Anda menghapus entitas root yang memiliki banyak entitas anak di beberapa level.

Untuk detail lebih lanjut tentang topik ini, lihat artikel ini .

Vlad Mihalcea
sumber
Jadi di bagian Penghapusan Orphan bagian dari jawaban Anda: post.getComments (). Remove (postComment); akan bekerja dalam pemetaan dua arah OneToMany hanya karena kaskade Persist. Tanpa cascading dan tidak memiliki penghapusan di sisi ManyToOne, seperti pada contoh Anda, penghapusan koneksi antara 2 entitas tidak akan bertahan dalam DB?
aurelije
Penghapusan anak yatim tidak terpengaruh oleh CascadeType. Ini mekanisme yang saling melengkapi. Sekarang, Anda salah mengira penghapusan tetap ada. Penghapusan anak yatim adalah tentang menghapus asosiasi yang tidak direferensikan sementara yang bertahan adalah tentang menyelamatkan entitas baru. Anda perlu mengikuti tautan yang disediakan dalam jawaban untuk mendapatkan pemahaman yang lebih baik tentang konsep-konsep ini.
Vlad Mihalcea
Saya tidak mengerti satu hal: bagaimana penghapusan yatim piatu dalam pemetaan dua arah jika kita tidak pernah menghapus koneksi di sisi M? Saya berpikir bahwa menghapus PostComment dari daftar Post tanpa mengatur PostComment.post ke nol tidak akan menghasilkan penghapusan koneksi antara 2 entitas di DB. Itulah mengapa saya pikir penghapusan anak yatim tidak akan berhasil, di dunia relasional ada PostComment bukan anak yatim. Saya akan mengujinya ketika saya mendapatkan waktu luang.
aurelije
1
Saya menambahkan dua contoh ini di repositori Java Persistence GitHub Kinerja Tinggi saya yang menunjukkan bagaimana semuanya bekerja. Anda tidak perlu menyinkronkan sisi anak seperti yang biasanya harus Anda lakukan untuk menghapus entitas secara langsung. Namun, penghapusan anak yatim hanya berfungsi jika kaskade ditambahkan, tetapi itu tampaknya merupakan batasan Hibernate, bukan spesifikasi JPA.
Vlad Mihalcea
5

@GaryK jawaban benar-benar hebat, saya telah menghabiskan satu jam mencari penjelasan orphanRemoval = truevs CascadeType.REMOVEdan itu membantu saya mengerti.

Ringkasnya: orphanRemoval = trueberfungsi sama dengan CascadeType.REMOVE HANYA JIKA kita menghapus objek ( entityManager.delete(object)) dan kami ingin objek childs juga dihapus.

Dalam sitiuation yang sama sekali berbeda, ketika kita mengambil beberapa data seperti List<Child> childs = object.getChilds()dan kemudian menghapus child ( entityManager.remove(childs.get(0)) menggunakan orphanRemoval=trueakan menyebabkan entitas yang sesuai dengan itu childs.get(0)akan dihapus dari database.

pzeszko
sumber
1
Anda memiliki kesalahan ketik pada paragraf kedua: Tidak ada metode seperti entityManager.delete (obj); itu adalah entitasManager.remove (obj).
JL_SO
3

penghapusan anak yatim memiliki efek yang sama seperti ON DELETE CASCADE dalam skenario berikut: - Katakanlah kita memiliki hubungan banyak ke satu yang sederhana antara entitas siswa dan entitas panduan, di mana banyak siswa dapat dipetakan ke panduan yang sama dan dalam database kami memiliki hubungan kunci asing antara tabel Student and Guide sehingga tabel student memiliki id_guide sebagai FK.

    @Entity
    @Table(name = "student", catalog = "helloworld")
    public class Student implements java.io.Serializable {
     @Id
     @GeneratedValue(strategy = IDENTITY)
     @Column(name = "id")
     private Integer id;

    @ManyToOne(cascade={CascadeType.PERSIST,CascadeType.REMOVE})
    @JoinColumn(name = "id_guide")
    private Guide guide;

// Entitas induk

    @Entity
    @Table(name = "guide", catalog = "helloworld")
    public class Guide implements java.io.Serializable {

/**
 * 
 */
private static final long serialVersionUID = 9017118664546491038L;

@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "id", unique = true, nullable = false)
private Integer id;

@Column(name = "name", length = 45)
private String name;

@Column(name = "salary", length = 45)
private String salary;


 @OneToMany(mappedBy = "guide", orphanRemoval=true) 
 private Set<Student> students = new  HashSet<Student>(0);

Dalam skenario ini, hubungannya adalah sedemikian rupa sehingga entitas siswa adalah pemilik dari hubungan dan dengan demikian kita perlu menyelamatkan entitas siswa untuk mempertahankan seluruh objek grafik misalnya

    Guide guide = new Guide("John", "$1500");
    Student s1 = new Student(guide, "Roy","ECE");
    Student s2 = new Student(guide, "Nick", "ECE");
    em.persist(s1);
    em.persist(s2);

Di sini kita memetakan panduan yang sama dengan dua objek siswa yang berbeda dan karena CASCADE.PERSIST digunakan, grafik objek akan disimpan seperti di bawah ini dalam tabel database (MySql dalam kasus saya)

Tabel MAHASISWA: -

Nama ID Dept Id_Guide

1 Roy ECE 1

2 Nick ECE 1

Tabel PANDUAN: -

Gaji ID NAMA

1 Yohanes $ 1500

dan Sekarang jika saya ingin menghapus salah satu siswa, gunakan

      Student student1 = em.find(Student.class,1);
      em.remove(student1);

dan ketika catatan siswa dihapus, catatan panduan terkait juga harus dihapus, di situlah atribut CASCADE.REMOVE dalam entitas Siswa muncul dalam gambar dan apa yang dilakukan adalah, menghapus siswa dengan pengenal 1 serta objek panduan yang sesuai (pengidentifikasi 1). Tetapi dalam contoh ini, ada satu objek siswa lagi yang dipetakan ke catatan panduan yang sama dan kecuali jika kita menggunakan atribut orphanRemoval = true dalam Entitas Pemandu, kode hapus di atas tidak akan berfungsi.

kunal
sumber