Hazırda Bekleme - cascade = ”all-delete-orphan” içeren bir koleksiyona artık sahip olan varlık örneği tarafından başvuru yapılmadı


226

Varlığımı güncellemeye çalışırken şu sorunu yaşıyorum:

"A collection with cascade=”all-delete-orphan” was no longer referenced by the owning entity instance".

Bir ebeveyn varlığım var ve Set<...>bazı çocuk varlıkları var. Güncellemeye çalıştığımda, bu koleksiyonlara ayarlanacak tüm referansları alıp ayarlıyorum.

Aşağıdaki kod eşlememi temsil ediyor:

@OneToMany(mappedBy = "parentEntity", fetch = FetchType.EAGER)
@Cascade({ CascadeType.ALL, CascadeType.DELETE_ORPHAN })
public Set<ChildEntity> getChildren() {
    return this.children;
}

Ben sadece Set <..> temizlemeye çalıştım, buna göre: Nasıl "olası" sorunu çözmek için ama işe yaramadı.

Herhangi bir fikriniz varsa, lütfen bana bildirin.

Teşekkürler!


1
@ mel3kings, sağladığınız bağlantı artık etkin değil.
Opal


elemanları çıkarırken değiştirilebilir koleksiyonlar kullanmayı deneyin. Örneğin , bir something.manyother.remove(other)ise kullanmayın . Çok daha mutable, gibi yapmak ve kullanmakmanyotherList<T>ArrayList<T>orphanDelete = true
nurettin

Yanıtlar:


220

SonEntities'e bir şey atadığınız tüm yerleri kontrol edin. Referansta bulunduğunuz bağlantı, yeni bir HashSet oluşturmaya dikkat çekiyor, ancak seti yeniden atadığınızda bu hatayı alabilirsiniz. Örneğin:

public void setChildren(Set<SonEntity> aSet)
{
    this.sonEntities = aSet; //This will override the set that Hibernate is tracking.
}

Genellikle bir yapıcıda yalnızca bir kez "yeni" olmak istersiniz. Listeye bir şey eklemek veya listeye bir şey silmek istediğinizde, yeni bir liste atamak yerine listenin içeriğini değiştirmeniz gerekir.

Çocuk eklemek için:

public void addChild(SonEntity aSon)
{
    this.sonEntities.add(aSon);
}

Çocukları çıkarmak için:

public void removeChild(SonEntity aSon)
{
    this.sonEntities.remove(aSon);
}

8
Aslında benim sorunum, varlıklarımın eşit ve hashcode'u ile ilgiliydi. Eski bir kod çok fazla sorun getirebilir, asla kontrol etmeyi unutmayın. Yaptığım tek şey silmek-yetim stratejisini tutmak ve eşittir ve hashcode'u düzeltmekti.
axcdnt

6
Sorununu çözmene sevindim. eşittir ve hashcode beni Hibernate ile birkaç kez ısırdı. Sorunun başlığını "[Çözüldü]" ile güncellemek yerine, devam edip cevabınızı göndermeli ve daha sonra kabul edilen cevap olarak işaretlemelisiniz.
brainimus

Teşekkürler, ben benzer bir şey koştu ve Set Set boştu için setter çıktı ..
Siddhartha

evet boş listelerde bile bu hatayı alırsınız. (liste boştu) hiçbir yetim olmadığı için bunu beklemem.
tibi

4
Genellikle bir yapıcıda yalnızca bir kez "yeni" olmak istersiniz. Listeye bir şey eklemek veya listeye bir şey silmek istediğinizde, yeni bir liste atamak yerine listenin içeriğini değiştirmeniz gerekir. En küçük imp
Nikhil Sahu

109

Yöntem:

public void setChildren(Set<SonEntity> aSet) {
    this.sonEntities = aSet;
}

ayrılırsa parentEntityve tekrar güncellersek çalışır.
Ancak, varlık bağlama göre ayrılmazsa (yani, bul ve güncelleştirme işlemleri aynı işlemde ise) aşağıdaki yöntem çalışır.

public void setChildren(Set<SonEntity> aSet) {
    //this.sonEntities = aSet; //This will override the set that Hibernate is tracking.
    this.sonEntities.clear();
    if (aSet != null) {
        this.sonEntities.addAll(aSet);
    }
}

