在Java程式中,物件與物件之間會透過某些關係互相參考,如果有一個物件已經是持久化物件,被它參考的物件直覺上也應該要持久化,以維持物件之間關聯的完整性,這是藉由可達性完成持久化(Persistence by reachability)的基本概念。
如果將物件之間的關聯想像為一個樹狀圖,從某一個持久化物件為樹根出發,父節點若是持久化物件,則被父節點參考到的子節點應自動持久化,而另一方面,如果有一子節點沒辦法藉由任何的父節點來參考至它,則它沒有被持久化的需求,它應從資料庫中加以刪除。
Hibernate並沒有完全實現以上的概念,它讓使用者自行決定自動持久化的方式,當物件之間被指定關聯(例如多對一、一對多等),您可以決定被持久化物件關聯的暫存對象是否進行自動持久化與如何自動持久化。
以之前「多對一實體映射」主題為例,之前我們在設定好User類別中的Room屬性之後,我們分別要對Room與User進行save(),在物件的關係圖上,基本上我們應實現的是儲存User,而Room的持久化應自動完成,而不用我們再特地指定,為了達到這個目的,我們在映射多對一關係時可以使用 cascade來指定自動持久化的方式,例如修改User.hbm.xml為:
User.hbm.xml
<many-to-one name="room"
column="ROOM_ID"
class="onlyfun.caterpillar.Room"
cascade="save-update"/>
預設上cascade是none,也就是不進行自動持久化,所以預設上我們必須對每一個要持久化的物件進行save(),在上面我們指定了cascade為save-update,這表示當我們儲存或更新User時,自動對其所關聯到的Room(暫時)物件進行持久化。
這邊要注意的是,使用cascade自動持久化時,會先檢查被關聯物件的id屬性,未被持久化的物件之id屬性是由unsaved-value決定,預設是null,如果您使用long這樣的原生型態(primitive type)時,則必須自行指定預設值,所以在「多對一實體映射」的Room.hbm.xml的id映射上,我們必須改為:
Room.hbm.xml
<id name="id" column="ROOM_ID" unsaved-value="0">
<generator class="increment"/>
</id>
如果您不想額外設定unsaved-value資訊,則可以將long改為Long,這可以符合預設的unsaved-value為null的設定,關於unsaved-value進一步的介紹,可以參考這邊:
http://www./76.html
修改映射文件之後,我們可以使用以下的方式來儲存資料:
HibernateTest.java
import onlyfun.caterpillar.*;
import net.sf.hibernate.*;
import net.sf.hibernate.cfg.*;
public class HibernateTest {
public static void main(String[] args) throws HibernateException {
SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
Room room1 = new Room();
room1.setAddress("NTU-M8-419");
User user1 = new User();
user1.setName("bush");
user1.setRoom(room1);
User user2 = new User();
user2.setName("caterpillar");
user2.setRoom(room1);
Session session = sessionFactory.openSession();
Transaction tx= session.beginTransaction();
session.save(user1);
session.save(user2);
tx.commit();
session.close();
sessionFactory.close();
}
}
這次我們不用特地儲存room了,透過cascade設定為save-update,被User關聯到的物件,在儲存或更新時,都會自動持久化,之後我們甚至可以如下來進行物件儲存:
Transaction tx = session.beginTransaction();
User user = (User) session.get(User.class, new Long(1));
Room room = new Room();
room.setAddress("NTU-M5-105");
user.setRoom(room);
tx.commit();
session.close();
cascade的指定除了save-update之外,還可以使用delete、all、all-delete-orphan、delete- orphan,各個設定的作用,建議您查看參考手冊9.8Lifecyles and object graphs,當中有詳細的說明,而有關於可達性完成持久化(Persistence by reachability)的說明,可以參考Hibernate in Action的4.3。