使用Hibernate(JPA)一键式删除
在舊版本的Hibernate中,我可以看到手冊中指示的一鍵式刪除 。 但是較新的版本不再包含此部分。 我不知道為什么。 因此,在這篇文章中,我來看看它是否仍然有效。
一鍵式刪除部分顯示:
有時一個接一個地刪除收集元素可能效率極低。 對于新的空集合(例如,如果您調用list.clear() ,Hibernate不會這樣做。 在這種情況下,Hibernate將發出一個DELETE 。
假設您向大小為20的集合中添加了一個元素,然后刪除了兩個元素。 除非集合是一個包,否則Hibernate將發出一個INSERT語句和兩個DELETE語句。 這當然是可取的。
但是,假設我們刪除了18個元素,剩下兩個,然后添加了新元素。 有兩種可能的方式進行
- 一一刪除十八行,然后再插入三行
 - 在一個SQL DELETE刪除整個集合,并一一插入所有五個當前元素
 
Hibernate不知道第二個選項可能更快。 對于Hibernate來說,如此直觀可能是不希望的,因為這種行為可能會使數據庫觸發器混亂,等等。
幸運的是,您可以隨時通過丟棄(即取消引用)原始集合并返回帶有所有當前元素的新實例化集合來強制執行此行為(即第二種策略)。
一擊刪除不適用于映射為inverse="true"集合。
inverse="true"用于(休眠映射)XML。 但是在這篇文章中,我們將看到JPA (以Hibernate為提供者)如何進行“一次性刪除”。
我們將嘗試不同的方法,看看哪種方法會導致一次刪除。
我們將使用具有多個CartItem的Cart實體。
雙向一對多
對于這一點,我們從雙方的引用。
@Entity public class Cart { ...@OneToMany(mappedBy="cart", cascade=ALL, orphanRemoval=true)Collection<OrderItem> items; }@Entity public class CartItem { ...@ManyToOne Cart cart; }為了測試這一點,我們為Cart的表插入一行,為CartItem的表插入三行或更多行。 然后,我們運行測試。
public class CartTests { ...@Testpublic void testOneShotDelete() throws Exception {Cart cart = entityManager.find(Cart.class, 53L);for (CartItem item : cart.items) {item.cart = null; // remove reference to cart}cart.items.clear(); // as indicated in Hibernate manualentityManager.flush(); // just so SQL commands can be seen} }所示的SQL命令將每個項目分別刪除(而不是一次性刪除)。
delete from CartItem where id=? delete from CartItem where id=? delete from CartItem where id=?丟棄原始集合也不起作用。 它甚至引起了異常。
public class CartTests { ...@Testpublic void testOneShotDelete() throws Exception {Cart cart = entityManager.find(Cart.class, 53L);// remove reference to cartcart.items = new LinkedList<CartItem>(); // discard, and use new collectionentityManager.flush(); // just so SQL commands can be seen} }javax.persistence.PersistenceException:org.hibernate.HibernateException:A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance: ….Cart.items我用Hibernate 4.3.11和HSQL 2.3.2對此進行了測試。 如果您的結果有所不同,請點擊評論 。
單向一對多(帶連接表)
為此,我們對映射進行了更改。 這將導致創建一個聯接表。
@Entity public class Cart { ...@OneToMany(cascade=ALL)Collection<OrderItem> items; }@Entity public class CartItem { ...// no @ManyToOne Cart cart; }再次,我們為Cart的表插入一行,為CartItem的表插入三行或更多行。 我們還必須在連接表( Cart_CartItem )中插入適當的記錄。 然后,我們運行測試。
public class CartTests { ...@Testpublic void testOneShotDelete() throws Exception {Cart cart = entityManager.find(Cart.class, 53L);cart.items.clear(); // as indicated in Hibernate manualentityManager.flush(); // just so SQL commands can be seen} }顯示的SQL命令已刪除聯接表中的關聯行(使用一個命令)。 但是表中CartItem的行仍然存在(并且沒有被刪除)。
delete from Cart_CartItem where cart_id=? // no delete commands for CartItem嗯,不完全是我們想要的,因為CartItem表中的行仍然存在。
單向一對多(無聯接表)
從JPA 2.0開始,通過指定@JoinColumn可以避免單向一對多的連接表。
@Entity public class Cart { ...@OneToMany(cascade=CascadeType.ALL, orphanRemoval=true)@JoinColumn(name="cart_id", updatable=false, nullable=false)Collection<OrderItem> items; }@Entity public class CartItem { ...// no @ManyToOne Cart cart; }再次,我們為Cart的表插入一行,為CartItem的表插入三行或更多行。 然后,我們運行測試。
public class CartTests { ...@Testpublic void testOneShotDelete() throws Exception {Cart cart = entityManager.find(Cart.class, 53L);cart.items.clear(); // as indicated in Hibernate manualentityManager.flush(); // just so SQL commands can be seen} }丟棄原始集合也不起作用。 這也導致了相同的異常(如雙向一對多)。
javax.persistence.PersistenceException:org.hibernate.HibernateException:A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance: ….Cart.items單向一對多(帶有
JPA 2.0引入了@ElementCollection 。 這允許與許多側之中任建立一個一對多關系@Basic或@Embeddable (即不是@Entity )。
@Entity public class Cart { ...@ElementCollection // @OneToMany for basic and embeddables@CollectionTable(name="CartItem") // defaults to "Cart_items" if not overriddenCollection<OrderItem> items; }@Embeddable // not an entity! public class CartItem {// no @Id// no @ManyToOne Cart cart;private String data; // just so that there are columns we can set }再次,我們為Cart的表插入一行,為CartItem的表插入三行或更多行。 然后,我們運行測試。
public class CartTests { ...@Testpublic void testOneShotDelete() throws Exception {Cart cart = entityManager.find(Cart.class, 53L);cart.items.clear(); // as indicated in Hibernate manualentityManager.flush(); // just so SQL commands can be seen} }是的 CartItem的關聯行在CartItem被刪除。
delete from CartItem where Cart_id=?總結思想
使用ElementCollection以單向一對多的方式進行一次刪除( ElementCollection是嵌入式的,而不是實體)。
在單向一對多聯接表方案中,刪除聯接表中的條目不會增加太多價值。
我不確定為什么一鍵式刪除在Hibernate中起作用(或為什么這樣起作用)。 但是我確實有一個猜測。 那就是底層的JPA提供者不能一口氣刪除,因為它不能確保多端實體不會被其他實體引用。 與ElementCollection不同,多面不是實體,其他實體也不能引用。
現在,這并不意味著您必須一直使用ElementCollection 。 一次性刪除可能僅適用于聚合根。 在這些情況下,使用Embeddable和ElementCollection可能適合于組成聚合的值對象的集合。 當除去聚合根時,最好也應除去“子”對象(并以有效的方式)。
我希望JPA中有一種方法可以指示子實體是私有的,并且在刪除父實體時可以安全地刪除它們(例如,類似于EclipseLink中的@PrivateOwned )。 讓我們看看它是否將包含在API的將來版本中。
希望這可以幫助。
翻譯自: https://www.javacodegeeks.com/2016/07/one-shot-delete-hibernate-jpa.html
總結
以上是生活随笔為你收集整理的使用Hibernate(JPA)一键式删除的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: 一日掉粉超100万,李佳琦哭了
 - 下一篇: 拖动滑块拼图背景图没显示_计划B? 那是