1
@ Benzer bir sorunum var ve çözümünüzü uyguladım (ayarlayıcı yönteminde çocuk koleksiyonunu temizliyorum - this.children.clear () - ve yeni çocukları ekledim - this.children.addAll (children)). Bu değişiklik sorunumu çözmedi. Hala "cascade =" all-delete-yetim "ile bir koleksiyon artık varlık varlık örneği tarafından başvurulan" istisna alıyorum. Neden olduğu hakkında bir fikrin var mı? Çok teşekkür ederim!
ovdsrn

@ovdsrn Üzgünüm, bu benim cevabım değil, cevabın formatını yeni sıraladım, orijinal yazar (kmmanu) size yardımcı olabilir (veya senaryonuz farklıysa yeni bir soru başlatmak isteyebilirsiniz) burada sorulan orijinal soru) İyi şanslar
Skuld

1
Bu iyi çalışır, ancak çocuklar iç içe geçmiş bir hazırda bekletme bileşeninde bulunduğunda ve bileşen Varlığında boş bırakıldığında çok iyi değildir. Sonra aynı hatayı alıyorsunuz. Bunun nedeni, çocukların sahibinin null yapılan bileşen değil, kök Varlık olmasıdır ... Böyle bir bileşenin hiçbir zaman boş kalmasına izin verilmez, ancak bunun yerine bir destroy () yöntemine iletilmesi gerekir. çocuk ... En azından, daha iyi bir çözüm bilmiyorum ... Bu bir tür koleksiyon açık () inşaat ama sonra destroy () ile bir bileşen üzerinde ...
edbras

Yukarıda daha fazla ayrıntı için bu
gönderiye

7
sonEntities.clear () üzerinden this.sonEntities.retainAll (aSet) kullanın çünkü aSet == this.sonEntities (yani aynı nesne) eğer eklemeden önce seti temizleyeceksiniz!
Martin

33

Hazırda bekletme modunun bir koleksiyona atamanızı istemediği çeşitli yerlerde okuduğumda, yapılacak en güvenli şeyin açıkça bu şekilde son haline getirmek olduğunu varsaydım:

class User {
  private final Set<Role> roles = new HashSet<>();

public void setRoles(Set<Role> roles) {
  this.roles.retainAll(roles);
  this.roles.addAll(roles);
}
}

Ancak, bu işe yaramaz ve bu durumda aslında oldukça yanıltıcı olan korkunç "artık başvurulmadı" hatası alırsınız.

Hazırda bekletme modunun setRoles yönteminizi çağırdığı ortaya çıkıyor VE özel koleksiyon sınıfının burada yüklü olmasını istiyor ve koleksiyon sınıfınızı kabul etmiyor. Bu, koleksiyon metodunuzda koleksiyonunuza atanmama ile ilgili tüm uyarıları okumama rağmen, UZUN bir süre için beni zorladı.

Bu yüzden bunu değiştirdim:

public class User {
  private Set<Role> roles = null;

  public void setRoles(Set<Role> roles) {
  if (this.roles == null) {
    this.roles = roles;
  } else {
    this.roles.retainAll(roles);
   this.roles.addAll(roles);
  }
}
}

Böylece ilk çağrıda, hazırda bekletme özel sınıfını yükler ve sonraki çağrılarda her şeyi mahvetmeden yöntemi kendiniz kullanabilirsiniz. Sınıfınızı fasulye olarak kullanmak istiyorsanız, muhtemelen çalışan bir ayarlayıcıya ihtiyacınız var ve bu en azından işe yarıyor gibi görünüyor.


2
Neden retainAll () öğesini çağırıyorsunuz? Neden clear () ve ardından addAll ()?
edbras

1
"Neden retainAll () diyorsun? Neden clear () ve ardından addAll ()? İyi soru, sanırım hibernate'in bunları tekrar eklemeden önce öğeleri kaldırmazsanız bunu bir veritabanı güncellemesi olarak görmesi daha az muhtemel olacağı varsayımı altında çalışıyordum. Ama yine de bu şekilde çalışmadığından şüpheleniyorum.
xpusostomos

2
RetainAll () öğesini clear () üzerinde kullanmalısınız, aksi takdirde aynı set nesnesine geçtiğinizde rolleri silebilirsiniz. Örneğin: user.setRoles (user.getRoles ()) == user.roles.clear ()
Martin

