Hibernate<二> - Session

一、Session概述java

    Session接口是Hibernate嚮應用程序提供的操縱數據庫的最主要的接口,它提供了基本的保存更新刪除加載Java對象的方法。git

    每個Session對象都在內存中維護了一個緩存,位於緩存中的對象稱爲持久化對象,它和數據庫表中的相關記錄保持着一種對應關係。經過Session緩存,Hibernate最大限度的減小了應用程序訪問數據庫的次數,實現了對內存空間更加有效的利用。github

    Hibernate把持久化類的對象分爲4種狀態:持久化狀態臨時狀態遊離狀態刪除狀態。Session的特定方法能使對象從一個狀態轉換到另外一個狀態。數據庫

二、Session緩存緩存

    在應用系統開發過程當中,使用緩存每每是提高程序性能的主要手段。在Hibernate中,Session對象維護的是一個線程級的一級緩存,SessionFactory維護了一個進程級別的二級緩存。二級緩存會在後面的博客中詳細介紹,如今先關注Session維護的一級緩存。session

    2.1  使用Session緩存的好處併發

        ①減小訪問數據庫的次數ide

        兩次調用session.get(Student.class,1);方法,僅發送1條SQL語句。緣由是第一次使用get()方法加載的Student被緩存到了內存中,第二次調用get()方法直接使用了內存中緩存的Student對象,從而減小了訪問數據庫的次數性能

    Tips:應用程序中訪問數據庫的次數每每是最主要的性能瓶頸。hibernate

        ②將內存中數據的變化自動同步到數據庫中

    使用session.get(Student.class,1);方法將一個Student對象加載到內存中後,Student對象就處於Session對象的管理中。此時調用student.setStuName(「a」);方法將觸發Hibernate對數據庫表中對應數據記錄的修改,即發送一條UPDATE語句,更新Student對象在數據庫表中對應的記錄。

    Hibernate最大的特色就是將對數據庫記錄的操做,轉換爲對Java對象的操做簡化開發

2.2 Session緩存操做概述

    ①flush:推送。將緩存中數據的改變落實到數據庫中

    ②refresh:刷新。將數據庫中數據的改變提取到緩存中

    ③clear:清空。清空Session緩存

2.3    flush操做
    ①Session對象默認在什麼狀況下執行flush操做?
    ②默認狀況下Session執行flush操做的時機:
        [1]顯式調用Session的flush()方法
        [2]調用Transaction對象的commit()方法時,先flush緩存,在提交事務
        [3]執行HQL、QBC查詢以前,先flush緩存,保證HQL或QBC查詢到的數據是最新的。
    ③相關問題
        [1]修改持久化對象的屬性值,並不會當即發送SQL語句,而是提交事務以前,執行flush操做的時候纔會發送相應的UPDATE語句。
        [2]發送UPDATE語句時,數據的修改並不會當即生效,只有提交了事務以後,數據的修改纔會永久的保存下來。

2.4    refresh操做與事務隔離級別
①提出問題
在使用MySQL客戶端修改數據後,refresh()方法讀取到的仍是舊的未修改的數據,爲何?
②分析
MySQL默認的數據庫事務隔離級別是「可重複讀」,要保證當前事務執行期間的不一樣時間點讀取到的數據是同樣的。
③結論
refresh()方法讀取到的數據是否是真正的最新數據,須要參照當前的事務隔離級別。
3    事務隔離級別回顧
3.1    數據庫事務併發問題
假設如今有兩個事務:Transaction01和Transaction02併發執行。
①髒讀
    [1]Transaction01將某條記錄的AGE值從20修改成30。
    [2]Transaction02讀取了Transaction01更新後的值:30。
    [3]Transaction01回滾,AGE值恢復到了20。
    [4]Transaction02讀取到的30就是一個無效的值。
②不可重複讀
    [1]Transaction01讀取了AGE值爲20。
    [2]Transaction02將AGE值修改成30。
    [3]Transaction01再次讀取AGE值爲30,和第一次讀取不一致。
③幻讀
    [1]Transaction01讀取了STUDENT表中的一部分數據。
    [2]Transaction02向STUDENT表中插入了新的行。
    [3]Transaction01讀取了STUDENT表時,多出了一些行。
3.2    隔離級別
    數據庫系統必須具備隔離併發運行各個事務的能力,使它們不會相互影響,避免各類併發問題。一個事務與其餘事務隔離的程度稱爲隔離級別。SQL標準中規定了多種事務隔離級別,不一樣隔離級別對應不一樣的干擾程度,隔離級別越高,數據一致性就越好,但併發性越弱。
    ①讀未提交:READ UNCOMMITTED
        容許Transaction01讀取Transaction02未提交的修改。
    ②讀已提交:READ COMMITTED
        要求Transaction01只能讀取Transaction02已提交的修改。
    ③可重複讀:REPEATABLE READ
        確保Transaction01能夠屢次從一個字段中讀取到相同的值,即Transaction01執行期間禁止其它事務對這個字段進行更新。
    ④串行化:SERIALIZABLE
        確保Transaction01能夠屢次從一個表中讀取到相同的行,在Transaction01執行期間,禁止其它事務對這個表進行添加、更新、刪除操做。能夠避免任何併發問題,但性能十分低下。
    ⑤各個隔離級別解決併發問題的能力見下表

    ⑥各類數據庫產品對事務隔離級別的支持程度

