Katakanlah saya memiliki dua entitas: Grup dan Pengguna. Setiap pengguna dapat menjadi anggota dari banyak grup dan setiap grup dapat memiliki banyak pengguna.
@Entity
public class User {
@ManyToMany
Set<Group> groups;
//...
}
@Entity
public class Group {
@ManyToMany(mappedBy="groups")
Set<User> users;
//...
}
Sekarang saya ingin menghapus grup (misalkan memiliki banyak anggota).
Masalahnya adalah ketika saya memanggil EntityManager.remove () di beberapa Grup, penyedia JPA (dalam kasus saya Hibernate) tidak menghapus baris dari tabel gabungan dan operasi penghapusan gagal karena kendala kunci asing. Memanggil remove () pada Pengguna berfungsi dengan baik (saya kira ini ada hubungannya dengan memiliki sisi hubungan).
Jadi bagaimana cara menghapus grup dalam kasus ini?
Satu-satunya cara yang bisa saya lakukan adalah memuat semua pengguna di grup, lalu untuk setiap pengguna, hapus grup saat ini dari grupnya dan perbarui pengguna. Tetapi tampaknya konyol bagi saya untuk memanggil update () pada setiap pengguna dari grup hanya untuk dapat menghapus grup ini.
Berikut ini bekerja untuk saya. Tambahkan metode berikut ke entitas yang bukan pemilik hubungan (Grup)
@PreRemove private void removeGroupsFromUsers() { for (User u : users) { u.getGroups().remove(this); } }
Perlu diingat bahwa agar ini berfungsi, Grup harus memiliki daftar Pengguna yang diperbarui (yang tidak dilakukan secara otomatis). jadi setiap kali Anda menambahkan Grup ke daftar grup di entitas Pengguna, Anda juga harus menambahkan Pengguna ke daftar pengguna di entitas Grup.
sumber
Saya menemukan solusi yang mungkin, tetapi ... Saya tidak tahu apakah itu solusi yang baik.
@Entity public class Role extends Identifiable { @ManyToMany(cascade ={CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH}) @JoinTable(name="Role_Permission", joinColumns=@JoinColumn(name="Role_id"), inverseJoinColumns=@JoinColumn(name="Permission_id") ) public List<Permission> getPermissions() { return permissions; } public void setPermissions(List<Permission> permissions) { this.permissions = permissions; } } @Entity public class Permission extends Identifiable { @ManyToMany(cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH}) @JoinTable(name="Role_Permission", joinColumns=@JoinColumn(name="Permission_id"), inverseJoinColumns=@JoinColumn(name="Role_id") ) public List<Role> getRoles() { return roles; } public void setRoles(List<Role> roles) { this.roles = roles; }
Saya telah mencoba ini dan berhasil. Saat Anda menghapus Peran, relasi juga akan dihapus (tetapi bukan entitas Izin) dan saat Anda menghapus Izin, relasi dengan Peran juga dihapus (tetapi bukan instance Peran). Tapi kami memetakan relasi searah dua kali dan kedua entitas adalah pemilik relasi. Mungkinkah ini menyebabkan masalah pada mode Hibernasi? Jenis masalah apa?
Terima kasih!
Kode di atas berasal dari postingan lain yang terkait.
sumber
Sebagai alternatif untuk solusi JPA / Hibernate: Anda dapat menggunakan klausa CASCADE DELETE dalam definisi database dari kunci foregin Anda di tabel gabungan, seperti (sintaks Oracle):
CONSTRAINT fk_to_group FOREIGN KEY (group_id) REFERENCES group (id) ON DELETE CASCADE
Dengan cara itu DBMS sendiri secara otomatis menghapus baris yang mengarah ke grup saat Anda menghapus grup. Dan itu berfungsi apakah penghapusan dibuat dari Hibernate / JPA, JDBC, secara manual di DB atau dengan cara lain.
fitur penghapusan kaskade didukung oleh semua DBMS utama (Oracle, MySQL, SQL Server, PostgreSQL).
sumber
Untuk apa nilainya, saya menggunakan EclipseLink 2.3.2.v20111125-r10461 dan jika saya memiliki hubungan searah @ManyToMany, saya mengamati masalah yang Anda gambarkan. Namun, jika saya mengubahnya menjadi hubungan dua arah @ManyToMany, saya dapat menghapus entitas dari sisi non-pemilik dan tabel JOIN diperbarui dengan tepat. Ini semua tanpa menggunakan atribut kaskade apa pun.
sumber
Ini bekerja untuk saya:
@Transactional public void remove(Integer groupId) { Group group = groupRepository.findOne(groupId); group.getUsers().removeAll(group.getUsers()); // Other business logic groupRepository.delete(group); }
Juga, tandai metode @Transactional (org.springframework.transaction.annotation.Transactional), ini akan melakukan seluruh proses dalam satu sesi, menghemat waktu.
sumber
Ini adalah solusi yang bagus. Bagian terbaiknya ada di sisi SQL - menyempurnakan ke level mana pun itu mudah.
Saya menggunakan MySql dan MySql Workbench ke Cascade saat menghapus untuk KUNCI Asing yang Diperlukan.
ALTER TABLE schema.joined_table ADD CONSTRAINT UniqueKey FOREIGN KEY (key2) REFERENCES schema.table1 (id) ON DELETE CASCADE;
sumber
Inilah yang akhirnya saya lakukan. Semoga seseorang dapat merasakan manfaatnya.
@Transactional public void deleteGroup(Long groupId) { Group group = groupRepository.findById(groupId).orElseThrow(); group.getUsers().forEach(u -> u.getGroups().remove(group)); userRepository.saveAll(group.getUsers()); groupRepository.delete(group); }
sumber
Untuk kasus saya, saya menghapus mappedBy dan menggabungkan tabel seperti ini:
@ManyToMany(cascade = CascadeType.ALL) @JoinTable(name = "user_group", joinColumns = { @JoinColumn(name = "user", referencedColumnName = "user_id") }, inverseJoinColumns = { @JoinColumn(name = "group", referencedColumnName = "group_id") }) private List<User> users; @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER) @JsonIgnore private List<Group> groups;
sumber
Ini berfungsi untuk saya pada masalah serupa di mana saya gagal menghapus pengguna karena referensi. Terima kasih
@ManyToMany(cascade = {CascadeType.MERGE, CascadeType.PERSIST,CascadeType.REFRESH})
sumber