2
OrphanRemoval = true değerini ayarladığınızda ve bu koleksiyonların boş olduğu bir kayıt oluşturduğunuzda, bu hatayı da alırsınız. Yani: a, yetimremoval = true değerine sahip oneToMany b'ye sahiptir. B = null olduğunda A oluşturduğunuzda, bu sorun tetiklenir. Başlatma ve sonlandırma çözümünüz en iyi gibi görünüyor.
Lawrence

Teşekkür ederim! Listemi olarak başlatıyordum List<String> list = new ArrayList<>();. List<String> list = null;Sorunu
Radical

19

Aslında benim sorunum, varlıklarımın eşit ve hashcode'u ile ilgiliydi. Eski bir kod çok fazla sorun getirebilir, asla kontrol etmeyi unutmayın. Yaptığım tek şey silmek-yetim stratejisini tutmak ve eşittir ve hashcode'u düzeltmekti.


9
lütfen, iyi yapılmış bir eşittir ve hashCode yöntemleri bir örnek? çünkü çok fazla sorunum var: ya da setimi güncelleyemiyorum ya da bir StackOverflow hatası alıyorum. Burada bir soru açtım: stackoverflow.com/questions/24737145/… . teşekkürler
SaganTheBest

11

Aynı hatayla karşılaştım. Benim için sorun, varlığı kaydettikten sonra eşlenen koleksiyonun hala boş olması ve varlığı güncellemeye çalışırken kural dışı durumun atılmış olmasıydı. Bana ne yardımcı oldu: Varlığı kaydettikten sonra bir yenileme yapın (koleksiyon artık boş değil) ve ardından güncellemeyi gerçekleştirin. Belki de koleksiyonun yeni ArrayList () veya başka bir şeyle başlatılması da yardımcı olabilir.


Boş yerine yeni ArrayList göndermek benim için çalıştı. Thanks
rpajaziti

4

İLİŞKİ TÜRÜ VARDIR:


Koleksiyonu bildirildiğinde koleksiyonu somutlaştırmaya çalışmayın hasMany, sadece nesneleri ekleyin ve kaldırın.

class Parent {
    static hasMany = [childs:Child]
}

İLİŞKİ TÜRÜ:


Ancak koleksiyon yalnızca bir özellik (kullanım ilişkisi) olarak bildirildiğinde ve bildirimde başlatılmadığında boş olabilir.

class Parent {
    List<Child> childs = []
}

4

@ User2709454 yaklaşımını küçük bir gelişme ile kullandım.

public class User {
    private Set<Role> roles;

    public void setRoles(Set<Role> roles) {
        if (this.roles == null) {
            this.roles = roles;
        } else if(this.roles != roles) { // not the same instance, in other case we can get ConcurrentModificationException from hibernate AbstractPersistentCollection
            this.roles.clear();
            if(roles != null){
                this.roles.addAll(roles);
            }
        }
    }
}

3

Kullanmaya çalışırken bu sorunu yaşadım TreeSet. Ben initialize yaptığı oneToManyile TreeSethangi işlerin

@OneToMany(mappedBy = "question", fetch = FetchType.EAGER, cascade = { CascadeType.ALL }, orphanRemoval=true)
@OrderBy("id")
private Set<WizardAnswer> answers = new TreeSet<WizardAnswer>();

Ancak, bu questionyukarıda açıklanan hatayı getirecektir . Yani hibernatedestekleniyor gibi görünüyor SortedSetve eğer biri sadece yukarıdaki satırı değiştirirse

@OneToMany(mappedBy = "question", fetch = FetchType.EAGER, cascade = { CascadeType.ALL }, orphanRemoval=true)
@OrderBy("id")
private SortedSet<WizardAnswer> answers;

sihir gibi çalışır :) hakkında daha fazla bilgi buradahibernate SortedSet olabilir


Belki, tek bir boş liste kullanmak için Collections.emptySet () kullanıcısını kullanmak daha iyidir
ryzhman

3

Bu hatayı aldığım tek zaman, koleksiyon için ayarlayıcıya NULL iletmeye çalıştığım zamandır. Bunu önlemek için ayarlayıcılarım şöyle görünür:

