Hibernate 持久化對象的各種狀態(tài)
1. 前言
本節(jié)課和大家聊聊持久化對象的 3 種狀態(tài)。通過本節(jié)課程,你將了解到:
- 持久化對象的 3 種狀態(tài);
- 什么是對象持久化能力。
2. 持久化對象的狀態(tài)
程序運行期間的數據都是存儲在內存中。內存具有臨時性。程序結束、計算機掛機…… 內存中的數據將不復存在。
重要的數據,需要使用持久化技術將數據保存到永久性設備上。Hibernate 能夠通過 PO(持久化對象) 將數據持久化到數據庫。
Hibernate 對 PO 進行操作期間,PO 本身會發(fā)生一系列的狀態(tài)變化。
2.1 瞬時狀態(tài)(Transient)
分析一段保存數據的實例:
使用 Hibernate 保存數據之前,須先在程序中創(chuàng)建一個名為 stu 的 PO:
Student stu=new Student("PO對象的瞬時狀態(tài)", "男");
此 PO 在程序運行的內存中存在,數據庫中沒有。PO 此時處于瞬時狀態(tài) (Transient)。
瞬時狀態(tài)下的 PO 特征如下:
- 程序中有、數據庫中沒有此對象的相關信息;
- 對象的標識性屬性(對應表中主鍵的那個屬性) 為空;
- 如果不使用 Session 對象的相關方法進行數據庫請求操作,程序退出時瞬時狀態(tài)的對象信息會丟失。
2.2 持久化狀態(tài)( Persistent )
創(chuàng)建 PO 后,使用 Session 的相關方法,如 save() 方法向數據庫提交保存請求:
Student stu=new Student("PO對象的瞬時狀態(tài)", "男");
session.save(stu);
此時 PO 在程序中有,數據庫中也有,狀態(tài)便由瞬時狀態(tài)轉變成為持久化狀態(tài)(Persistent)。
持久化狀態(tài)下的 PO 有如下幾個特征:
- 程序、數據庫中都有 PO 的信息;
- 對象的標識屬性的值為數據庫中對應記錄的主鍵值;
- 持久化狀態(tài)最大的特點是 PO 處于 Session 生命周期之內。此狀態(tài)下的 PO 具有持久化能力。
2.3 游離狀態(tài)(Detached)
PO 的持久化狀態(tài)一直維持到 Session 對象關閉。如果 Session 對象關閉了,此 PO 的狀態(tài)將由持久化狀態(tài)轉變成游離狀態(tài) (Detached)。
游離狀態(tài)時 PO 的特點:
- 程序、數據庫都有 PO 的信息;
- 但是,此狀態(tài)下的 PO 不具有持久化能力。
PO 不會一直停留在某一個狀態(tài)上,PO 隨時可以在 3 種狀態(tài)之間進行切換。
從上圖可看出,PO 的 3 種狀態(tài)之間的相互演變都是通過調用 Session 對象的相關方法實現的。
由此看來,Session 對象被稱為持久化容器是有道理的。
由上圖可知,處于瞬時狀態(tài)或游離狀態(tài)的對象才有可能被 JVM 垃圾回收器回收。
3. 對象持久化能力
知道了 PO 的 3 種狀態(tài)。自然會問:不同狀態(tài)下的對象對實際操作有什么實際指導意義?
3 種狀態(tài)中,持久化狀態(tài)的意義最大,如果 PO 處于持久化狀態(tài),此時 PO 就具有持久化能力。
所謂對象持久化能力,通俗理解:
程序中的數據發(fā)生變化,會自動同步到數據庫中。
演示一段數據更新實例,更新之前先查詢數據:
try {
transaction = session.beginTransaction();
//查詢學生
Student stu=(Student)session.load(Student.class, new Integer(2));
//修改學生信息
//執(zhí)行更新操作
transaction.commit();
} catch (Exception e) {
transaction.rollback();
} finally {
session.close();
}
通過 Session 的 get() 方法查詢出來的 stu 對象,此時就處于持久化狀態(tài)。
在” 修改學生信息 “的注釋下添加一行代碼:
stu.setStuName("持久化狀態(tài)就是這么牛");
不需要調用 Session 中的任何其它方法,執(zhí)行代碼,程序中修改的數據立即同步到數據庫中。
這就是持久化狀態(tài)的特點:通過 PO 自動同步程序與數據庫中的數據。
所謂對象持久化能力本質上還是 Session 給的。
Session 記錄對象是否處于持久化狀態(tài),并充當后臺靠山。處于持久狀態(tài)的對象與數據庫之間的數據同步,只是不需要 Session 顯示調用。
除了 get()、load()方法。save()、update()、saveOrUpdate()、persis()、megre() 方法都可稱為持久化方法。
調用這些方法后,能讓對象進入持久化狀態(tài),Session 記錄并且默默維持 PO 中數據與數據庫中數據的同步。
3.1 save() 和 persist() 方法
saveOrUpdate( ) 方法很好理解,是 save( ) 和 update( ) 方法的綜合簡化版,內在本質沒改變。
save() 和 persist() 方法有細節(jié)上的區(qū)別。
save() 方法原型:
public Serializable save(Object object);
上一段 save ( ) 方法的測試實例:
try {
Student stu = new Student("save()方法", "男");
Serializable stuId = session.save(stu);
System.out.println("----------輸出學生編號Id---------");
System.out.println(stu.getStuId());
System.out.println(stuId);
System.out.println("----------事務在后面-------");
transaction = session.beginTransaction();
transaction.commit();
} catch (Exception e) {
transaction.rollback();
} finally {
session.close();
}
輸出結果:
Hibernate:
insert
into
Student
(stuName, stuPassword, stuPic, stuSex)
values
(?, ?, ?, ?)
----------輸出學生編號Id---------
40
40
----------事務在后面-------
結果即結論:
Save() 方法可以在事務之外執(zhí)行;
有一個關鍵點需要引起重視:
無論是在事務之內還是事務之外,save() 方法都會向數據庫發(fā)送了一條 Sql 語句請求,控制臺輸出結果是一樣的。
但是:
- 如果程序中 Hibernate 不顯示發(fā)送事務提交指令,數據會回滾(丟失);
- 只有當數據庫系統接收到程序中發(fā)送過來的事務提交指令后,才會真正意義上保存。
很好理解,因為事務是交給 Hibernate 管理的,數據庫接收到插入指令后,在沒有明確事務提交指令之前,只會把數據緩存在內存中。
也就是說,雖然 save() 方法看起來不依賴事務就可插入數據,但,沒有事務組件的指令,最后也是虛行一場。
persist() 方法原型:
public void persist(Object object);
上一段 persist() 測試實例:
try {
Student stu = new Student("persist()方法", "男");
session.persist(stu);
System.out.println("----------輸出學生編號Id---------");
System.out.println(stu.getStuId());
System.out.println("----------事務在后面-------");
transaction = session.beginTransaction();
System.out.println("-------------事務提交---------------");
transaction.commit();
System.out.println(stu.getStuId());
} catch (Exception e) {
transaction.rollback();
} finally {
session.close();
}
輸出結果:
----------輸出學生編號Id---------
null
----------事務在后面-------
-------------事務提交---------------
Hibernate:
insert
into
Student
(stuName, stuPassword, stuPic, stuSex)
values
(?, ?, ?, ?)
39
persist() 方法只有當事務提交后,才會發(fā)送 Sql 請求,數據直接寫入數據庫,方法本身沒有返回值。
save() 和 persist() 方法區(qū)別:
- 在事務之內調用時,兩者區(qū)別不大;事務之外,區(qū)別明顯。
- save() 返回主鍵值,persist() 方法沒有返回值;
- persist() 完全依賴事務組件,否則不會提交 Sql 請求;
- persist() 方法除了可進行 save 操作,還可以進行 update 操作。
3.2 merge() 方法
方法原型:
public Object merge(Object object);
merge() 方法和 persist() 方法類似, 區(qū)別在于:
- merge() 方法接收一個 PO 作為參數,創(chuàng)建并返回此 PO 的副本對象;
- 此副本對象具有對象持久化能力。這一點是 merge() 方法與其他方法最大的不同。
上一段實例:
try{
transaction = session.beginTransaction();
//查詢出來的stu具有持久化能力
Student stu = (Student) session.get(Student.class, new Integer(2));
//轉stu對象持久化狀態(tài)轉變成游離狀態(tài)
session.clear();
//stu_對象具有持久化能力
Student stu_ = (Student) session.merge(stu);
//這個操作不能同步到數據庫
stu.setStuName("我已經不具有持久化能力");
//這個操作能同步到數據庫
stu_.setStuName("我具有持久化能力");
transaction.commit();
} catch(Exception e) {
transaction.rollback();
} finally {
session.close();
}
merge() 方法返回的 stu 對象的副本 stu_,此對象具有持久化能力。執(zhí)行下面代碼,數據能同步到數據庫中。
stu_.setStuName("我具有持久化能力");
Session 中提供的每一個方法都有其實際意義。
特別是 merge() 方法,既可以保護原對象中的數據不被污染,又能行使數據庫同步操作。
在很多場景里都會有這個需求。
4. 小結
本節(jié)課程講解 PO 對象的 3 種狀態(tài),以及 3 種狀態(tài)之間的轉化方式。了解處于持久化狀態(tài)的 PO 具有持久化能力,這是 Hibernate 提供的一個很棒的 程序中對象與數據庫數據自動同步的方案。
也是一種快速開發(fā)方案。
本節(jié)課區(qū)分了幾個常用方法的差異性。但真相似乎就是:大家都和持久化狀態(tài)有關系。