Hibernate - Koleksi dengan cascade = "all-delete-yatim" tidak lagi dirujuk oleh instance entitas yang memiliki

225

Saya mengalami masalah berikut ketika mencoba memperbarui entitas saya:

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

Saya memiliki entitas induk dan entitas Set<...>entitas anak. Ketika saya mencoba untuk memperbaruinya, saya mendapatkan semua referensi untuk diset ke koleksi ini dan mengaturnya.

Kode berikut mewakili pemetaan saya:

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

Saya sudah mencoba untuk membersihkan Set <..> saja, sesuai dengan ini: Cara "mungkin" menyelesaikan masalah tetapi tidak berhasil.

Jika Anda punya ide, beri tahu saya.

Terima kasih!

axcdnt
sumber
1
@ mel3kings, tautan yang Anda berikan tidak lagi aktif.
Opal
coba gunakan koleksi yang bisa berubah ketika menghapus elemen. Misalnya jangan gunakan something.manyother.remove(other)jika manyothera List<T>. Buat banyak yang bisa berubah, suka ArrayList<T>dan gunakanorphanDelete = true
nurettin

Jawaban:

220

Periksa semua tempat di mana Anda menetapkan sesuatu untuk sonEntities. Tautan yang Anda referensikan secara jelas menunjukkan pembuatan HashSet baru tetapi Anda dapat memiliki kesalahan ini setiap kali Anda menetapkan ulang set. Sebagai contoh:

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

Biasanya Anda hanya ingin "baru" set sekali dalam konstruktor. Setiap kali Anda ingin menambah atau menghapus sesuatu ke dalam daftar, Anda harus mengubah isi daftar alih-alih menetapkan daftar baru.

Untuk menambah anak:

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

Untuk menghapus anak-anak:

public void removeChild(SonEntity aSon)
{
    this.sonEntities.remove(aSon);
}
brainimus
sumber
8
Sebenarnya, masalah saya adalah tentang sama dan kode hash entitas saya. Kode lama dapat menyebabkan banyak masalah, jangan pernah lupa untuk memeriksanya. Yang saya lakukan hanyalah terus menghapus strategi anak yatim dan memperbaiki persamaan dan kode hash.
axcdnt
6
Saya senang Anda menyelesaikan masalah Anda. sama dengan dan kode hash telah menggigit saya beberapa kali dengan Hibernate. Alih-alih memperbarui judul pertanyaan dengan "[Diselesaikan]" Anda harus melanjutkan dan memposting jawaban Anda dan kemudian menandainya sebagai jawaban yang diterima.
brainimus
Terima kasih, saya bertemu dengan sesuatu yang serupa dan ternyata setter saya untuk Set itu kosong ..
Siddhartha
ya bahkan pada daftar kosong Anda mendapatkan kesalahan ini. saya tidak akan berharap itu karena tidak ada anak yatim (daftar itu kosong).
tibi
4
Biasanya Anda hanya ingin "baru" set sekali dalam konstruktor. Setiap kali Anda ingin menambah atau menghapus sesuatu ke dalam daftar, Anda harus mengubah isi daftar alih-alih menetapkan daftar baru. Paling imp
Nikhil Sahu
109

Metode:

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

berfungsi jika parentEntityterlepas dan lagi jika kita memperbaruinya.
Tetapi jika entitas tidak terlepas dari per konteks, (yaitu menemukan dan memperbarui operasi dalam transaksi yang sama) metode di bawah ini berfungsi.

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);
    }
}
Manu
sumber
1
@ Skuld Saya punya masalah serupa dan saya menerapkan solusi Anda (dalam metode setter saya menghapus koleksi anak-anak - this.children.clear () - dan saya menambahkan anak-anak baru - this.children.addAll (anak-anak)). Perubahan ini tidak memperbaiki masalah saya. Saya masih mendapatkan "A collection with cascade =" all-delete-yatim "tidak lagi dirujuk oleh instance entitas pemilik". Apakah Anda punya ide mengapa? Terima kasih banyak!
ovdsrn
@ovdsrn Maaf ini bukan jawaban saya, saya hanya memilah format jawaban, penulis asli (kmmanu) mungkin dapat membantu Anda (atau alt Anda mungkin ingin memulai pertanyaan baru jika skenario Anda berbeda dengan pertanyaan asli diajukan di sini) Semoga beruntung
Skuld
1
Ini bekerja dengan baik, tetapi tidak begitu baik ketika anak-anak terkandung dalam komponen hibernate bersarang dan komponen dibuat nol di Entity-nya. Maka Anda mendapatkan kesalahan yang sama. Ini karena pemilik anak-anak adalah Entitas root dan bukan komponen yang dibuat null ... Komponen seperti itu, komponen tidak pernah boleh menjadi nol, melainkan menghasilkan metode forward to destroy () dalam metode anak ... Setidaknya, saya tidak tahu solusi yang lebih baik ... Ini semacam koleksi yang jelas () konstruksi tetapi kemudian pada komponen melalui menghancurkan () ...
edbras
Lihat juga posting ini untuk detail lebih lanjut di atas: stackoverflow.com/questions/4770262/…
edbras
7
gunakan this.sonEntities.retainAll (aSet) di atas sonEntities.clear () karena jika aSet == this.sonEntities (yaitu objek yang sama), Anda akan menghapus set sebelum Anda menambahkannya!
Martin
33

