2010-10-21 50 views
15

我知道刪除孤兒對象是SO上的一個常見問題,對於初次接觸Hibernate的人來說是一個常見問題,並且相當標準的答案是確保您在子集合上有一些cascade=all,delete-orphancascade=all-delete-orphan的變體。更新分離對象時,Hibernate是否可以刪除孤立的集合?

我希望能夠讓Hibernate檢測到已經從父對象清空/移除了子集合,並且在父對象更新時讓子表中的行從數據庫中刪除。例如:

Parent parent = session.get(...); 
parent.getChildren().clear(); 
session.update(parent); 

我對Parent類當前的映射是這樣的:

<bag name="children" cascade="all-delete-orphan"> 
    <key column="parent_id" foreign-key="fk_parent_id"/> 
    <one-to-many class="Child"/> 
</bag> 

更新附帶的對象時,這工作對我很好,但我有一個用例中,我們想能夠接受一個分離的對象(已經通過HTTP/JSON由遠程客戶端發送給我們的API方法),並將其直接傳遞給Hibernate Session - 以允許客戶端以任何方式操縱父對象他們喜歡並且變化依然存在。

在我分離的對象上調用session.update(parent)時,子表中的行是孤立的(FK列設置爲空)但未被刪除。請注意,當我打電話給session.update()時,這是Hibernate Session第一次看到這個對象實例 - 我沒有以任何其他方式重新附加或合併對象與Session。我依靠客戶端來傳遞標識符與數據庫中實際對象相對應的對象。舉例來說,在我的API服務的方法的邏輯是這樣的:

String jsonString = request.getParameter(...); 
Parent parent = deserialize(jsonString); 
session.update(parent); 

是否可以傳遞給session.update(parent)當Hibernate來檢測分離的父對象的孤兒收藏?或者我以某種方式誤用了分離的對象?

我的希望是,我可以避免與Hibernate進行任何複雜的交互,以將更改保留到分離的實例。在調用session.update(parent)後,我的API方法不需要進一步修改分離的對象,這種方法僅僅負責持久化遠程客戶端應用程序所做的更改。

回答

1

我認爲,當使用分離會話時,您可能會遇到問題,並帶有集合。我會建議你先用集合加載實體,然後用更改更新該實體,這將有所幫助。

+0

你的意思是,加載現有的實體,然後'合併( )'它與分離的實例傳遞給我的API? – 2010-10-27 13:27:31

+0

@ mattb:您可以編寫一個合併集合的邏輯,甚至可以替換以前的集合並設置新集合,但要確保已在您的hbm中添加了標籤。 這個將包含一個簡單的sql刪除查詢來刪除完整的集合,然後再保存一個新集合。或者以其他方式,無論你添加什麼東西,這個sql刪除將會每次被觸發,然後你的集合的新副本將被添加。這可以解決您每次手動更新集合的問題。 – 2010-10-27 13:31:46

+0

不知道我是否喜歡這個解決方案,因爲它需要改變我的使用模式 - 例如添加「」。希望這可以通過單獨的映射來解決。 – 2010-10-31 02:39:38

8

你映射(簡體)

<?xml version="1.0" encoding="UTF-8"?> 
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> 
<hibernate-mapping package="br.com._3988215.model.domain"> 
    <class name="Parent" table="PARENT"> 
     <id name="id"> 
      <generator class="native"/> 
     </id> 
     <bag cascade="all,delete-orphan" name="childList"> 
      <key column="PARENT_ID" not-null="false"/> 
      <one-to-many class="Child"/> 
     </bag> 
    </class> 
    <class name="Child" table="CHILD"> 
     <id name="id" column="CHILD_ID"> 
      <generator class="native"/> 
     </id> 
    </class> 
</hibernate-mapping> 

產生

PARENT 
    ID 

CHILD 
    CHILD_ID 
    PARENT_ID 

根據你所說的

我希望能有休眠檢測子集已從中刪除父對象,並有行子表從數據庫

喜歡的東西

Parent parent = session.get(...); 
parent.getChildren().clear(); 

session.update(parent); 

你說這工作得很好,因爲你有父對象被更新刪除附加的父實例

