使用JPA和Hibernate有效删除数据
您可能會(huì)遇到必須對(duì)關(guān)系數(shù)據(jù)庫中存儲(chǔ)的大量數(shù)據(jù)集執(zhí)行批量刪除的情況。 如果您將JPA與Hibernate一起用作基礎(chǔ)OR映射器,則可以嘗試通過以下方式調(diào)用EntityManager的remove()方法:
首先,我們加載要?jiǎng)h除的實(shí)體的引用表示形式,然后將此引用傳遞給EntityManager。 假設(shè)上面的RootEntity與名為ChildEntity的類有子關(guān)系:
@OneToMany(mappedBy = "rootEntity", fetch = FetchType.EAGER, cascade = CascadeType.ALL) private Set childEntities = new HashSet(0);如果現(xiàn)在打開hibernate的屬性show_sql,我們將想知道發(fā)出什么SQL語句:
selectrootentity0_.id as id5_1_,rootentity0_.field1 as field2_5_1_,rootentity0_.field2 as field3_5_1_,childentit1_.PARENT as PARENT5_3_,childentit1_.id as id3_,childentit1_.id as id4_0_,childentit1_.field1 as field2_4_0_,childentit1_.field2 as field3_4_0_,childentit1_.PARENT as PARENT4_0_fromROOT_ENTITY rootentity0_left outer joinCHILD_ENTITY childentit1_on rootentity0_.id=childentit1_.PARENTwhererootentity0_.id=?deletefromCHILD_ENTITYwhereid=?deletefromROOT_ENTITYwhereid=?為什么Hibernate首先將所有數(shù)據(jù)加載到內(nèi)存中以便隨后立即刪除該數(shù)據(jù)? 原因是JPA的生命周期要求該對(duì)象處于“托管”狀態(tài),然后才能將其刪除。 僅在這種狀態(tài)下,所有生命周期功能(如攔截器)才可用(請(qǐng)參閱此處 )。 因此,Hibernate會(huì)在刪除之前發(fā)出SELECT查詢,以便將RootEntity和ChildEntity都轉(zhuǎn)移到“托管”狀態(tài)。 但是,如果我們只想刪除RootEntity和ChildEntity,并且知道RootEntity的ID,該怎么辦? 答案是使用簡(jiǎn)單的DELETE查詢,如下面的查詢。 但是由于子表的完整性約束,我們首先必須刪除所有依賴的子實(shí)體。 以下代碼演示了如何:
List childIds = entityManager.createQuery("select c.id from ChildEntity c where c.rootEntity.id = :pid").setParameter("pid", id).getResultList(); for(Long childId : childIds) {entityManager.createQuery("delete from ChildEntity c where c.id = :id").setParameter("id", childId).executeUpdate(); } entityManager.createQuery("delete from RootEntity r where r.id = :id").setParameter("id", id).executeUpdate();上面的代碼通過調(diào)用remove()產(chǎn)生了我們期望的三個(gè)SQL語句。 現(xiàn)在您可能會(huì)說,這種刪除方式比僅調(diào)用EntityManager的remove()方法更為復(fù)雜。 它還會(huì)忽略我們已放置在兩個(gè)實(shí)體類中的注解,例如@OneToMany和@ManyToOne。 那么,為什么不編寫一些代碼來使用關(guān)于兩個(gè)類文件中已經(jīng)存在的兩個(gè)實(shí)體的知識(shí)呢? 首先,我們?cè)赗ootEntity類中使用反射查找@OneToMany批注,提取子實(shí)體的類型,然后查找其后向字段,并用@ManyToOne批注。 完成此操作后,我們可以輕松地以更通用的方式編寫三個(gè)SQL語句:
public void delete(EntityManager entityManager, Class parentClass, Object parentId) {Field idField = getIdField(parentClass);if (idField != null) {List oneToManyFields = getOneToManyFields(parentClass);for (Field field : oneToManyFields) {Class childClass = getFirstActualTypeArgument(field);if (childClass != null) {Field manyToOneField = getManyToOneField(childClass, parentClass);Field childClassIdField = getIdField(childClass);if (manyToOneField != null && childClassIdField != null) {List childIds = entityManager.createQuery(String.format("select c.%s from %s c where c.%s.%s = :pid", childClassIdField.getName(), childClass.getSimpleName(), manyToOneField.getName(), idField.getName())).setParameter("pid", parentId).getResultList();for (Long childId : childIds) {entityManager.createQuery(String.format("delete from %s c where c.%s = :id", childClass.getSimpleName(), childClassIdField.getName())).setParameter("id", childId).executeUpdate();}}}}entityManager.createQuery(String.format("delete from %s e where e.%s = :id", parentClass.getSimpleName(), idField.getName())).setParameter("id", parentId).executeUpdate();} }上面的代碼中的方法getFirstActualTypeArgument(),getManyToOneField(),getIdField()和getOneToManyFields()并未在此處顯示,但是聽起來像它們的名字。 實(shí)施后,我們可以輕松刪除所有以樹的根開頭的實(shí)體。
- 可以在github上找到一個(gè)可用于檢查上述行為和解決方案的簡(jiǎn)單示例應(yīng)用程序。
翻譯自: https://www.javacodegeeks.com/2013/11/efficiently-delete-data-with-jpa-and-hibernate.html
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的使用JPA和Hibernate有效删除数据的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 优派发布新款 VX1655 便携显示器:
- 下一篇: 银行卡理财需要签约吗?