JPA: tek yönlü çoktan bire ve basamaklı silme


98

Bir var ki tek yönlü @ManyToOne aşağıdaki gibi bir ilişki:

@Entity
public class Parent implements Serializable {

    @Id
    @GeneratedValue
    private long id;
}

@Entity
public class Child implements Serializable {

    @Id
    @GeneratedValue
    private long id;

    @ManyToOne
    @JoinColumn
    private Parent parent;  
}

Eğer bir ebeveynim P ve çocuklarım varsa C 1 ... C n P'ye geri dönüyorlarsa, JPA'da P çıkarıldığında C 1 ... C n çocuklarını otomatik olarak çıkarmanın temiz ve güzel bir yolu var entityManager.remove(P)mı (yani )?

Aradığım şey SQL'deki gibi bir işlevsellik ON DELETE CASCADE.


1
Yalnızca 'Çocuk'un' Ebeveyn'e bir referansı olsa bile (bu şekilde referans tek yönlüdür) '@OneToMany' eşlemesi ve 'Cascade = ALL' özniteliğiyle 'Alt' Listesini eklemeniz sizin için sorunludur. 'Ebeveyn'? JPA'nın sadece 1 tarafın referansı tuttuğunu çözmesi gerektiğini varsayıyorum.
kvDennis

1
@kvDennis, birçok tarafı bir tarafa sıkıca bağlamak istemediğiniz durumlar vardır. Örneğin, güvenlik izinlerinin şeffaf "eklenti" olduğu ACL benzeri kurulumlarda
Bachi

Yanıtlar:


73

Ebeveyni çocukla her iki yönde ilişkilendirmediğiniz sürece, JPA'daki ilişkiler her zaman tek yönlüdür. REMOVE işlemlerinin ebeveynden çocuğa basamaklandırılması, ebeveynden çocuğa bir ilişki gerektirir (tam tersi değil).

Bu nedenle, bunu yapmanız gerekecek:

  • Tek yönlü @ManyToOneilişkiyi iki @ManyToOneyönlü veya tek yönlü olarak değiştirin @OneToMany. Ardından, KALDIRMA işlemlerini basamaklandırabilirsiniz, böylece EntityManager.removeüst ve alt öğeler kaldırılır. orphanRemovalÜst koleksiyondaki alt öğe null olarak ayarlandığında, öksüz çocukları silmek için true olarak da belirtebilirsiniz , yani herhangi bir ebeveyn koleksiyonunda bulunmadığında çocuğu kaldırabilirsiniz.
  • Veya alt tablodaki yabancı anahtar kısıtlamasını olarak belirtin ON DELETE CASCADE. Kalıcılık bağlamının yenilenmesi gerektiğinden EntityManager.clear()çağırdıktan sonra çağırmanız gerekir EntityManager.remove(parent)- alt varlıkların veritabanında silindikten sonra kalıcılık bağlamında var olmaması gerekir.

7
JPA ek açıklamasıyla No2 yapmanın bir yolu var mı?
user2573153

3
Hibernate xml eşleştirmeleriyle No2'yi nasıl yaparım?
arg20

96

JPA sağlayıcınız olarak hazırda bekletme kullanıyorsanız, @OnDelete notunu kullanabilirsiniz. Bu açıklama, alt öğelerin veri tabanına silinmesini yetkilendiren ON DELETE CASCADE tetikleyicisini ilişkiye ekleyecektir .

Misal:

public class Parent {

        @Id
        private long id;

}


public class Child {

        @Id
        private long id;

        @ManyToOne
        @OnDelete(action = OnDeleteAction.CASCADE)
        private Parent parent;
}

Bu çözümle, çocuktan ebeveyne tek yönlü bir ilişki, tüm çocukları otomatik olarak çıkarmak için yeterlidir. Bu çözüm herhangi bir dinleyiciye ihtiyaç duymaz. Ayrıca DELETE FROM Parent WHERE id = 1 gibi bir sorgu alt öğeleri kaldıracaktır.


4
Bu şekilde çalışmasını sağlayamam, özel bir hazırda bekletme sürümü veya bunun gibi daha ayrıntılı başka bir örnek var mı?
Mardari

3
Neden senin için çalışmadığını söylemek zor. Bunu çalıştırmak için şemayı yeniden oluşturmanız gerekebilir veya kademeli silmeyi manuel olarak eklemeniz gerekebilir. @OnDelete ek açıklaması bir süredir ortalıkta göründüğü için sürümün bir sorun olduğunu tahmin edemem.
Thomas Hunziker

12
Cevap için teşekkürler. Hızlı not: veritabanı kademeli tetikleyicisi, yalnızca hazırda bekletme aracılığıyla DDL oluşturmayı etkinleştirdiyseniz oluşturulacaktır. Aksi takdirde, ad hoc sorguların doğrudan DB'ye karşı çalıştırılmasına izin vermek için başka bir yol (örn. Likit taban) eklemeniz gerekir; örneğin, 'DELETE FROM Parent WHERE id = 1', kademeli kaldırma gerçekleştirir.
mjj1409