現在我們來看看下面的一個(注意Assert.assertNull(second)

public class WhatYouWantTest { 

    private static SessionFactory sessionFactory; 

    private Serializable parentId; 

    private Serializable firstId; 
    private Serializable secondId; 

    @BeforeClass 
    public static void setUpClass() { 
     Configuration c = new Configuration(); 
     c.addResource("mapping.hbm.3988215.xml"); 

     sessionFactory = c.configure().buildSessionFactory(); 
    } 

    @Before 
    public void setUp() throws Exception { 
     Parent parent = new Parent(); 
     Child first = new Child(); 
     Child second = new Child(); 

     Session session = sessionFactory.openSession(); 
     session.beginTransaction(); 

     parentId = session.save(parent); 
     firstId = session.save(first); 
     secondId = session.save(second); 

     parent.getChildList().add(first); 
     parent.getChildList().add(second); 

     session.getTransaction().commit(); 
     session.close(); 
    } 

    @Test 
    public void removed_second_from_parent_remove_second_from_database() { 
     Parent parent = new Parent(); 
     parent.setId((Integer) parentId); 

     Child first = new Child(); 
     first.setId((Integer) firstId); 

     /** 
      * It simulates the second one has been removed 
      */ 
     parent.getChildList().add(first); 

     Session session = sessionFactory.openSession(); 
     session.beginTransaction(); 

     session.update(parent); 

     session.getTransaction().commit(); 
     session.close(); 

     session = sessionFactory.openSession(); 
     session.beginTransaction(); 

     Child second = (Child) session.get(Child.class, secondId); 
     Assert.assertNull(second); 

     session.getTransaction().commit(); 
     session.close(); 
    } 
} 

可惜,測試不及格。你可以做什麼 ???

  • 啓用一個長期運行的對話

Hibernate參考說

擴展(或長)會話 - Hibernate的Session可以從底層的JDBC連接斷開的數據庫事務後已提交,並在發生新的客戶端請求時重新連接。這種模式被稱爲會話每會話,甚至不需要重新連接。自動版本控制用於隔離併發修改,通常不允許會話自動刷新,但明確地說。

免責聲明:我沒有任何使用長時間運行的對話的場景。 Java EE有狀態會話Bean支持長時間運行的對話。但它的支持是JPA(不休眠)

您可以創建一個替代映射,使您的孩子作爲複合元素。因爲它的生命週期取決於父對象上,你可以依靠的複合元素得到你想要的

創建一個名爲AlternativeParent 類擴展父

public class AlternativeParent extends Parent {} 

現在它的映射(注意兒童作爲複合元素是什麼而非純@Entity)

<class name="AlternativeParent" table="PARENT"> 
    <id name="id"> 
     <generator class="native"/> 
    </id> 
    <bag name="childList" table="CHILD"> 
     <key column="PARENT_ID" not-null="false"/> 
     <composite-element class="Child"> 
      <property column="CHILD_ID" name="id"/> 
     </composite-element> 
    </bag> 
</class> 

現在實現了方便equals方法在子類

public boolean equals(Object o) { 
    if (!(o instanceof Child)) 
     return false; 

    Child other = (Child) o; 
    // identity equality 
    // Used by composite elements 
    if(getId() != null) { 
     return new EqualsBuilder() 
        .append(getId(), other.getId()) 
        .isEquals(); 
    } else { 
     // object equality 
    } 
} 

如果我重構如上圖所示(現在使用AlternativeParent代替)測試用例

@Test 
public void removed_second_from_parent_remove_second_from_database() { 
    AlternativeParent parent = new AlternativeParent(); 
    parent.setId((Integer) parentId); 

    Child first = new Child(); 
    first.setId((Integer) firstId); 

    /** 
     * It simulates the second one has been removed 
     */ 
    parent.getChildList().add(first); 

    Session session = sessionFactory.openSession(); 
    session.beginTransaction(); 

    session.update(parent); 

    session.getTransaction().commit(); 
    session.close(); 

    session = sessionFactory.openSession(); 
    session.beginTransaction(); 

    Child second = (Child) session.get(Child.class, secondId); 
    Assert.assertNull(second); 

    session.getTransaction().commit(); 
    session.close(); 

} 

我看到一個綠色條

相關問題