1. 程式人生 > >day27HibernateDay02(持久化類 悲樂觀鎖 事務 session一級快取 Criteria介面查詢)

day27HibernateDay02(持久化類 悲樂觀鎖 事務 session一級快取 Criteria介面查詢)

持久化類
    就是一個Java類(編寫的JavaBean),這個Java類與表建立了關係就可以成為持久化類
        持久化類 = JavaBean + xxx.hbm.xml

持久化類的編寫規則
1、提供一個無引數 public訪問控制符的構造器       底層需要進行反射
2、提供一個標識屬性,對映資料表主鍵欄位            唯一標識OID,資料庫中通過主鍵。Java物件通過地址確定物件,持久化類通過唯一標識OID確定記錄
3、所有屬性提供public訪問控制符的 set或者get方法
4、標識屬性應儘量使用基本資料型別的包裝型別

區分自然主鍵和代理主鍵
1、建立表的時候
    自然主鍵:物件本身的一個屬性,建立一個人員表,每個人都有一個身份證號(唯一的)使用身份證號作為表的主鍵,自然主鍵(開發不會用 因為可能重複)

    代理主鍵:不是物件本身的一個屬性,建立一個人員表,為每個人員單獨建立一個欄位,用這個欄位作為主鍵,代理主鍵(推薦使用)

2、建立表的時候儘量使用代理主鍵建立表

主鍵的生成策略
1、increment:適用於short,int,long作為主鍵,不是使用的資料庫自動增長機制
    Hibernate中提供的一種增長機制
        先進行查詢 select max(id) from user
        再進行插入 獲得最大值+1作為新的記錄的主鍵
    問題:不能在叢集環境下或者有併發訪問的情況下使用

2、identity:適用於short,int,long作為主鍵,但是這個必須使用在有自動增長資料庫中,才有的是資料庫底層的自動增長機制
    底層使用的是資料庫的自動增長(auto_increment)像Oracle資料庫沒有自動增長

3、sequence:適用於short,int,long作為主鍵,底層使用的是序列的增長方式
    Oracle資料庫底層沒有自動增長,想自動增長需要使用序列

4、uuid:適用於char,varchar型別的作為主鍵
    使用隨機的字串作為主鍵

5、native:本地策略,根據底層的資料庫不同,自動選擇適用於該種資料庫的生成策略(short,int,long)
    如果底層使用的是MySQL的資料庫,相當於identity
    如果底層使用的是Oracle資料庫,相當於sequence

6、assigned:主鍵的生成不用Hibernate管理了,必須手動設定主鍵

持久化物件的狀態
1、Hibernate的持久化類
    持久化類:Java類與資料庫的某個表建立了對映關係,這個表就稱之為持久化類
        持久化類=Java類+hbm的配置檔案

2、Hibernate的持久化類的狀態
    Hibernate為了管理持久化類:將持久化類分成了三個狀態
        瞬時態:Transient Object
            沒有持久化標識OID,沒有被納入到Session物件的管理

        持久態:Persistent Object
            有持久化標識OID,已經被納入到Session物件的管理
            注意:持久化持久態的物件有自動更新資料庫的能力

        脫管態:Detached Object
            有持久化標識OID,沒有被納入到Session物件的管理

持久化物件的狀態的轉換
    1、瞬時態Transient      沒有oid沒有被session管理
        獲得瞬時態的物件
            User user = new User();
        瞬時態物件轉換成持久態
            save()/saveOrUpdate()
        瞬時態物件轉換成脫管態
            user.setId(1)
    2、持久態Persistent         有持久化標識的OID,已經被納入到Session物件的管理
        獲得持久態的物件
            get()/load()
        持久態轉換成瞬時態物件
            delete()    比較有爭議的,進入特殊的狀態(刪除態:Hibernate中不建議使用的)
        持久態物件轉換成脫管物件
            session的close()/evict()/clear()
    3、脫管態Detached           有持久化標識OID,沒有被納入到Session物件的管理
        獲得脫管態物件,不建議直接獲得脫管態的物件
            User user = new User();
            user.setId(1);
        脫管態物件轉換成持久態物件
            update()/saveOrUpdate()/lock()
        脫管態物件轉換成瞬時態物件
            user.setId(null)
    4、持久態物件有自動更新資料庫的能力