1
dernek olduğunda bu çalışmıyor @OneToOneile nasıl çözüleceğini Herhangi bir fikir @OneToOne?
stakowerflol

1
@ThomasHunziker bu öksüz kaldırma için çalışmayacak değil mi?
oxyt

14

Bunun gibi iki yönlü bir ilişki oluşturun:

@Entity
public class Parent implements Serializable {

    @Id
    @GeneratedValue
    private long id;

    @OneToMany(mappedBy = "parent", cascade = CascadeType.REMOVE)
    private Set<Child> children;
}

9
kötü cevap, çift yönlü ilişkiler
JPA'da

1
Çift yönlü ilişkilerin yavaş olduğuna dair kanıt var mı?
shalama

@enerccio Ya çift yönlü ilişki bire birse? Ayrıca, lütfen iki yönlü ilişkilerin yavaş olduğunu belirten bir makale gösterin. ne yavaş? geri alma? siliniyor mu? güncelleniyor mu?
saran3h

@ saran3h her işlem (ekle, kaldır) tüm alt öğeleri yükleyecektir, bu nedenle büyük bir veri yüklemesi işe yaramaz olabilir (bir değer eklemek gibi, tüm alt öğeleri veritabanından yüklemeyi gerektirmez, bu eşlemenin yaptığı tam olarak budur).
Enerccio

@Enerccio Bence herkes katılımlarda tembel yükleme kullanıyor. Öyleyse nasıl hala bir performans sorunu?
saran3h

2

Tek yönlü @ManytoOne'da gördüm, silme beklendiği gibi çalışmıyor. Ebeveyn silindiğinde, ideal olarak çocuk da silinmelidir, ancak yalnızca ebeveyn silinir ve çocuk SİLİNMEZ ve yetim olarak bırakılır

Kullanılan teknoloji Spring Boot / Spring Data JPA / Hibernate'dir

Sprint Boot: 2.1.2. YAYIN

Spring Data JPA / Hibernate, .eg satırını silmek için kullanılır

parentRepository.delete(parent)

ParentRepository, standart CRUD havuzunu aşağıda gösterildiği gibi genişletir ParentRepository extends CrudRepository<T, ID>

Varlık sınıfım aşağıdadır

@Entity(name = “child”)
public class Child  {

    @Id
    @GeneratedValue
    private long id;

    @ManyToOne( fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = “parent_id", nullable = false)
    @OnDelete(action = OnDeleteAction.CASCADE)
    private Parent parent;
}

@Entity(name = “parent”)
public class Parent {

    @Id
    @GeneratedValue
    private long id;

    @Column(nullable = false, length = 50)
    private String firstName;


}

Silme işleminin neden çalışmadığının çözümünü buldum. görünüşe göre hazırda bekletme mysql Engine -INNODB kullanmıyordu, yabancı anahtar kısıtlaması oluşturmak için mysql için motor INNODB'ye ihtiyacınız var. Application.properties dosyasında aşağıdaki özelliklerin kullanılması, INNODB mysql motorunu kullanmak için Spring boot / hibernate olmasını sağlar. Yani yabancı anahtar kısıtlaması çalışıyor ve dolayısıyla kademeli
siliyor

Eksik özellikler önceki yorumda kullanılır. kullanılan yay özellikleri spring.jpa.hibernate.use-new-id-generator-mappings=true spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
aşağıdadır

Bilginize, "kodda yanlışsınız . Bakınname= "parent"
alexander

0

Yalnızca bir tarafı silmek için bu yolu kullanın

    @ManyToOne(cascade=CascadeType.PERSIST, fetch = FetchType.LAZY)
//  @JoinColumn(name = "qid")
    @JoinColumn(name = "qid", referencedColumnName = "qid", foreignKey = @ForeignKey(name = "qid"), nullable = false)
    // @JsonIgnore
    @JsonBackReference
    private QueueGroup queueGroup;

-1

@Cascade (org.hibernate.annotations.CascadeType.DELETE_ORPHAN)

Verilen ek açıklama benim için çalıştı. Deneyebilir miyim

Örneğin :-

     public class Parent{
            @Id
            @GeneratedValue(strategy=GenerationType.AUTO)
            @Column(name="cct_id")
            private Integer cct_id;
            @OneToMany(cascade=CascadeType.REMOVE, fetch=FetchType.EAGER,mappedBy="clinicalCareTeam", orphanRemoval=true)
            @Cascade(org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
            private List<Child> childs;
        }
            public class Child{
            @ManyToOne(fetch=FetchType.EAGER)
            @JoinColumn(name="cct_id")
            private Parent parent;
    }

-1

Kodunuz yerine çift yönlü ilişkilendirme kullanmanıza gerek yok, sadece CascaType eklemelisiniz. ManyToOne ek açıklamasına bir özellik olarak kaldır, ardından @OnDelete (action = OnDeleteAction.CASCADE) kullan, benim için iyi çalışıyor.

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.