Hibernate 處理事務


1. Hibernate 的持久化類

1.1 什么是持久化類

  • 持久化類: 就是一個 Java 類(JavaBean),這個 Java類與表建立了映射關系就可以是持久化類;
  • 持久化類 = JavaBean + xxx.hbm.xml;

1.2 持久化類的編寫規則

  • 提供一個無參數的構造方法,因為底層需要進行反射;
  • 提供一個唯一的標識 OID, 即映射數據表主鍵;
    數據庫中通過主鍵, Java 對象通過地址確定對象. 持久化類通過唯一標識 OID 確定記錄.
  • 所有屬性提供公有的 set 或者 get 方法;
  • 標識屬性應盡量使用基本數據類型的包裝類;

1.3 自然主鍵和代理主鍵

  • 自然主鍵: 對象本身的一個屬性. 例如,創建一個人員表,使用身份證號(唯一的)作為表的主鍵.
  • 代理主鍵: 不是對象本身的一個屬性. 例如,創建一個人員表,為每個人員單獨創建一個作為主鍵的字段.
  • 創建表時,盡量使用代理主鍵.

1.4 主鍵生成的策略

<id name="cust_id" column="cust_id">
    // 主鍵的生成策略
    <generator class="native"/>
</id>
  1. increment: 適用於 short, int, long 作為主鍵,不是使用的數據庫自動增長機制;
    • Hibernate 中提供的一種增長機制;
    • 先進行查詢: select max(id) from user, 再進行插入,將查詢出的最大值+1 作為新記錄的主鍵;
    • 缺點:不能在集群環境下或者有並發訪問的情況下使用;
  2. identity: 適用於 short, int, long 作為主鍵,但是這個必須使用在自動增長的數據庫中.
    • 底層使用的是數據庫的自動增長機制;
  3. sequence: 適用於short, int, long 作為主鍵.
    • 底層使用的是序列的增長方式,例如 Oracle 數據庫.
  4. uuid: 適用於 char, varchar 類型的作為主鍵;
    • 使用隨機的字符串作為主鍵;
  5. native: 使用本地策略,根據底層的數據庫不同,自動選擇適用於該種數據庫的生成策略.
    • 如果底層使用的 MySql 數據庫,相當於 identity;
    • 如果底層使用的是 Oracle 數據庫,相當於 sequence;
  6. assigned: 主鍵的生成不用 Hibernate 管理,必須手動設置主鍵;

2. Hibernate 持久化對象的狀態

2.1 Hibernate 持久化類的狀態

  1. Hibernate 為了管理持久化類,將持久化類分成了三個狀態:
    • 瞬時態(Transient Object), 沒有持久化標識 OID,沒有被納入到 Session 對象的管理;
    • 持久態(Persistent Object)
      • 有持久化標識OID,已經被納入到 Session 對象的管理;
      • 注意: 持久化持久態的對象有自動更新數據庫的能力!! 因為 Session 的一級緩存.
    • 托管態(Detached Object), 有持久化標識 OID,沒有被納入到 Session 對象的管理;

2.2 Hibernate 持久化對象的狀態轉換

  1. 瞬時態
    • 獲得瞬時態對象: User user = new User();
    • 瞬時態對象轉換成持久態: save() 或 saveOrUpdate();
    • 瞬時態對象轉換成托管態(不推薦): user.setId();
  2. 持久態
    • 獲得持久態的對象: get() 或 load();
    • 持久態轉換成瞬時態對象: delete();
    • 持久態對象轉成托管態對象: session 的 close() 或 evict() 或 clear()
  3. 托管態
    • 托管態轉換成瞬時態: user.setId(null);
    • 托管態轉換成持久態: update() 或 saveOrUpdate();

3. Hibernate 的一級緩存

3.1 什么是緩存

  • 緩存其實就是一塊內存空間,將數據源(數據庫或者文件)中的數據存放到緩存中,再次獲取的時候,直接從緩存中獲取.
    可以提升程序的性能.