Ketika saya membaca di berbagai tempat yang hibernate tidak suka Anda tetapkan ke koleksi, saya berasumsi bahwa hal paling aman untuk dilakukan adalah dengan membuatnya menjadi final seperti ini:

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

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

Namun, ini tidak berhasil, dan Anda mendapatkan kesalahan "tidak lagi direferensikan" yang ditakuti, yang sebenarnya cukup menyesatkan dalam kasus ini.

Ternyata hibernate memanggil metode setRoles Anda DAN ia ingin kelas koleksi khusus diinstal di sini, dan tidak akan menerima kelas koleksi Anda. Ini membuat saya bingung untuk waktu yang lama, meskipun membaca semua peringatan tentang tidak menetapkan koleksi Anda dalam metode yang Anda tentukan.

Jadi saya berubah menjadi ini:

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

Sehingga pada panggilan pertama, hibernate menginstal kelas khusus, dan pada panggilan selanjutnya Anda dapat menggunakan metode ini sendiri tanpa merusak semuanya. Jika Anda ingin menggunakan kelas Anda sebagai kacang, Anda mungkin memerlukan setter yang berfungsi, dan ini setidaknya berfungsi.

xpusostomos
sumber
2
Mengapa Anda memanggil retainAll ()? Mengapa tidak jelas () diikuti oleh addAll ()?
edbras
1
"Kenapa kamu memanggil retainAll ()? Kenapa tidak clear () diikuti oleh addAll ()?" Pertanyaan bagus, saya pikir saya sedang bekerja di bawah asumsi bahwa hibernate akan cenderung melihat ini sebagai pembaruan basis data jika Anda tidak menghapus item sebelum menambahkannya lagi. Tapi saya kira itu tidak akan berhasil.
xpusostomos
2
Anda harus menggunakan retainAll () di atas clear () jika tidak, Anda dapat menghapus peran jika Anda melewati objek set yang identik. Sebagai contoh: user.setRoles (user.getRoles ()) == user.roles.clear ()
Martin
2
Ketika Anda menetapkan orphanRemoval = true dan Anda membuat catatan di mana koleksi ini adalah nol, Anda juga mendapatkan kesalahan ini. Jadi: a memiliki oneToMany b dengan orphanremoval = true. Saat Anda membuat A di mana B = null, masalah ini dipicu. Solusi Anda untuk menginisialisasi dan menjadikannya final sepertinya yang terbaik.
Lawrence
Terima kasih! Saya menginisialisasi Daftar saya sebagai List<String> list = new ArrayList<>();. Mengubahnya untuk List<String> list = null;memperbaiki masalah :)
Radical
19

Sebenarnya, masalah saya adalah tentang sama dan kode hash entitas saya. Kode lama dapat menyebabkan banyak masalah, jangan pernah lupa untuk memeriksanya. Yang saya lakukan hanyalah terus menghapus strategi anak yatim dan memperbaiki persamaan dan kode hash.

axcdnt
sumber
9
tolong, contoh metode equalh dan hashCode yang dibuat dengan baik? karena saya memiliki banyak masalah: atau saya tidak dapat memperbarui set saya atau saya mendapatkan kesalahan StackOverflow. saya membuka pertanyaan di sini: stackoverflow.com/questions/24737145/… . terima kasih
SaganTheBest
11

Saya memiliki kesalahan yang sama. Masalahnya bagi saya adalah, bahwa setelah menyimpan entitas koleksi yang dipetakan masih nol dan ketika mencoba untuk memperbarui entitas pengecualian dilemparkan. Apa yang membantu saya: Menyimpan entitas, lalu menyegarkan (koleksi tidak lagi nol) dan kemudian melakukan pembaruan. Mungkin menginisialisasi koleksi dengan ArrayList baru () atau sesuatu mungkin membantu juga.

Konsumierer
sumber
Mengirim ArrayList baru bukan nol, bekerja untuk saya. Terima kasih
rpajaziti
4

MEMILIKI JENIS HUBUNGAN:


Jangan mencoba instantiate koleksi ketika dideklarasikan hasMany, cukup tambahkan dan hapus objek.

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

GUNAKAN JENIS HUBUNGAN:


Tetapi koleksi bisa menjadi nol hanya ketika dinyatakan sebagai properti (gunakan relasi) dan tidak diinisialisasi dalam deklarasi.

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

Saya menggunakan pendekatan @ user2709454 dengan sedikit peningkatan.

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);
            }
        }
    }
}
luwojtaszek
sumber
3

Saya memiliki masalah ini ketika mencoba menggunakan TreeSet. Saya menginisialisasi oneToManydengan TreeSetyang bekerja

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

Tapi, ini akan membawa kesalahan yang dijelaskan di questionatas. Jadi sepertinya itu hibernatedidukung SortedSetdan kalau kita ubah saja baris di atas menjadi

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

itu bekerja seperti sihir :) info lebih lanjut tentang hibernate SortedSetbisa ada di sini