Session物件的一級快取
1、什麼是快取
    其實就是一塊記憶體空間,將資料來源(資料庫或檔案)中的資料放到快取中,再次獲取的時候,直接從快取中獲取,可以提升程式的效能

2、Hibernate框架提供了兩種快取
    一級快取        自帶的不可解除安裝的,一級快取的生命週期與session一致,一級快取稱為session級別的快取
    二級快取        預設沒有開啟,需要手動配置才可以使用的,二級快取可以在多個session中共享資料,二級快取稱為是sessionFactory級別的快取

3、Session物件的快取概述
    Session介面中,有一系列的java的集合,這些java集合構成了Session級別的快取(以及快取),將物件存入到一級快取中,
    session沒有結束生命週期,那麼物件在session中存放著

    記憶體中包含session例項->Session的快取(一些集合)->集合中包含的是快取物件

4、證明一級快取的存在,編寫查詢的程式碼即可證明
    在同一個session物件中查詢兩次,可以證明用了快取

5、Hibernate框架是如何做到資料庫發生包含時進行同步操作
    使用get方法查詢User物件
    然後設定User物件的一個屬性,注意:沒有做update的操作 卻發現數據庫中的記錄也改變了
    利用快照機制來完成的(SnapShot)

Session介面中與以及快取相關的方法
    Session.clear()                清空快取
    Session.evict(Object entiyty)  從一級快取中清除指定的實體物件
    Session.flush()                刷出快取

事務的概念
1、什麼是事務
    事務就是邏輯上的一組操作,組成事務的各個執行單元,操作要麼全部成功,要麼全部失敗
    轉賬的 扣錢和加錢 兩個操作組成了一個事情

2、事務的特性
    原子性     事務不可分隔
    一致性     事務執行的前後資料的完整性保持一致
    隔離性     一個事務執行的過程中,不應該收到其他的事務的干擾
    永續性     事務一旦提交,資料就永遠保持在資料庫中

3、如果不考慮隔離性,引發一些讀的問題
    髒讀          一個事務讀到另一個事務未提交的資料
    不可重複讀       一個事務讀到了另一個事務已經提交的update資料,導致多次查詢結果不一致
    虛讀          一個事務讀到了另一個事務已經提交的insert資料,導致多次查詢結果不一致

4、通過設定資料庫的隔離級別來解決讀的問題
    未提交讀:以上的讀的問題都有可能發生
    已提交讀:避免髒讀,但是不可重複讀,虛讀都有可能發生
    可重複讀:避免髒讀、不可重複讀,但是虛讀可能發生
    序列化:以上讀的情況都可以避免

5、如果要在Hibernate的框架中設定隔離級別,需要在hibernate.cfg.xml的配置檔案中通過標籤來配置
    通過hibernate.connection.isolation = 4來配置
    取值
        1-Read uncommitted isolation
        2-Read committed isolation oracle預設
        4-Repeatable read isolation mysql預設
        8-Serializable isolation

丟失更新的問題
1、如果不考慮隔離性,也會產生寫入資料的問題,這一類類的問題叫丟失更新的問題
2、例如:兩個事務同時對某一條記錄做修改,就會引發丟失更新的問題
    A事務和B事務同時獲取到一條資料,同時再做修改
    如果A事務修改完成後,提交了事務
    B事務修改完成後,不管是提交還是回滾,如果不做處理,都會對資料產生影響