⑦在MySQL中設置隔離級別

    [1]每啓動一個MySQL程序,就會得到一個單獨的數據庫鏈接。每一個數據庫鏈接都有一個全局變量@@tx_isolation,表示當前的事務隔離級別。

    [2]查看當前的隔離級別:

SELECT @@tx_isolation;

    [3]設置當前MySQL鏈接的隔離級別:

SET TRANSACTION ISOLATION LEVEL READ COMMITTED;

    [4]設置數據庫系統的全局的隔離級別:

SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;

    ⑧在Hibernate中設置隔離級別

    [1]JDBC數據庫鏈接使用數據庫系統默認的隔離級別。

    [2]在Hibernate的配置文件中能夠顯式的設置隔離級別。每個隔離級別都對應一個整數:

    [3]設置方式

<!-- 設置當前數據庫事務隔離級別爲讀已提交 -->
<property name="hibernate.connection.isolation">2</property>

4    持久化對象的狀態
    4.1    四種狀態
    ①臨時狀態
        [1]OID:沒有OID。例如:new Student(null, "Kate2015", 18, new Date());
        [2]是否在Session緩存中:不在
        [3]是否在數據庫中有對應的記錄:沒有
    ②持久化狀態
        [1]OID:有OID。例如:Student student = (Student) session.get(Student.class, 1);
        [2]是否在Session緩存中:在
        [3]是否在數據庫中有對應的記錄:有
    ③遊離狀態
        [1]OID:有OID。例如:new Student(1, "Kate2015", 18, new Date());
        [2]是否在Session緩存中:不在
        [3]是否在數據庫中有對應的記錄:有
    ④刪除狀態
        [1]OID:有OID。一般是通過刪除操做獲得的。
        [2]是否在Session緩存中:不在
        [3]是否在數據庫中有對應的記錄:沒有

4.2    四種狀態之間的轉換

5    Session核心方法
    5.1    save()
        ①將一個臨時對象轉換爲持久化對象
        ②對於OID:忽略臨時對象中的「OID」值,save()方法會將數據庫產生的新的OID的值賦值給持久化對象
        ③持久化狀態的對象的OID的值是不容許修改的
    5.2    persist()
        ①做用和save()相同
        ②不容許保存帶有OID的對象,會拋異常:org.hibernate.PersistentObjectException
    5.3    get()
        ①根據OID的值,從數據庫中當即加載一個對象到內存中。
        ②若是調用get()方法時,Session緩存中已經有了一個OID相同的對象,則get()方法會將緩存中的對象返回,不發送SQL語句。
    5.4    load()
        ①採用延遲檢索策略,在調用load()方法時僅返回一個代理對象,不發送SQL語句。在真正用到非OID屬性值時,才發送SQL語句進行查詢。
        ②懶加載初始化異常
        [1]全類名:org.hibernate.LazyInitializationException
        [2]產生緣由:對一個延遲加載的代理對象進行初始化時,Session緩存中沒有對應OID的對象,沒法完成初始化。
5.5    update()
    ①根據遊離對象或持久化對象對數據庫表進行更新
    ②update()方法更新一個遊離對象時,默認狀況下沒法獲知當前遊離對象中的數據和數據庫表中的數據是否有差別。因此,不管是否有差別都會發送SQL語句。爲了不盲目的發update語句,能夠在hbm配置文件的class元素中設置select-before-update屬性的值爲true。一般不設置這個屬性。
    ③使用update()方法更新一個OID不存在的對象會拋出異常:org.hibernate.StaleObjectStateException
    ④使用update()方法更新一個遊離對象,但此時Session緩存中已經存在了OID相同的持久化對象,會拋出異常:org.hibernate.NonUniqueObjectException
5.6    saveOrUpdate()
    ①兼具保存和更新兩種功能。傳入臨時對象執行保存,傳入遊離對象執行更新。
    ②判斷執行INSERT語句仍是UPDATE語句的依據
    [1]根據OID的值,若是爲null則執行保存,不然執行更新
    [2]若是OID的值等於hbm文件中,id元素設置的unsaved-value屬性則執行保存操做。
5.7    delete()
    ①執行刪除操做
    ②刪除對象後將OID的值置空:在Hibernate配置文件中加入以下配置

<property name="hibernate.use_identifier_rollback" >true </property>

5.8    doWork()
    ①用於在Hibernate環境下執行原生的JDBC代碼
    ②示例代碼

@Test
public void testWork() {
	session.doWork(new Work() {
		
		@Override
		public void execute(Connection connection) throws SQLException {
			
			//使用傳入的Connection對象實現原生的JDBC操做...
			
		}
	});
}

 GitHub地址:https://github.com/leebingbin/  

相關文章
相關標籤/搜索