淺談Hibernate對象狀態(tài)之間的神奇轉(zhuǎn)換
狀態(tài)分類
在Hibernate框架中,為了管理持久化類,Hibernate將其分為了三個狀態(tài):
- 瞬時態(tài)(Transient Object)
- 持久態(tài)(Persistent Object)
- 脫管態(tài)(Detached Object)
有很多人好像對這些概念和它們之間的轉(zhuǎn)換不太明白,那么本篇文章就是來解決這些問題的,看完了還不會你來找我。(開個玩笑~~)
詳細描述
我們先來詳細地了解一下三種狀態(tài):
1、瞬時態(tài)
對象由new操作符創(chuàng)建,且尚未與Hibernate中的Session關(guān)聯(lián)的對象被認為處于瞬時態(tài)。瞬時態(tài)對象不會被持久化到數(shù)據(jù)庫中,也不會賦予持久化標識,如果程序中失去了瞬時態(tài)對象的引用,瞬時態(tài)對象將被垃圾回收機制銷毀。
2、持久態(tài)
持久化實例在數(shù)據(jù)庫中有對應(yīng)的記錄,并擁有一個持久化標識。持久化的實例可以是剛剛保存的,也可以是剛剛被加載的。無論哪一種,持久化對象都必須與指定的Session對象關(guān)聯(lián)。
3、脫管態(tài)
某個實例曾經(jīng)處于持久化狀態(tài),但隨著與之關(guān)聯(lián)的Session被關(guān)閉,該對象就變成脫管狀態(tài)。脫管狀態(tài)的引用引用依然有效,對象可繼續(xù)被修改。如果重新讓脫管對象與某個Session關(guān)聯(lián),該脫管對象會重新轉(zhuǎn)換為持久化狀態(tài)。
瞬時態(tài) | 持久態(tài) | 脫管態(tài) | |
是否存于Session緩存中 | × | √ | × |
數(shù)據(jù)庫中是否有對應(yīng)記錄 | × | √ | √ |
例如:
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); } }
那么在這樣的一個例子中,從創(chuàng)建Person對象到給name和age屬性賦值,這些過程都處于瞬時態(tài),而當調(diào)用了session對象的save()方法之后,該對象才從瞬時態(tài)轉(zhuǎn)為了持久態(tài)。而當session關(guān)閉之后,該對象又從持久態(tài)轉(zhuǎn)為了脫管態(tài)。
對象狀態(tài)之間的轉(zhuǎn)換
了解了三種對象狀態(tài)的相關(guān)概念后,我們來看一看三種對象狀態(tài)之間是如何神奇地相互轉(zhuǎn)換的。
瞬時態(tài) <——> 持久態(tài)
我們知道當創(chuàng)建一個對象之后,該對象即為瞬時態(tài),那么它將如何轉(zhuǎn)換為持久態(tài)呢?
看一個例子:
@Test public void test() { Person p = new Person(); p.setPid(2); p.setPname("李四"); p.setAge(30); session.save(p); //session.saveOrUpdate(p); }
給name和age屬性賦值時,該對象仍然處于瞬時態(tài),這個前面已經(jīng)說過了。但需要注意的是,當給主鍵也就是Pid屬性賦值時,該對象將不再處于瞬時態(tài),而是轉(zhuǎn)換為脫管態(tài),因為此時已經(jīng)有了持久化標識,但是并沒有與Session發(fā)生關(guān)聯(lián)。而當調(diào)用session對象的update()或者saveOrUpdate()方法時,該對象才會轉(zhuǎn)換為持久態(tài)。
當然,調(diào)用session對象的get()、load()、query、find()等方法從數(shù)據(jù)庫中查詢得到的對象也處于持久態(tài)。
而僅僅當session對象調(diào)用delete()方法將一個持久化的對象從數(shù)據(jù)庫中刪除后,該對象才從持久態(tài)轉(zhuǎn)為了瞬時態(tài)。
持久態(tài) <——> 脫管態(tài)
當調(diào)用session對象的close()、clear()等方法后,該session所關(guān)聯(lián)的對象將從持久態(tài)轉(zhuǎn)為脫管態(tài),此時這些對象失去了相關(guān)session的關(guān)聯(lián)。而要想從脫管態(tài)轉(zhuǎn)回持久態(tài),只需調(diào)用save()、saveOrUpdate()等方法即可。
瞬時態(tài) ——> 脫管態(tài)
這個前面也已經(jīng)說過了,當創(chuàng)建對象后調(diào)用setXXX()方法設(shè)置主鍵屬性時,該對象就從瞬時態(tài)轉(zhuǎn)為脫管態(tài),前提是這個主鍵是數(shù)據(jù)庫中存在的。
對象生命周期
下面以一個對象從創(chuàng)建到保存至數(shù)據(jù)庫的流程做一個分析:
try { Session session = HibernateUtil.openSession(); //開始事務(wù) session.beginTransaction(); //person對象進入瞬時狀態(tài)態(tài) Person p = new Person(); p.setPname("王五"); p.setAge(40); //person對象進入持久化狀態(tài) session.save(p); //提交事務(wù),隱式包含了session.flush()的動作 session.getTransaction().commit(); //提交完成后,person處于游離狀態(tài) } catch (HibernateException e) { e.printStackTrace(); if (session != null) session.getTransaction().rollback(); } finally { if (session != null) session.close(); }
當一個對象被實例化后,該對象是瞬時狀態(tài),當調(diào)用session.save(Object)后,該對象被加入到session緩存中,進入持久化狀態(tài),這時數(shù)據(jù)庫中還不存在對應(yīng)的記錄。當session提交事務(wù)后,數(shù)據(jù)庫生成了對應(yīng)的記錄,但是這里需要注意一點,因為事務(wù)提交的時候默認會去調(diào)用session.flush()方法來清空緩存,相當于調(diào)用了clear()方法,而我們知道,調(diào)用了clear()方法,對象會從持久態(tài)轉(zhuǎn)為脫管態(tài)。而處于脫管態(tài)的對象會被垃圾回收機制銷毀。這就是一個對象從創(chuàng)建到保存至數(shù)據(jù)庫的完整生命周期過程。
其它
對于對象狀態(tài)有了一定的了解之后,可以用來解釋很多現(xiàn)象。
在Hibernate中,唯有當對象從其它狀態(tài)轉(zhuǎn)為持久態(tài)時,它才會去自動生成sql語句,其它時候是不會去重復(fù)生成sql,這就是Hibernate框架提高效率的關(guān)鍵所在。
例如:
@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();這條語句上打了一個斷電,然后調(diào)試運行。
可以看到,控制臺只輸出了一條sql語句,也就是執(zhí)行save()方法時生成的插入語句,但是執(zhí)行update()方法卻并沒有生成sql。這是因為在執(zhí)行update()方法時,Hibernate框架會去判斷當前對象的狀態(tài),它發(fā)現(xiàn)當前對象處于持久態(tài),所以不重復(fù)生成sql,只是將持久態(tài)對象的值改變而已,然后調(diào)用commit()方法進行事務(wù)提交的時候才去生成更新語句。
我們繼續(xù)看一個例子:
@Test public void test2() { Session session = HibernateUtil.getSession(); Transaction transaction = session.beginTransaction(); Person p = new Person(); p.setPname("張三"); p.setAge(30); session.save(p);//此時該對象從瞬時態(tài)轉(zhuǎn)為持久態(tài),生成sql語句 p.setPname("王五"); session.save(p);//此時該對象為持久態(tài),不生成sql語句 p.setPname("趙六"); session.update(p);//此時該對象為持久態(tài),不生成sql語句 transaction.commit(); session.close(); }
你要知道,這跟你調(diào)用哪個方法是無關(guān)的,關(guān)鍵在于對象的狀態(tài),只有轉(zhuǎn)為持久態(tài)時才會生成sql語句。所以上面的程序段依然只會產(chǎn)生兩條sql,一條是save()方法產(chǎn)生的,一條是commit()方法產(chǎn)生的。
控制臺信息如下:
Hibernate: insert into PERSON (PNAME, AGE) values (?, ?) Hibernate: update PERSON set PNAME=?, AGE=? where PID=?
理解Hibernate的三種狀態(tài),將更有利于理解Hibernate的運行機制,這些可以讓你在開發(fā)中對疑點問題的定位產(chǎn)生關(guān)鍵性的幫助。
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Java深入講解instanceof關(guān)鍵字的使用
instanceof 是 Java 的一個二元操作符,類似于 ==,>,< 等操作符。instanceof 是 Java 的保留關(guān)鍵字。它的作用是測試它左邊的對象是否是它右邊的類的實例,返回 boolean 的數(shù)據(jù)類型2022-05-05mybatis自動生成時如何設(shè)置不生成Example類詳解
這篇文章主要給大家介紹了關(guān)于mybatis自動生成時如何設(shè)置不生成Example類的相關(guān)資料,文中介紹的非常詳細,對大家具有一定的參考學習價值,需要的朋友們下面來一起看看吧。2017-05-05mybatis 自定義實現(xiàn)攔截器插件Interceptor示例
這篇文章主要介紹了mybatis 自定義實現(xiàn)攔截器插件Interceptor,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-10-10Intellij IDEA如何自定義注釋模板的實現(xiàn)方法
這篇文章主要介紹了Intellij IDEA如何自定義注釋模板的實現(xiàn)方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-05-05