ek
sumber
Mungkin, lebih baik pengguna Collections.emptySet () untuk menggunakan daftar kosong tunggal
ryzhman
3

Satu-satunya saat saya mendapatkan kesalahan ini adalah ketika saya mencoba meneruskan NULL ke setter untuk koleksi. Untuk mencegah ini, setter saya terlihat seperti ini:

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

Saya mengalami ini ketika memperbarui entitas dengan permintaan posting JSON. Kesalahan terjadi ketika saya memperbarui entitas tanpa data tentang anak-anak, bahkan ketika tidak ada. Menambahkan

"children": [],

ke tubuh permintaan memecahkan masalah.

bersama
sumber
1

Salah satu penyebab lainnya mungkin menggunakan lombok.

@Builder- Menyebabkan menyimpan Collections.emptyList()bahkan jika Anda mengatakan.myCollection(new ArrayList());

@Singular- Mengabaikan default level kelas dan meninggalkan bidang nullbahkan jika bidang kelas dinyatakan sebagaimyCollection = new ArrayList()

2 sen saya, hanya menghabiskan 2 jam dengan hal yang sama :)

Jan Zyka
sumber
1

Saya menggunakan Spring Boot dan memiliki masalah ini dengan koleksi, meskipun tidak secara langsung menimpanya, karena saya mendeklarasikan bidang tambahan untuk koleksi yang sama dengan serializer kustom dan deserializer untuk memberikan representasi yang lebih ramah-ramah dari frontend. data:

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

Tampaknya bahwa meskipun saya tidak Timpa koleksi sendiri , deserialization melakukannya di bawah tenda, memicu masalah ini semua sama. Solusinya adalah mengubah setter yang terkait dengan deserializer sehingga akan menghapus daftar dan menambahkan semuanya, daripada menimpanya:

  @JsonDeserialize(using = AttributeDeserializer.class)
  public void setAttributesList(List<Attribute> attributes) {
    this.attributes.clear();
    this.attributes.addAll(attributes);
  }
Sofia Paixão
sumber
1

Saya memiliki masalah yang sama, tetapi ketika set adalah nol. Hanya di Set koleksi di daftar pekerjaan temukan. Anda dapat mencoba anotasi hibernasi @LazyCollection (LazyCollectionOption.FALSE) yang ditanamkan pada anotasi JPA fetch = FetchType.EAGER.

Solusi saya: Ini adalah konfigurasi saya dan berfungsi dengan baik

@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;
Dario Gaston
sumber
1
@OneToMany(mappedBy = 'parent', cascade= CascadeType.ALL, orphanRemoval = true)
List<Child> children = new ArrayList<>();

Saya mengalami kesalahan yang sama ketika saya menambahkan objek anak ke daftar Objek Anak yang ada.

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

Apa yang memecahkan masalah saya berubah menjadi:

child = childService.saveOrUpdate(child);

Sekarang anak itu dihidupkan kembali dengan detail lain dan itu bekerja dengan baik.

Neha Gupta
sumber
0

Menambahkan jawaban bodoh saya. Kami menggunakan Spring Data Rest. Ini adalah hubungan standar kami yang cantik. Pola itu digunakan di tempat lain.

//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

Dengan hubungan yang kami ciptakan, selalu dimaksudkan agar anak-anak ditambahkan melalui repo mereka sendiri. Saya belum menambahkan repo. Tes integrasi yang kami lakukan sedang menjalani siklus hidup lengkap entitas melalui panggilan REST sehingga transaksi akan ditutup di antara permintaan. Tidak ada repo untuk anak berarti json memiliki anak-anak sebagai bagian dari struktur utama daripada di _embedded. Pembaruan ke orang tua kemudian akan menyebabkan masalah.

Snekse
sumber
0

Solusi berikut berhasil untuk saya

//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;
priyanka_rao
sumber
0

Alih-alih menugaskan koleksi baru

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

Ganti semua elemen dengan

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

hati-hati dengan

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

Metode ini juga memecah hibernate.

Diógenes Ricardo
sumber
0

Milik saya benar-benar berbeda dengan Spring Boot! Bagi saya itu bukan karena pengaturan properti koleksi.

Dalam pengujian saya, saya mencoba membuat entitas dan mendapatkan kesalahan ini untuk koleksi lain yang tidak digunakan!

Setelah mencoba begitu banyak, saya baru saja menambahkan @Transactionalmetode pengujian dan menyelesaikannya. Tapi jangan alasan.

madz
sumber
0

Ini berbeda dengan jawaban sebelumnya, saya memiliki kesalahan yang persis sama: "Koleksi dengan cascade =" all-delete-yatim piatu "tidak lagi dirujuk ...." ketika fungsi setter saya terlihat seperti ini:

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

Dan kemudian menghilang ketika saya mengubahnya ke versi sederhana:

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

(versi hibernasi - mencoba keduanya 5.4.10 dan 4.3.11. Menghabiskan beberapa hari mencoba segala macam solusi sebelum kembali ke tugas sederhana dalam setter. Bingung sekarang mengapa ini terjadi.)

ligett
sumber