初識Hibernate之理解持久化類

分類:IT技術 時間:2017-10-03

上一篇文章我們簡單介紹了Hibernate相關的一些最基本的文件及其作用,並在最後完整的搭建了Hibernate的運行環境,成功的完成了與數據庫的映射。但是至於其中的一些更加細節的地方並沒有很詳盡的解釋,本篇則主要介紹Hibernate中的一個關鍵元素,持久化類。主要涉及以下一些內容:

  • 定義用作持久化類的基本要求
  • 持久化對象的幾種不同狀態及其相互之間的轉換
  • 使用Hibernate完成對數據庫的crud操作

一、定義用作持久化類的基本要求
所謂的持久化類其實本質上也就是一個普通的Java類,只不過我們通過配置文件讓它與數據庫中的某張表形成對應關系。雖然Hibernate號稱低侵入式設計,對持久化類基本不做要求,但是實際上為了一些優化效率而言,遵守一定的規則則可以提高我們框架的運行效率。
首先,在該類中需要提供一個無參的構造器。因為我們的持久化類和數據庫中具體的數據表形成了映射,那麽我們從數據庫中取出的數據都會被轉換成持久化類的對象返回,這裏的無參構造器就是用於框架在反射時構建持久化類對象時候使用的。當然,這一點一般不用我們關心,在Java類中如果沒有顯式指定構造器都會有一個默認的無參構造器。
其次,在該類中定義的屬性,也就是用於與數據表的字段相對應的元素,它們需要滿足Javabean規範,提供相對應的getter和setter方法。這一點毋庸置疑,和我們平常對類屬性方法策略是相同的,但是如果有其他需要,也可以自定義訪問策略,此處只是Hibernate建議。
最後,該類不能不定義為final類。在Hibernate中通過生成代理對象來優化框架性能是很常見的操作,而大部分生成代理的方式是通過javassist生成持久化的子類進行代理,如果持久化被定義為final,顯然是無法進行代理的。

還有一些其他的規則需要遵守,但是由於並不是強制要求且只有在某些場景下才具有相應的應用價值,此處暫時不做介紹,等到相應的場景再進行補充。

二、持久化對象的三種不同的狀態
一個持久化對象對應於數據表中的一條記錄,那麽無論是對數據表的增加刪除還是修改都是基於該持久化對象的。比如我想要插入一條記錄到數據表中,我就可以new一個持久化對象並為其各個屬性(對應於數據表的字段)賦值,然後映射到數據表中。這就是典型的以操作對象的方式操作數據庫,但是這個持久化對象是有幾種狀態的,某個狀態下對對象的修改是可以映射到數據表的,而某個狀態下則不能作為持久化對象與數據表進行映射操作。而持久化對象主要有以下三個不同狀態:

  • 瞬態:對象剛剛被new創建出來,只是一個普通的類對象。
  • 持久化:持久化對象與一個Hibernate Session相關聯,在這個狀態下,對象的所有屬性值的改動,都是可以在事務結束時提交到數據庫中的
  • 脫管:原本處於持久狀態的對象因為其對應的Session被關閉,而失去持久化能力。此時的對象就處於脫管狀態。一旦有Session願意關聯脫管對象,那麽該對象就可以立馬變為持久狀態。

至於這三種不同狀態下的相互轉換可以用下面這張圖很明顯的表示出來:

這裏寫圖片描述

其中,Transient表示瞬態,Persisitent表示持久化,Detached表示脫管狀態。這張圖同時也囊括了持久化對象的整個生命周期,至於其中各個方法的詳細介紹,本篇的下一小節將陸續介紹,通過這些方法的調用來感受持久化對象的狀態變化。

三、使用Hibernate完成對數據庫的crud操作
上述主要介紹了有關Hibernate持久化對象的一些基本狀態等內容,但是對於上圖中具體方法調用後,持久化對象狀態改變情況並不是很直觀。本小節就將從具體代碼執行的結果看這些狀態之間的切換,至於一些配置文件的內容此處不再編寫(詳見上篇文章,此處節約篇幅突出重點),首先我們看insert操作。

1、持久化實體對象
持久化實體對象也可以理解為插入一條記錄到數據表中,反正最終都是讓我們new出來的持久化對象和數據表中的某一行相關聯。例如:

Transaction transaction = session.beginTransaction();

UserInfo user = new UserInfo();
user.setName("single");
user.setAge(21);

session.save(user);
//提交事務
transaction.commit();
session.close();

此處只列出了最核心的一部分代碼。我們首先創建了一個user的持久化對象,此時該對象只是一個普通Java對象並不具備持久化能力,這個狀態就是瞬態。接著我們調用save方法,這個方法就會將user對象當前各個屬性的值映射到數據庫中,並且在save方法調用後,user這個對象此時的狀態就變成了持久化狀態。所以說,我們的插入操作也是持久化實體對象的一個過程。從Navicat中可以顯然的看出來,新數據已經插入:

這裏寫圖片描述

此時的user,只要session不關閉就可以不斷的通過修改user屬性的值來映射數據表。例如下面一段程序:

UserInfo user = new UserInfo();
user.setName("single");
user.setAge(21);

session.save(user);

user.setName("cyy");
//提交事務
transaction.commit();
session.close();

我們在save方法之後再次對user對象的屬性進行修改,然後我們看保存到數據庫中的是什麽。

這裏寫圖片描述

再看看控制臺的輸出:

這裏寫圖片描述