3.2 Hibernate 框架提供了兩種緩存

  1. 一級緩存

    • 自帶的,不可卸載的;
    • 一級緩存的生命周期與 session 一致, 一級緩存稱為 session 級別的緩存;
  2. 二級緩存

    • 二級緩存可以在多個 session 中共享數據;
    • 二級緩存稱為 sessionFactory 級別的緩存;
    • 二級緩存是為了增強一級緩存,一級緩存的生命周期比較短暫;
    • 二級緩存默認沒有開啟,需要手動配置才可以使用;
  3. session 對象的緩存概述

    • Session 對象中有一系列 java 的集合,這些集合構成了一級緩存;
  4. Session 中與一級緩存相關的方法

    • session.clear(): 清空一級緩存;
    • session.evict(Object entity): 從一級緩存中清除指定的實體對象;
    • session.flush(): 刷出緩存;
  5. Hibernate 框架是如何做到數據發生變化時,進行同步操作的呢?

4. Hibernate 中的事務和並發

4.1 Hibernate 框架中設置隔離級別

  1. 需要在 hibernate.cfg.xml 的配置文件中通過標簽來配置;
    • <property name="hibernate.connection.isolation">4</property>
    • "1"表示 Read uncommitted isolation
    • "2"表示 Read committed isolation
    • "4"表示 Repeatable read isolation
    • "8"表示 Serializable isolation

4.2 丟失更新的問題

  1. 如果不考慮隔離性,也會產生寫入數據的問題,即丟失更新的問題;

  2. 例如: A 和 B 兩個事務同時對某一條記錄做修改,就會引發丟失更新的問題;

  3. 解決方案

    • "悲觀鎖"
      • 采用的是數據庫提供的一種鎖機制,如果采用了這種機制,在SQL語句的后面添加for update子句
      • 當 A 事務在操作該條記錄時,會把該條記錄鎖起來,其他事務是不能操作這條記錄的;
      • 只有當 A 事務提交后,鎖釋放了,其他事務才能操作該條記錄;
    • "樂觀鎖"
      • 采用版本號的機制來解決,會在表結構添加一個字段 version=0,默認值為 0;
      • 當 A 事務在操作完該條記錄,提交事務時,會先檢查版本號,只有版本號值相同,才可以提交事務.
        同時,更新版本號 version=1;
      • 當 B 事務在操作完該條記錄,提交事務時,會先檢查版本號,如果發現版本號不同,程序會拋出異常.
  4. Hibernate 框架解決丟失更新的問題

    • "悲觀鎖":較少使用,效率慢
    • "樂觀鎖"
      • 在對應的 JavaBean 中添加一個屬性,名稱可以是任意的.並提供 get 和 set 方法.
        例如:private Integer version;
      • 在映射的配置文件中,提供 <version name="version"/> 標簽即可;

4.3 綁定本地 Session

  1. JavaWeb 中的事務,需要在業務層使用 Connection 來開啟事務
    • 一種是通過參數的方式傳遞;
    • 另一種是把 Connection 綁定到 ThreadLocal 對象中;
  2. Hibernate 框架,使用 session 對象開啟事務.框架提供了 ThreadLocal 的方式,傳遞 session 對象

// 需要在 hibernate.cfg.xml 的配置文件中提供如下配置
    <property name="hibernate.current_session_context_class">thread</property>

// 重寫 HiberanteUtils 工具類
    public class HibernateUtils {
        private static final Configuration CONFIG;
        private static final SessionFactory FACTORY;

        static{
            CONFIG = new Configuration().configure();
            FACTORY = CONFIG.buildSessionFactory();
        }

        public static Session getCurrentSession(){

            // 從 ThreadLocal 中獲取當前 session 對象
            // 該對象不用再手動關閉,線程結束了,會自動關閉.
            return FACTORY.getCurrentSession();
        }
    }

參考資料


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM