2011-07-11 53 views
16

問題標題基本上說明了一切。 JPA /休眠可以優雅地阻止從數據庫中刪除實體嗎?我想要的是將該實體標記爲「隱藏」而不是實際刪除它。JPA/Hibernate - 防止PreRemove處理程序中的刪除?

我也希望保留Cascade語義,這樣如果我試圖刪除一個擁有其他實體集合的實體,那麼擁有實體和集合中的每個實體都會被標記爲隱藏,而不需要額外的工作除了實施@PreRemove處理程序之外,我必須執行此操作,以防止刪除並將實體標記爲隱藏。

這是可能的,還是我需要找出其他方法?

+0

您是否試圖模仿使用JPA的邏輯刪除? –

+0

@Vineet雷諾茲 - 本質上,是的。我希望記錄保持在周圍,因爲應用程序與移動客戶端同步,並且需要能夠告訴客戶端已刪除的內容。我可以通過保留一個單獨表中刪除的所有內容的ID列表以及它被刪除的時間戳來解決這個問題,但我希望能夠覆蓋刪除行爲,以便它只是隱藏事物而不是刪除它們。 – aroth

回答

12

是否有可能在JPA/Hibernate中優雅地阻止從數據庫中刪除實體?

是的,只要你避免使用EntityManager.remove(entity)這是可能的。如果您確實使用EntityManager.remove(),那麼JPA提供程序將使用相應的SQL DELETE語句將該對象標記爲刪除,這意味着一旦將該對象標記爲刪除對象,將無法實現優雅的解決方案。

在休眠中,您可以使用@SQLDelete and @Where annotations來實現此目的。但是,這對於JPA並不會很好,因爲已知EntityManager.find()忽略了@Where註釋中指定的過濾器。

因此,僅限JPA的解決方案將涉及在實體類中添加標誌(即列),以將數據庫中的邏輯刪除實體與「活動」實體區分開來。您將需要使用適當的查詢(JPQL和本地)來確保邏輯刪除的實體在結果集中不可用。您可以使用​​和@PrePersist註釋來掛鉤實體生命週期事件,以確保該標誌在持續更新事件時進行更新。同樣,您需要確保您不會調用EntityManager.remove方法。

我會使用@PreRemove註釋鉤到被觸發的去除實體的生命週期事件建議,但使用的實體監聽器,以防止刪除是充滿着低於陳述麻煩的原因:

  • 如果需要防止邏輯意義上的SQL DELETE發生,則需要在同一事務中保留對象以重新創建對象*。唯一的問題是,在EntityListener中引用EntityManager並且通過推理在偵聽器中調用EntityManager.persist不是一個好的設計決策。理由非常簡單 - 您可能可能最終在EntityListener中獲得不同的EntityManager引用,這隻會導致您的應用程序中模糊和混亂的行爲。
  • 如果您需要防止事務本身中的SQL DELETE發生,那麼您必須在EntityListener中引發Exception。這通常會導致回滾事務(特別是如果Exception是RuntimeException或聲明爲導致回滾的應用程序異常),並且不提供任何好處,因爲整個事務將被回滾。

如果您在使用,而不是休眠的EclipseLink的選項,那麼它看來,如果你定義一個適當的DescriptorCustomizer或使用AdditionalCriteria標註一個優雅的解決方案是可能的。這兩種方法似乎都適用於EntityManager.removeEntityManager.find調用。但是,您可能仍然需要編寫您的JPQL或本機查詢來說明邏輯刪除的實體。


*這在JPA Wikibook on the topic of cascading Persist概述:

,如果你刪除一個對象將它刪除,如果你再調用持久化對象,它會復活的對象,而且它會再次堅持下去。如果這是故意的,這可能是需要的,但JPA規範也需要這種行爲級聯持續。因此,如果您刪除一個對象,但忘記從級聯持久關係中刪除對它的引用,則刪除將被忽略。

+0

啊,我一直希望能夠使用'EntityManager.remove()'來使它工作。但正如你所說,沒有可靠的方法在偵聽器中獲得正確的EntityManager實例。我已經按照你的建議走了很長的路,並用更新取代了我的刪除,這是行之有效的。但是,我希望JPA允許爲這個用例提供更優雅的解決方案。 – aroth

+0

是的,我同意在JPA中應該包含更優雅的解決方案,以便可以將em.remove()映射到本機SQL查詢或JPQL語句,並在適當的位置指定此自定義行爲 - 在實體上或其他地方。 –