這是我參與8月更文挑戰的第12天,活動詳情查看:8月更文挑戰java
在Hibernate框架中,爲了管理持久化類,Hibernate將其分爲了三個狀態:sql
有不少人好像對這些概念和它們之間的轉換不太明白,那麼本篇文章就是來解決這些問題的,看完了還不會你來找我。(開個玩笑~~)數據庫
咱們先來詳細地瞭解一下三種狀態:緩存
對象由new操做符建立,且還沒有與Hibernate中的Session關聯的對象被認爲處於瞬時態。瞬時態對象不會被持久化到數據庫中,也不會賦予持久化標識,若是程序中失去了瞬時態對象的引用,瞬時態對象將被垃圾回收機制銷燬。markdown
持久化實例在數據庫中有對應的記錄,並擁有一個持久化標識。持久化的實例能夠是剛剛保存的,也能夠是剛剛被加載的。不管哪種,持久化對象都必須與指定的Session對象關聯。session
某個實例曾經處於持久化狀態,但隨着與之關聯的Session被關閉,該對象就變成脫管狀態。脫管狀態的引用引用依然有效,對象可繼續被修改。若是從新讓脫管對象與某個Session關聯,該脫管對象會從新轉換爲持久化狀態。框架
瞬時態 | 持久態 | 脫管態 | |
是否存於Session緩存中 | × | √ | × |
數據庫中是否有對應記錄 | × | √ | √ |
例如:post
public class HibernateTest {
private Session session;
private Transaction transaction;
@Before
public void before() {
session = HibernateUtil.getSession();
transaction = session.beginTransaction();
}
@After
public void after() {
transaction.commit();
session.close();
}
@Test
public void test() {
Person p = new Person();
p.setPname("張三");
p.setAge(20);
session.save(p);
}
}
複製代碼
那麼在這樣的一個例子中,從建立Person對象到給name和age屬性賦值,這些過程都處於瞬時態,而當調用了session對象的save()方法以後,該對象才從瞬時態轉爲了持久態。而當session關閉以後,該對象又從持久態轉爲了脫管態。spa
瞭解了三種對象狀態的相關概念後,咱們來看一看三種對象狀態之間是如何神奇地相互轉換的。調試
咱們知道當建立一個對象以後,該對象即爲瞬時態,那麼它將如何轉換爲持久態呢? 看一個例子:
@Test
public void test() {
Person p = new Person();
p.setPid(2);
p.setPname("李四");
p.setAge(30);
session.save(p);
//session.saveOrUpdate(p);
}
複製代碼
給name和age屬性賦值時,該對象仍然處於瞬時態,這個前面已經說過了。但須要注意的是,當給主鍵也就是Pid屬性賦值時,該對象將再也不處於瞬時態,而是轉換爲脫管態,由於此時已經有了持久化標識,可是並無與Session發生關聯。而當調用session對象的update()或者saveOrUpdate()方法時,該對象纔會轉換爲持久態。 固然,調用session對象的get()、load()、query、find()等方法從數據庫中查詢獲得的對象也處於持久態。 而僅僅當session對象調用delete()方法將一個持久化的對象從數據庫中刪除後,該對象才從持久態轉爲了瞬時態。
當調用session對象的close()、clear()等方法後,該session所關聯的對象將從持久態轉爲脫管態,此時這些對象失去了相關session的關聯。而要想從脫管態轉回持久態,只需調用save()、saveOrUpdate()等方法便可。
這個前面也已經說過了,當建立對象後調用setXXX()方法設置主鍵屬性時,該對象就從瞬時態轉爲脫管態,前提是這個主鍵是數據庫中存在的。
下面以一個對象從建立到保存至數據庫的流程作一個分析:
try {
Session session = HibernateUtil.openSession();
//開始事務
session.beginTransaction();
//person對象進入瞬時狀態態
Person p = new Person();
p.setPname("王五");
p.setAge(40);
//person對象進入持久化狀態
session.save(p);
//提交事務,隱式包含了session.flush()的動做
session.getTransaction().commit();
//提交完成後,person處於遊離狀態
} catch (HibernateException e) {
e.printStackTrace();
if (session != null)
session.getTransaction().rollback();
} finally {
if (session != null)
session.close();
}
複製代碼
當一個對象被實例化後,該對象是瞬時狀態,當調用session.save(Object)後,該對象被加入到session緩存中,進入持久化狀態,這時數據庫中還不存在對應的記錄。當session提交事務後,數據庫生成了對應的記錄,可是這裏須要注意一點,由於事務提交的時候默認會去調用session.flush()方法來清空緩存,至關於調用了clear()方法,而咱們知道,調用了clear()方法,對象會從持久態轉爲脫管態。而處於脫管態的對象會被垃圾回收機制銷燬。這就是一個對象從建立到保存至數據庫的完整生命週期過程。
對於對象狀態有了必定的瞭解以後,能夠用來解釋不少現象。 在Hibernate中,惟有當對象從其它狀態轉爲持久態時,它纔會去自動生成sql語句,其它時候是不會去重複生成sql,這就是Hibernate框架提升效率的關鍵所在。 例如:
@Test
public void test2() {
Session session = HibernateUtil.getSession();
Transaction transaction = session.beginTransaction();
Person p = new Person();
p.setPname("李四");
p.setAge(30);
session.save(p);
p.setPname("王五");
session.update(p);
transaction.commit();
session.close();
}
複製代碼
我在transaction.commit();這條語句上打了一個斷電,而後調試運行。 能夠看到,控制檯只輸出了一條sql語句,也就是執行save()方法時生成的插入語句,可是執行update()方法卻並無生成sql。這是由於在執行update()方法時,Hibernate框架會去判斷當前對象的狀態,它發現當前對象處於持久態,因此不重複生成sql,只是將持久態對象的值改變而已,而後調用commit()方法進行事務提交的時候纔去生成更新語句。
咱們繼續看一個例子:
@Test
public void test2() {
Session session = HibernateUtil.getSession();
Transaction transaction = session.beginTransaction();
Person p = new Person();
p.setPname("張三");
p.setAge(30);
session.save(p);//此時該對象從瞬時態轉爲持久態,生成sql語句
p.setPname("王五");
session.save(p);//此時該對象爲持久態,不生成sql語句
p.setPname("趙六");
session.update(p);//此時該對象爲持久態,不生成sql語句
transaction.commit();
session.close();
}
複製代碼
你要知道,這跟你調用哪一個方法是無關的,關鍵在於對象的狀態,只有轉爲持久態時纔會生成sql語句。因此上面的程序段依然只會產生兩條sql,一條是save()方法產生的,一條是commit()方法產生的。 控制檯信息以下:
Hibernate:
insert
into
PERSON
(PNAME, AGE)
values
(?, ?)
Hibernate:
update
PERSON
set
PNAME=?,
AGE=?
where
PID=?
複製代碼
理解Hibernate的三種狀態,將更有利於理解Hibernate的運行機制,這些可讓你在開發中對疑點問題的定位產生關鍵性的幫助。