顯然,調用save方法,Hibernate會為我們生成一條insert語句,我們重新修改user對象的屬性值,Hibernate又為我們生成了一條update語句。
但是在沒有提交事務之前,所有的Sql語句對於數據庫的操作都是預操作,並不會實際改變數據庫。直到事務提交的時候,所有的操作才變為實際數據表的變化。還有幾個和save相關的方法在這裏簡單介紹下:

  • Serializable save(Object var1):這是我們上述一直在使用的save方法,var1就是我們的持久化對象,通過調用該方法,Hibernate會為我們生成一條insert語句並立即對數據庫進行一次預插入操作。最後返回該對象所對應的數據表中一行的主鍵值。
  • void persist(Object var1):這個方法所做的事情和save方法是一樣的,都是將持久化對象的各個屬性值去映射到數據表中的一行數據,只是不返回對應的主鍵的值。

除此之外,persisit方法和save方法還有一個重要區別。save方法在調用後會立馬向數據庫發送一條Sql,做一次預插入操作。而perisist方法采用懶加載機制,persist如果在事務之外調用,它不會立即向數據庫發送Sql語句進行預插入,而是暫時被緩存直到清除緩存的時候才向數據進行插入。這樣做有一個好處就是減少了對數據庫的訪問次數,但是缺點就在於數據庫中的數據始終沒有得到更新,容易產生臟數據讀取。通過個例子看看:

UserInfo user = new UserInfo();
user.setName("single");
user.setAge(21);

session.save(user);

我們將這段代碼從事務中抽離出來單獨執行,通過打斷點可以看到在save方法調用結束之時控制臺輸出的信息:

這裏寫圖片描述

顯然,save方法調用結束就會立馬對數據庫進行預插入操作。下面我們看persisit方法:

UserInfo user = new UserInfo();
user.setName("single");
user.setAge(21);
        
session.persist(user);
session.flush();

通過斷點調試,可以看到perisist方法調用結束後,控制臺並沒有輸出任何信息,反而在flush方法調用完畢之後,控制臺輸出insert語句。這就是persisit的懶加載思想,平常的一般操作首選save,在一些長會話流程的時候可以選擇persist方法降低數據庫壓力。

2、根據主鍵加載持久化實體
以上我們可以通過save方法向數據庫中插入一條記錄,同樣我們也可以使用get方法根據主鍵的值從數據庫中加載出來一個持久化對象。下面我們看個例子,首先展示下userinfo表中內容:

這裏寫圖片描述

接著我們使用Hibernate取出其中某條記錄:

/*這裏依然只是展示部分代碼,說明問題即可*/
UserInfo user = (UserInfo)session.get(UserInfo.class,2);
system.out.println("name: "+user.getName()+",age: "+ user.getAge());

//提交事務
transaction.commit();

這裏的get方法主要有兩個參數,第一個參數指定要加載的數據表,第二個參數指定主鍵值。Hibernate將根據該主鍵的值進行加載,最後會返回一個Object對象。運行結果如下:

這裏寫圖片描述

從運行結果來看,顯然我們成功的根據主鍵值加載出來一個userInfo對象。除此之外,get方法調用結束後也會立即向數據庫進行訪問操作,這點和save方法是類似的。當然,如果主鍵的值不存在,那麽將返回null,否則則會返回相對應的持久化對象。這裏需要註意一點的是,我們的get方法是用於加載一個持久化對象的,而對於數據庫的各種查詢操作將在後文介紹。

3、更新持久化實體
除了insert和get,我們還可以通過操作持久化對象的屬性值來修改數據表中的數據內容。例如:

UserInfo user = (UserInfo)session.get(UserInfo.class, 2);
user.setName("aaaa");
user.setAge(111);

session.update(user);

transaction.commit();
session.close();

程序比較簡單,從數據庫的變化來看,上述程序將主鍵值為2的記錄name和age字段的信息做了修改。程序在控制臺一共輸出了兩條Sql語句,一條是get方法調用結束生成的,一條是commit時候生成的update更新語句。也就是說update方法調用結束後並沒有立即訪問數據庫,而是暫時存放在緩存中,等事務提交的時候在要求數據庫執行。

3、刪除持久化實體
Hibernate中提供delete方法通過持久化對象來刪除數據表中的一行記錄。例如:

//刪除數據庫中id為3的記錄
UserInfo user = (UserInfo)session.get(UserInfo.class,3);

session.delete(user);

//提交事務
transaction.commit();
session.close();

首先我們獲得數據表中id為3的一條記錄的引用,然後直接調用delete方法刪除該記錄。同樣的,Hibernate為我們生成兩條Sql語句,一條是get生成的,一條是delete方法產生的,但是delete方法結束後並沒有立即向數據庫發送Sql語句,而是等到事務提交之時。

最後還要提到兩個方法,這兩個方法用於清除session中的持久化對象。

  • clear:調用該方法將清除與session綁定的所有持久化對象,這些對象統統變為脫管狀態,或者說遊離狀態
  • evict:該方法有一個參數,調用該方法將顯式指定清除session中的某個具體的持久化對象,調用完畢之後,該對象將處於遊離狀態

至此,有關持久化類及其生成的對象的相關知識,已經簡單的介紹了,下篇我們將學習映射。總結不到之處,望指出。


Tags: 持久化 構造器 Hibernate 我們 定義 數據庫

文章來源:


ads
ads

相關文章
ads

相關文章

ad