JPA orphanRemoval = true, ON DELETE CASCADE DML yan tümcesinden nasıl farklıdır?


184

JPA 2.0 orphanRemovalözelliği hakkında biraz kafam karıştı .

Ben ON DELETE CASCADEbelirli bir ilişki için bir temel veritabanı DDL oluşturmak için JPA sağlayıcının DB oluşturma araçlarını kullandığımda gerekli olduğunu düşünüyorum .

Ancak, DB varsa ve zaten bir ON DELETE CASCADEilişkisi varsa, bu silme uygun şekilde kademelendirmek için yeterli değil mi? Ek olarak ne yapar orphanRemoval?

Şerefe

Yanıtlar:


292

orphanRemovalhiçbir ilgisi yok ON DELETE CASCADE.

orphanRemovaltamamen ORM'ye özgü bir şeydir . Artık "üst" öğeden referans alınmadığında kaldırılacak olan "alt" öğeyi işaretler, örneğin alt öğeyi üst öğenin karşılık gelen koleksiyonundan kaldırdığınızda.

ON DELETE CASCADEBir olan veritabanına özel şey "üst" satır silindiğinde, bu veritabanındaki "çocuk" satırını siler.


3
Bu güvenli bir etkiye sahip oldukları anlamına mı geliyor, ancak bunun gerçekleşmesinden farklı bir sistem sorumlu mu?
Anonymoose

101
Anon, aynı etkiye sahip değil. ON DELETE CASCADE, DB'ye üst öğe silindiğinde tüm alt kayıtları silmesini söyler. INVOICE öğesini silip sonra bu INVOICE üzerindeki tüm öğeleri silersem. OrphanRemoval, ORM'ye, bir Nesne nesnesini bir Fatura nesnesine ait Öğeler koleksiyonundan (bellek işleminde) kaldırır ve sonra Faturayı "kaydeder", kaldırılan Öğe'nin temel DB'den silinmesi gerektiğini bildirir.
garyKeorkunian

2
Tek yönlü ilişki kullanıyorsanız, yetim, orphanRemoval = true ayarlamasanız bile yetim otomatik olarak kaldırılır
Tim

98

Burada alınan bir örnek :

Bir Employeevarlık nesnesi kaldırıldığında, kaldırma işlemi başvurulan Addressvarlık nesnesine basamaklandırılır . Bu bağlamda orphanRemoval=trueve cascade=CascadeType.REMOVEözdeştir ve orphanRemoval=truebelirtilirse CascadeType.REMOVEgereksizdir.

İki ayar arasındaki fark, bir ilişkinin kesilmesine verilen yanıttır. Örneğin, adres alanını nullbaşka bir Addressnesneye veya başka bir nesneye ayarlarken olduğu gibi .

  • Eğer orphanRemoval=trueis belirtilen bağlantısız Addressörneği otomatik olarak kaldırılır. Bu, Addressbir sahip nesnesinden (ör. Employee) Referans alınmadan var olmaması gereken bağımlı nesneleri (örn. ) Temizlemek için kullanışlıdır .

  • Yalnızca cascade=CascadeType.REMOVEbelirtilirse, ilişkinin kesilmesi kaldırma işlemi olmadığından otomatik bir işlem yapılmaz.

Yetimin kaldırılması sonucu atılan referanslardan kaçınmak için, bu özelliğin yalnızca özel paylaşılmayan bağımlı nesneleri içeren alanlarda etkinleştirilmesi gerekir.

Umarım bu daha net olur.


Cevabınızı okuduktan sonra, ikisi ile sorunum arasındaki farkın çözüldüğünün farkındayım. Üst varlık tanımlanmış koleksiyonundan kesilirse (kaldırılırsa), alt varlıkları veritabanından silmeye takıldım. Aynı soruyu ' stackoverflow.com/questions/15526440/… ' diye sordum . Her iki soruyu da bağlamak için yorumumu ekliyorum.
Narendra Verma

@forhas lütfen soru üzerinden geçin stackoverflow.com/questions/58185249/…
GokulRaj KN

46

Bir alt öğeyi koleksiyondan kaldırdığınızda, söz konusu alt öğeyi de DB'den kaldırmış olursunuz. orphanRemoval, ebeveynleri değiştiremeyeceğiniz anlamına da gelir; çalışanları olan bir departman varsa, o çalışanı başka bir departmana koymak için kaldırdığınızda, o çalışanı yanlışlıkla / taahhütte DB'den kaldırmış olursunuz (hangisi önce gelirse). Moral, o ebeveynin çocuklarının varlıkları boyunca farklı bir ebeveyne taşınmayacağından emin olduğunuz sürece orphanRemoval öğesini true değerine ayarlamaktır. OrphanRemoval özelliğini açmak da otomatik olarak basamaklı listeye KALDIR öğesini ekler.