3、解決方案
    悲觀鎖
        才有的是資料庫提供的一種鎖機制,如果採用了這種機制,在SQL語句後面新增for update子句
            當A事務在操作該條記錄時,會把該條記錄鎖起來,其他事務是不能操作這條記錄的
            只有當A事務提交後,鎖釋放了,其他事務才能操作該條記錄
    樂觀鎖
        採用版本號的機制來解決的,會給表結構新增一個欄位version=0,預設值是0
            當A事務在操作完該條記錄,提交事務時,會先檢查版本號,如果發現版本號的值相同時,才可以提交事務。同時會更新版本號version=1
            當B事務操作完該條記錄,提交事務時,會先檢查版本號,如果發現版本不同時,程式會出現錯誤

4、使用Hibernate框架來解決丟失更新的問題
    悲觀鎖
        使用session.get(Customer.class,1.LockMode.UPGRADE)方法
    樂觀鎖
        1、在對應的JavaBean中新增一個屬性,名稱可以是任意的。例如:private Integer version ;提供get和set方法
        2、在對映的配置檔案中,提供<version name="version"/>標籤即可

繫結本地的Session
1、JavaWeb的事務,需要在業務層使用Connection來開啟事務
    一種是通過引數的方式傳遞下去
    另一種是把Connection繫結到ThreadLocal物件中
2、現在的Hibernate框架中使用session物件來開啟事務,所以需要傳遞session物件,框架提供了ThreadLocal的方式
    需要在hibernate.cfg.xml的配置檔案中提供配置
        <property name="hibernate.current_session_context_class">thread</property>

    重新HibernateUtil工具類,使用SessionFactory的getCurrentSession()方法,獲取當前Session物件,並且該session物件不用手動關閉。
    執行緒結束了,會自動關閉
        public static Session getCurrentSession(){
            return factory.getCurrentSession();
        }

        想使用getCurrentSession()方法,必須先配置才能使用

Query查詢介面
1、具體的查詢程式碼:
    //1、查詢所有記錄
    Query query = session.createQuery("from Customer");
    List<Customer> list = query.list();
    System.out.println(list);
    //2、條件查詢
    Query query = session.createQuery("from Customer where name=?");
    query.setString(0,"l");
    List<Customer> list = query.list();
    System.out.println(list);
    //3、條件查詢
    Query query = session.createQuery("from Customer where name = :aaa and age =:bbb");
    query.setString("aaa","run");
    query.setInteger("bbb",12);
    List<Customer> list = query.list();
    System.out.println(list);

Criteria(做條件查詢非常合適)查詢介面
1、具體的查詢程式碼
    //1、查詢所有記錄
    Criteria criteria = session.createCriteria(Customer.class)
    List<Customer> list = criteria.list();
    syso(list)

    //2、條件查詢
    Criteria criteria = session.crateCriteria(Customer.class)
    criteria.add(Restrictions.eq("name","abc"));
    List<Customer> list = criteria.list();
    syso(list);

    //3、條件查詢
    Criteria criteria = session.createCriteria(Customer.class);
    criteria.add(Restrictions.eq("name","ll"));
    criteria.add(Restrictions.eq("age",11));
    List<Customer> list = criteria.list();
    syso(list); 

 hibernate.cfg.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
	"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
	"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
	<!-- 先配置sessionFactory標籤 一個數據庫對應一個sessionFactory標籤 -->
	<session-factory>
		<!-- 必須配置的五個引數 -->
		<!-- 四大引數 -->
		<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
		<property name="hibernate.connection.url">jdbc:mysql:///hibernate_day01</property>
		<property name="hibernate.connection.username">root</property>
		<property name="hibernate.connection.password">123456</property>
		<!-- 資料庫的方言 -->
		<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>

		<!-- 可選配置 -->
		<!-- 顯示SQL語句 -->
		<property name="hibernate.show_sql">true</property>
		<!-- SQL語句格式化 -->
		<property name="hibernate.format_sql">true</property>

		<!-- 生成資料庫的表結構<property name="hibernate.hbm2ddl.auto">create</property> -->

		<!-- 對映配置檔案,需要引入對映的配置檔案 -->
		<mapping resource="com/lingnet/domain/Customer.hbm.xml" />
	</session-factory>
</hibernate-configuration>