public void setSubmittedForms(Set<SubmittedFormEntity> submittedForms) {
    if(submittedForms == null) {
        this.submittedForms.clear();
    }
    else {
        this.submittedForms = submittedForms;
    }
}

3

Bir varlık bir JSON posta isteği ile güncellenirken bu koştu. Hata, varlığı çocuklar hakkında veri olmadan güncellediğimde, hiçbiri olmasa bile oluştu. Ekleme

"children": [],

talep gövdesi sorunu çözdü.


1

Başka bir neden lombok kullanıyor olabilir.

@Builder- Collections.emptyList()diyor olsanız bile tasarruf nedenleri.myCollection(new ArrayList());

@Singular- sınıf alanı varsayılanları olarak yok nullsayılır ve sınıf alanı olarak bildirilse bile alandan ayrılırmyCollection = new ArrayList()

Benim 2 sent, sadece aynı ile 2 saat geçirdim :)



1

Ben aynı koleksiyonu için ekstra bir alan açıyorum çünkü doğrudan üzerine yazmak değil rağmen, Bahar Boot kullanarak ve bir koleksiyonu ile bu sorunu vardı am özel seri hale ve deserializer sırayla daha ön uç dostu temsilini sağlamak veri:

  public List<Attribute> getAttributes() {
    return attributes;
  }

  public void setAttributes(List<Attribute> attributes) {
    this.attributes = attributes;
  }

  @JsonSerialize(using = AttributeSerializer.class)
  public List<Attribute> getAttributesList() {
    return attributes;
  }

  @JsonDeserialize(using = AttributeDeserializer.class)
  public void setAttributesList(List<Attribute> attributes) {
    this.attributes = attributes;
  }

Ben Koleksiyon üzerine yazma değilim halde görünüyor kendim , seri kaldırma hepsi aynı bu sorunu tetikleyen, kaputun altında yapıyor. Çözüm, serileştiriciyle ilişkili ayarlayıcıyı değiştirmekti, böylece listeyi temizleyecek ve üzerine yazmak yerine her şeyi ekleyecekti:

  @JsonDeserialize(using = AttributeDeserializer.class)
  public void setAttributesList(List<Attribute> attributes) {
    this.attributes.clear();
    this.attributes.addAll(attributes);
  }

1

Aynı sorunu yaşadım, ama set boştu. Sadece Liste çalışmalarında Set koleksiyonunda bulun. JPA ek açıklaması fetch = FetchType.EAGER öğesinde hazırda bulunan hazırda bekletme ek açıklama @LazyCollection (LazyCollectionOption.FALSE) için deneyebilirsiniz.

Benim çözümüm: Bu benim yapılandırmam ve iyi çalışıyor

@OneToMany(mappedBy = "format", cascade = CascadeType.ALL, orphanRemoval = true)
@LazyCollection(LazyCollectionOption.FALSE)
private Set<Barcode> barcodes;

@OneToMany(mappedBy = "format", cascade = CascadeType.ALL, orphanRemoval = true)
@LazyCollection(LazyCollectionOption.FALSE)
private List<FormatAdditional> additionals;

1
@OneToMany(mappedBy = 'parent', cascade= CascadeType.ALL, orphanRemoval = true)
List<Child> children = new ArrayList<>();

Varolan Alt Nesneler listesine alt nesne eklerken de aynı hatayla karşılaştım.

childService.saveOrUpdate(child);
parent.addToChildren(child);
parentService.saveOrUpdate(parent);

Sorunumu ne çözdü:

child = childService.saveOrUpdate(child);

Şimdi çocuk diğer detaylarla da canlanıyor ve iyi çalıştı.


0

Aptalca cevabımı ekliyorum. Spring Data Rest kullanıyoruz. Bu oldukça standart ilişkimizdi. Desen başka bir yerde kullanıldı.

//Parent class
@OneToMany(mappedBy = 'parent', 
           cascade= CascadeType.ALL, orphanRemoval = true)
@LazyCollection(LazyCollectionOption.FALSE)
List<Child> children = new LinkedList<>()


//Child class
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = 'ParentID', updatable = false)
@JsonBackReference
Parent parent