3
Kesinlikle doğru ... "özel" ebeveyn / çocuk ilişkisi de denir.
HDave

Bu, aradığım anda department.remove(emp);, çalışanın aramadan emp tablosundan silineceği anlamına gelircommit()
JavaTechnical

18

DDL eşdeğer JPA haritalama ON DELETE CASCADEolduğunu cascade=CascadeType.REMOVE. Yetimin kaldırılması, bağımlı varlıkların "üst" varlıklarıyla olan ilişki yok edildiğinde kaldırıldığı anlamına gelir. Örneğin, bir çocuk bir @OneToManyvarlıktan varlık yöneticisinde açıkça kaldırılmadan kaldırılırsa.


1
cascade=CascadeType.REMOVEeşdeğer DEĞİLDİR ON DELETE CASCADE. Açık uygulama kodu kaldırmak ve DDL, diğer DB yürütülür etkilemez. Bkz. Stackoverflow.com/a/19696859/548473
Grigory Kislin

9

Fark şudur:
- orphanRemoval = true: "Alt" varlık artık referans alınmadığında kaldırılır (üst öğesi kaldırılamayabilir).
- CascadeType.REMOVE: "Alt" varlık yalnızca "Üst" öğesi kaldırıldığında kaldırılır.


6

Bu çok yaygın bir soru olduğundan, bu cevabın dayandığı bu makaleyi yazdım .

Varlık durumu geçişleri

JPA, varlık durumu geçişlerini INSERT, UPDATE veya DELETE gibi SQL deyimlerine çevirir.

JPA varlık durumu geçişleri

Bir persistvarlık olduğunda , INSERT deyimini EntityManagerotomatik olarak veya el ile temizlendiğinde yürütülecek şekilde zamanlarsınız .

Ne zaman removebir varlık, sen Sebat Bağlam sifonu çekildiğinde yürütülecektir DELETE deyimi, zamanlıyorsanız.

Basamaklı varlık devleti geçişleri

Kolaylık sağlamak için JPA, varlık durumu geçişlerini üst varlıklardan alt öğeye geçirmenize olanak tanır.

Dolayısıyla, alt varlık ile Postbir @OneToManyilişkisi olan bir ana öğeniz varsa PostComment:

Post ve PostComment varlıkları

Varlıktaki commentskoleksiyon Postaşağıdaki gibi eşlenir:

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

CascadeType.ALL

cascadeNitelik ebeveyn gelen varlık hal geçişini geçmesine JPA sağlayıcı söyler Postherkese varlık PostCommentiçinde bulunan kişiler commentskoleksiyonunda.

Varlığı kaldırırsanız Post:

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

entityManager.remove(post);

JPA sağlayıcısı önce PostCommentvarlığı kaldıracak ve tüm alt varlıklar silindiğinde Postvarlığı da silecektir :

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

DELETE FROM post WHERE id = 1

Yetim kaldırma

orphanRemovalÖzelliği olarak ayarladığınızda, alt varlık koleksiyondan kaldırıldığında trueJPA sağlayıcı bir removeişlem zamanlayacaktır .

Yani, bizim durumumuzda,

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);

Varlık koleksiyonda artık referans gösterilmediğinden JPA sağlayıcısı ilişkili post_commentkaydı kaldıracak :PostCommentcomments

DELETE FROM post_comment WHERE id = 1

SİLİNDİR SİLME

ON DELETE CASCADEFK düzeyinde tanımlanır:

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

Bunu yaptıktan sonra bir postsatırı silerseniz :

DELETE FROM post WHERE id = 1

İlişkili tüm post_commentvarlıklar veritabanı motoru tarafından otomatik olarak kaldırılır. Ancak, yanlışlıkla bir kök varlığı silerseniz bu çok tehlikeli bir işlem olabilir.

Sonuç

JPA'nın cascadeve orphanRemovalseçeneklerin avantajı, kayıp güncellemeleri önlemek için iyimser kilitlemeden de yararlanabilmenizdir .

JPA basamaklı mekanizmayı kullanırsanız ON DELETE CASCADE, birden çok düzeyde çok sayıda alt öğe içeren bir kök varlığı kaldırırsanız çok tehlikeli bir işlem olabilecek DDL düzeyi kullanmanız gerekmez .

Bu konu hakkında daha fazla bilgi için bu makaleye göz atın .


