系統管理員可以創建新的categories,重命名categories,移除其子categories。你可以從圖4.3中看到這種結構。
現在,我們來映射這個類和關聯:
正如第三章討論的,這是一個遞歸的、雙向的一對多關聯。one-valued的一端使用了<many-to-one>元素,Set類型則使用了<set>元素。它們都指向了同一個外鍵列:PARENT_CATEGORY_ID。
假設我們創建一個Computer的category,並且它作爲一個子category出現,參見圖4.4。
我們有幾種方法來創建一個新的Laptops對象並且把它保存到數據庫當中。
computer實例是已經持久化了的,它的childCategories關聯也擁有cascade。因此,當tx.commit()的時候,laptops也會被保存,這要歸功於Hibernate的髒數據檢測。Hibernate會執行一個INSERT操作。
讓我們再來重做一遍,不過這次創建的關聯是在任何事務之外的。
處於detached狀態的computer對象以及其他和computer關聯的detached對象選擇和處於transient的laptops對象產生了關聯。我們通過另外一個session來持久化這種變化:
Hibernate會檢測laptops的父category的數據庫標識符屬性從而正確的在數據庫中創建和Computer的關聯。Hibernate會把父category的值作爲外鍵插入到Laptops行。
因爲cascade=”none”在parentCategory關聯中的設定,Hibernate會忽略任何其他categories的關聯。如果我們在<many-to-one>中指定了cascade=”save-update”,那麼Hibernate會將內存中所有關聯的對象保持和數據庫同步。這樣的可操作性並不好,因爲太多無用的數據會被請求到。在這個例子中,對於parentCategory關聯並不需要傳播性的持久化。
爲什麼我們需要cascading操作?我們可以像之前的例子那樣保存laptop對象而不需要任何關於cascade的映射。那麼請考慮下面的例子:
在上面的例子中,你需要單獨的存儲每個新的category。但是,如果我們在childCategories關聯中設置了cascade=”save-update”的話,我們就不需要這麼麻煩的操作。我們只需要在session中保存一個單獨的Laptops對象:
你可能會想爲什麼使用cascade=”save-update”而不是cascade=”save”。那麼請考慮下面的例子,假設之前我們已經保存三個新的categories:
我們爲Laptops增加了一個新的category,並且修改了三個已經存在的category。下面的代碼會把這些變化發送到數據庫中:
指定cascade=”save-update”會準確的反應以上這些變化。在這個例子中,它會更新三個detached對象,並且保存新的子category。
注意到最後一個代碼示例僅僅在調用方法的方法和之前的兩個例子不同。它使用了update()而不是save(),因爲laptops已經被持久化了。
我們也可以使用saveOrUpdate()方法重寫所有的例子:
saveOrUpdate方法則告訴Hibernate如果一個對象是transient的話,那麼插入到數據庫,如果是一個detached對象,則更新到數據庫。從另一方面說,它做了和cascade=”save-update”相同的事情。
最後一個問題:Hibernate如何知道那個子category是detached,而那個又是transient的呢?