Yarattığımız ilişki ile her zaman çocukların kendi repoları aracılığıyla eklenmesi amaçlanmıştı. Repoyu henüz eklememiştim. Sahip olduğumuz entegrasyon testi, REST çağrıları aracılığıyla işletmenin tam bir yaşam döngüsünden geçiyordu, böylece işlemler istekler arasında kapanacaktı. Çocuk için hiçbir repo, json'un çocuklarını ana yapının bir parçası olarak yerine getirdiği anlamına geliyordu _embedded. Üst öğede yapılan güncellemeler sorunlara neden olur.


0

Aşağıdaki çözüm benim için çalıştı

//Parent class
@OneToMany(mappedBy = 'parent', 
           cascade= CascadeType.ALL, orphanRemoval = true)
@OrderBy(value="ordinal ASC")
List<Child> children = new ArrayList<>()

//Updated setter of children 
public void setChildren(List<Children> children) {
    this.children.addAll(children);
    for (Children child: children)
        child.setParent(this);
}


//Child class
@ManyToOne
@JoinColumn(name="Parent_ID")
private Parent parent;

0

Yeni koleksiyon atamak yerine

public void setChildren(Set<ChildEntity> children) {
    this.children = children;
}

Tüm öğeleri

public void setChildren(Set<ChildEntity> children) {
    Collections.replaceAll(this.children,children);
}

0

dikkatli ol

BeanUtils.copyProperties(newInsum, insumOld,"code");

Bu yöntem de hazırda bekletme modunu bozar.



0

Benimki Spring Boot ile tamamen farklıydı! Benim için bunun nedeni koleksiyon özelliğini ayarlamak değildi.

Testlerimde bir varlık oluşturmaya çalışıyordum ve kullanılmayan başka bir koleksiyon için bu hatayı alıyordum!

Çok denedikten sonra ben sadece bir @Transactionaltest yöntemi ekledim ve çözdü. Gerçi sebep yok.


0

Bu önceki yanıtların aksine, ben tamamen aynı hata vardı: setter işlevi şöyle görünüyordu "cascade =” all-delete-yetim "bir koleksiyon artık başvurulan değil ....":

public void setTaxCalculationRules(Set<TaxCalculationRule> taxCalculationRules_) {
    if( this.taxCalculationRules == null ) {
        this.taxCalculationRules = taxCalculationRules_;
    } else {
        this.taxCalculationRules.retainAll(taxCalculationRules_);
        this.taxCalculationRules.addAll(taxCalculationRules_);
    }
}

Ve sonra basit sürüme değiştirdiğimde kayboldu:

public void setTaxCalculationRules(Set<TaxCalculationRule> taxCalculationRules_) {
    this.taxCalculationRules = taxCalculationRules_;
}

(hazırda bekleme sürümleri - hem 5.4.10 hem de 4.3.11'i denedi. Ayarlayıcıdaki basit atamaya geri dönmeden önce her türlü çözümü denemek için birkaç gün geçirdim.


0

Benim durumumda, birkaç iş parçacığından bir Hazırda Bekletme Oturumuna eşzamanlı erişim oldu. Ben 10 boyutu ile sayfa isteği ile varlıkları getirilen Spring Boot Toplu ve RepositoryItemReader uygulaması vardı .

Örneğin varlıklarım:

@Entity
class JobEntity {
    @ManyToOne(fetch = FetchType.LAZY)
    private GroupEntity group;
}

@Entity
class GroupEntity {
    @OneToMany(mappedBy = "group", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
    private Set<Config> configs; 
}

Toplu işlem: reader -> processor -> writerbir işlemde.

Bu varlıkların yapılandırmasında, GroupEntity diğer iş parçacıklarına kaçabilir:

Okuma bölümüne girilen ilk iş parçacığı, JobEntity sayfasını 10 boyutunda alır (RepositoryItemReader # doRead), bu öğeler bir paylaşılan GroupEntity nesnesi içerir (çünkü hepsi aynı grup kimliğine işaret etmiştir). Sonra ilk varlığı alır. Bölümü okumak için gelen sonraki iş parçacığı bu sayfa bitene kadar JobEntity bu sayfadan tek tek alır.

Böylece iş parçacıkları JobEntity örneklerini düşünen aynı GroupEntity örneğine erişebilir, bu da bir Hazırda Bekletme Oturumuna güvenli olmayan çok iş parçacıklı erişimdir .

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.