Cevabınızın Yetim Kaldırma bölümünde: post.getComments (). Remove (postComment); OneToMany çift yönlü eşlemede yalnızca Persist kaskad nedeniyle çalışır. Basamaklı olmadan ve ManyToOne tarafından kaldırılmadan, örneğin örnekte olduğu gibi, DB'de 2 varlık arasındaki bağlantının kaldırılması kalıcı olmaz mı?
aurelije

Yetimin kaldırılması etkilenmez CascadeType. Tamamlayıcı bir mekanizma. Şimdi, ısrarla devam etmeyi yanlış anlıyorsunuz. Yetimin kaldırılması, başvuruda bulunmayan derneklerin silinmesi ve devam eden yeni varlıkların kaydedilmesi ile ilgilidir. Bu kavramları daha iyi anlamak için yanıtta verilen bağlantıları takip etmeniz gerekir.
Vlad Mihalcea

Bir şeyi anlamıyorum: M tarafındaki bağlantıyı asla kaldırmazsak yetim kaldırma çift yönlü haritalamada nasıl devreye girecek? PostComment.post null için ayarlamadan PostComment listesinden kaldırmak, DB bu 2 varlık arasındaki bağlantıyı kaldırmaya neden olmayacağını düşünüyorum. Bu yüzden yetim kaldırmanın devreye girmeyeceğini düşünüyorum, ilişkisel dünyada PostComment yetim değil. Biraz boş zaman aldığımda test edeceğim.
aurelije

1
Bu iki örneği, hepsinin nasıl çalıştığını gösteren Yüksek Performanslı Java Kalıcılığı GitHub veri havuzuma ekledim . Varlıkları doğrudan kaldırmak için yapmanız gereken genellikle alt tarafı senkronize etmeniz gerekmez. Ancak, yetim kaldırma işlemi yalnızca basamaklı olarak eklenirse çalışır, ancak bu bir JPA belirtimi değil, Hazırda Bekletme sınırlaması gibi görünür.
Vlad Mihalcea

5

@GaryK cevap, ben bir açıklama beklediğini bir saat kesinlikle harika harcadım edilir orphanRemoval = truevs CascadeType.REMOVEve anlamama yardımcı oldu.

Özetle: SADECE object ( ) öğesini silersek ve alt nesnelerin de kaldırılmasını istiyorsak orphanRemoval = trueaynı şekilde çalışır .CascadeType.REMOVE entityManager.delete(object)

Tamamen farklı bir durumda, List<Child> childs = object.getChilds()bir child ( entityManager.remove(childs.get(0)) gibi bazı verileri getirip kaldırdığımızda orphanRemoval=truekarşılık gelen varlığın childs.get(0)veritabanından silinmesine neden olur .


1
İkinci paragrafınızda bir yazım hatası var: entityManager.delete (obj); entityManager.remove (obj).
JL_SO

3

yetim kaldırma aşağıdaki senaryoda SİLME SİLME ile aynı etkiye sahiptir: - Diyelim ki öğrenci varlığı ile bir rehber varlık arasında basit bir çoktan bir ilişkiye sahibiz, burada birçok öğrenci aynı kılavuza eşleştirilebilir Öğrenci tablosunun yabancı anahtar ilişkisi, öğrenci tablosunun FK olarak id_guide olması gerekir.

    @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;

// Ana varlık

    @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);

Bu senaryoda, ilişki, öğrenci varlığın ilişkinin sahibi olacağı şekildedir ve bu nedenle, tüm nesne grafiğini devam ettirmek için öğrenci varlığını kaydetmemiz gerekir;

    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);

Burada aynı kılavuzu iki farklı öğrenci nesnesiyle eşleştiriyoruz ve CASCADE.PERSIST kullanıldığından nesne grafiği aşağıdaki gibi veritabanı tablosuna kaydedilecektir (benim durumumda MySql)

ÖĞRENCİ tablosu: -

Kimlik Adı Bölüm Id_Guide

1 Roy ECE 1

2 Nick ECE 1

REHBER Tablo: -

Kimlik Adı Maaş

1 Yuhanna 1500

Ve şimdi öğrencilerden birini kaldırmak istersem

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

ve bir öğrenci kaydı kaldırıldığında, karşılık gelen rehber kaydı da kaldırılmalıdır; bu, Öğrenci kuruluşundaki CASCADE.REMOVE özniteliğinin resme girdiği ve ne yaptığıdır; 1). Ancak bu örnekte, aynı rehber kaydına eşlenen bir öğrenci nesnesi daha vardır ve Guide Entity'de orphanRemoval = true özniteliğini kullanmazsak yukarıdaki kaldırma kodu çalışmaz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.