天天看點

Hibernate架構入門學習(二)

個人部落格原文連結

持久化類和主鍵

持久化類

  1. 持久化類的概念
    • 相當于一個Java類(咱們編寫的JavaBean),這個Java類與表建立了映射關系就可以成為持久化類。持久化類 = JavaBean + 映射(xxx.hbm.xml)。
  2. 持久化類編寫規範
    • 提供一個無參public通路控制符的構造器:底層需要進行反射。
    • 提供一個辨別屬性,映射資料表主鍵字段:唯一辨別OID。資料庫中通過主鍵。Java對象通過位址确定對象。持久化類通過唯一辨別OID确定記錄。
    • 所有屬性提供public通路控制符的set或者get方法。
    • 辨別屬性應盡量使用基本資料類型的包裝類型。
    • 基本上與JavaBean的編寫規範一緻

主鍵

  1. 自然主鍵
    • 對象本身的一個屬性。例如建立一個人員表,每個人都有一個身份證号(唯一的)。使用身份證号作為表的主鍵。即自然主鍵。(開發中不會使用這種方式)
  2. 代理主鍵
    • 不是對象本身的一個屬性。例如建立一個人員表,為每個人員單獨建立一個字段。用這個字段作為主鍵,這個字段沒有任何實際含義隻用來當主鍵。即代理主鍵。(實際開發中使用)
  3. 主鍵生成政策
    • increment:适用于short,int,long類型作為主鍵,先查詢主鍵的已有最大值,再最大值+1。不能在叢集環境下或者有并發通路的情況下使用。
    • identity:适用于short,int,long類型作為主鍵,采用的是資料庫底層的自動增長機制,所選的資料庫必須支援自動增長機制才可以,例如MySQL支援自動增長機制,Oracle則不支援。
    • sequence:适用于short,int,long作為主鍵。底層使用的是序列的增長方式。所選的資料庫必須支援序列增長才可以,例如Oracle支援序列增長,MySQL則不支援。
    • uuid:适用于char,varchar類型的作為主鍵。使用随機的字元串作為主鍵。(實際開發中經常使用)
    • native:适用于short,int,long類型作為主鍵,采用本地政策。根據底層的資料庫不同,自動選擇适用于該種資料庫的生成政策。(實際開發中經常使用)
    • assigned:取消Hibernate自動管理主鍵,自己手動設定主鍵。

持久化對象

持久化對象的三個狀态

  1. 瞬時态:Transient Object
    • 沒有持久化辨別OID, 沒有被納入到Session對象的管理。
  2. 持久态:Persistent Object
    • 有持久化辨別OID,已經被納入到Session對象的管理。
  3. 脫管态:Detached Object
    • 有持久化辨別OID,沒有被納入到Session對象的管理。
  4. 案例
@Test
public void test3(){		
	Session session = HibernateUtils.getSession();
	Transaction tr = session.beginTransaction();
	
	//持久化Student對象
	//進入瞬時态,沒有持久化辨別OID, 沒有被納入到Session對象的管理。
	Student s = new Student();
	s.setS_name("小明");
	//使用session儲存持久化對象s,把s對象也儲存到session的緩存中。
	//進入持久态,有持久化辨別OID, 被納入到Session對象的管理。
	Serializable id = session.save(s);
	tr.commit();
	//進入脫管态,有持久化辨別OID,沒有被納入到Session對象的管理。
	session.close();
}
           

Hibernate持久化對象的狀态的轉換

  1. 瞬時态
    • 擷取瞬時态對象:

      User user = new User()

    • 瞬時态——>持久态:

      save()/saveOrUpdate()

    • 瞬時态——>脫管态:

      user.setId(1)

  2. 持久态
    • 擷取持久态對象:

      get()/load()

    • 持久态——>瞬時态:

      delete()

      (進入特殊的狀态删除态:Hibernate中不建議使用)
    • 持久态——>脫管态:

      session.[close()/evict()/clear()]

  3. 脫管态
    • 擷取脫管态對象:

      User user = new User();user.setId(1);

      (不建議直接擷取脫管态對象)
    • 脫管态——>持久态:

      update()/saveOrUpdate()/lock()

    • 脫管态——>瞬時态:

      user.setId(null)

  4. 注意:持久态對象具有自動更新資料庫的能力!!!!!!!!

Session的一級緩存

Session的一級緩存機制(重點)

  1. 緩存的概念

    -其實就是一塊記憶體空間,将資料源(資料庫或者檔案)中的資料存放到緩存中。再次擷取的時候 ,直接從緩存中擷取,可以提升程式的性能!

  2. Hibernate的緩存機制
    • 一級緩存:自帶的不可解除安裝的。一級緩存的生命周期與session一緻。一級緩存稱為session級别的緩存。
    • 二級緩存:預設沒有開啟,需要手動配置才可以使用的。二級緩存可以在多個session中共享資料,二級緩存稱為是sessionFactory級别的緩存。
  3. Session對象的緩存(一級緩存)概述
    • Session接口中,有一系列的java的集合,這些java集合構成了Session級别的緩存(一級緩存).将對象存入到一級緩存中,session沒有結束生命周期,那麼對象在session中存放着。
    • 記憶體中包含Session執行個體——>Session的緩存(一些集合)——> 集合中包含的是緩存對象!
  4. 證明一級緩存的存在
    • 在同一個Session對象内連續進行兩次查詢,隻有第一次有sql語句,後面的沒有sql語句,這是因為第一次從資料庫擷取資料已經把對象緩存到session中了,後面的隻要從session緩存中直接擷取資料即可。
  5. Hibernate架構是如何做到資料發生變化時進行同步操作的呢?
    • 使用get方法查詢一個對象,然後設定該對象的一個屬性,發現資料庫的資料也改變了
    • 利用快照機制來完成

快照機制

  • 利用快照機制來儲存自動更新資料庫的能力
/**
 * 測試快照機制
 */
@Test
public void test4(){		
	Session session = HibernateUtils.getSession();
	Transaction tr = session.beginTransaction();
	//擷取持久化對象
	//先查詢到對象,把查詢結果儲存到Student對象中,也緩存到session的緩存區域中和session的快照區域
	Student s = session.get(Student.class,"402881e763fe56140163fe5618a40000");
	//重新設定名稱
	s.setS_name("小張"); //修改session緩存區域中的資料
	/*送出之前:自己比對一下緩存區域和快照區域的資料是否一緻,如果是一緻的,沒問題
	 * 如果不一緻,修改資料庫中的對應值,同時把快照區域的值更新。
	 * */
	tr.commit();
	//session銷毀了緩存,所有内容都沒了
	session.close();
}
           

控制Session的一級緩存

  1. Session.clear():清空緩存
  2. Session.evict(Object entity):清空指定對象的緩存
  3. Session.flush():重新整理緩存

Hibernate中的事務與并發

事務

  1. 事務的概念
    • 事務就是邏輯上的一組操作,組成事務的各個執行單元,操作要麼全都成功,要麼全都失敗。例如銀行轉賬。
  2. 事務的特性
    • 原子性 :事務不可分割。
    • 一緻性 :事務執行的前後資料的完整性保持一緻。
    • 隔離性 :一個事務執行的過程中,不應該受到其他事務的幹擾。
    • 持久性 :事務一旦送出,資料就永久保持到資料庫中。
  3. 如果不考慮隔離性會引發的問題
    • 髒讀:一個事務讀到了另一個事務未送出的資料.
    • 不可重複讀: 一個事務讀到另一個事務已送出的update資料,導緻多次查詢結果不一緻。
    • 虛讀:一個事務讀到了另一個事務已經送出的insert資料,導緻多次查詢結果不一緻。
  4. 通過設定資料庫隔離級别來解決上述問題
    • 未送出讀:以上的讀的問題都有可能發生。
    • 已送出讀:避免髒讀,但是不可重複讀,虛讀都有可能發生。
    • 可重複讀:避免髒讀,不可重複讀.但是虛讀是有可能發生。
    • 串行化:以上讀的情況都可以避免。
  5. 在Hibernate架構中設定隔離級别
    • 通過在hibernate.cfg.xml的配置檔案中,通過

      hibernate.connection.isolation

      标簽來配置
    • 1:Read uncommitted isolation
    • 2:Read committed isolation
    • 4:Repeatable read isolation
    • 8:Serializable isolation

丢失更新

  1. 丢失更新的概念
    • 不考慮隔離性,也會産生寫入資料的問題,這一類的問題叫丢失更新的問題。
    • 兩個事務同時對某一條記錄做修改,就會引發丢失更新的問題。
      • A事務和B事務同時擷取到一條資料,同時再做修改
      • 如果A事務修改完成後,送出了事務
      • B事務修改完成後,不管是送出還是復原,如果不做處理,都會對資料産生影響,B事務會覆寫掉A事務的修改操作,使A事務的操作丢失。
  2. 解決方案
    • 悲觀鎖(不推薦)
      • 采用的是資料庫提供的一種鎖機制,如果采用做了這種機制,在SQL語句的後面添加

        for update

        子句
        • 當A事務在操作該條記錄時,會把該條記錄鎖起來,其他事務是不能操作這條記錄的。
        • 隻有當A事務送出後,鎖釋放了,其他事務才能操作該條記錄
    • 樂觀鎖
      • 采用版本号的機制來解決的。會給表結構添加一個字段version=0,預設值是0
        • 當A事務在操作完該條記錄,送出事務時,會先檢查版本号,如果發生版本号的值相同時,才可以送出事務。同時會更新版本号version=1.
        • 當B事務操作完該條記錄時,送出事務時,會先檢查版本号,如果發現版本不同時,程式會出現錯誤。
  3. 使用Hibernate架構解決丢失更新的問題
    • 悲觀鎖(不推薦)
      • 使用

        session.get(Customer.class, 1,LockMode.UPGRADE)

        方法
    • 樂觀鎖
      • 1.在對應的JavaBean中添加一個屬性,名稱可以是任意的。例如:

        private Integer version;

        生成對應的get和set方法。
      • 2.在映射的配置檔案中,提供

        <version name="version"/>

        标簽即可。

綁定本地的Session

  • 在Hibernate架構中提供了ThreadLocal的方式來傳遞Session對象,使用Session對象來開啟事務
    1. 修改hibernate.cfg.xml配置檔案
      • <property name="hibernate.current_session_context_class">thread</property>

    2. 修改HibernateUtil的工具類,使用SessionFactory的getCurrentSession()方法,擷取目前的Session對象。并且該Session對象不用手動關閉,線程結束了,會自動關閉。
    3. 注意:getCurrentSession()方法必須配置後才可以使用。
    4. 流程:在業務層從ThreadLocal擷取Session對象,然後開啟事務,在捕獲異常中復原事務,不需要在finally塊中手動關閉session了。在資料層直接從ThreadLocal擷取Session對象,然後把相應對象儲存到Session對象中即可。

Hibernate架構的查詢方式

Query查詢接口

  1. 查詢所有資料
/**
 * 測試Query的查詢接口,查詢所有資料
 * 持久化類相當于表
 */
@Test
public void run1(){
	Session session = HibernateUtils.getSession();
	Transaction tr = session.beginTransaction();
	//查詢方式HQL:相當于select * from user
	Query query = session.createQuery("from User");
	//查詢
	List<User> list = query.list();
	//列印查詢結果
	for (User user : list) {
		System.out.println(user);
	}
	tr.commit();
	session.close();
}
           
  1. 通過

    條件查詢
/**
 * 測試Query的查詢接口,條件查詢方式一
 * 持久化類相當于表
 */
@Test
public void run2(){
	Session session = HibernateUtils.getSession();
	Transaction tr = session.beginTransaction();
	//查詢方式HQL:相當于select * from user where u_id = 1; 
	Query query = session.createQuery("from User where id = ?");
	//設定?參數的值,?的下标從0開始
	query.setInteger(0, 1);
	//查詢
	List<User> list = query.list();
	//列印查詢結果
	for (User user : list) {
		System.out.println(user);
	}
	tr.commit();
	session.close();
}
           
  1. 通過

    :字元串

    條件查詢
/**
 * 測試Query的查詢接口,條件查詢方式二
 * 持久化類相當于表
 */
@Test
public void run3(){
	Session session = HibernateUtils.getSession();
	Transaction tr = session.beginTransaction();
	//查詢方式HQL:相當于select * from user where u_name="張三";
	Query query = session.createQuery("from User where name like :a");
	//設定a參數的值
	query.setString("a", "%三");
	//查詢
	List<User> list = query.list();
	//列印查詢結果
	for (User user : list) {
		System.out.println(user);
	}
	tr.commit();
	session.close();
}
           

Criteria查詢接口(适合條件查詢)

  1. 查詢所有記錄
/**
 * 測試Criteria查詢接口(适合條件查詢)
 */
@Test
public void run1(){
	Session session = HibernateUtils.getSession();
	Transaction tr = session.beginTransaction();
	//先擷取到Criteria接口
	Criteria criteria = session.createCriteria(User.class);
	//沒有添加條件,查詢所有資料select * from t_user
	List<User> list = criteria.list();
	for (User user : list) {
		System.out.println(user);
	}
	tr.commit();
	session.close();
}
           
  1. 多條件查詢
/**
 * 測試Criteria查詢接口(适合條件查詢)
 */
@Test
public void run2(){
	Session session = HibernateUtils.getSession();
	Transaction tr = session.beginTransaction();
	//先擷取到Criteria接口
	Criteria criteria = session.createCriteria(User.class);
	//select * from t_user where u_age > 18 and like '%測試%';
	//criteria是Hibernate架構提供的條件查詢對象,想傳入條件需要使用工具類Restrictions
	//Restrictions提供靜态方法拼接查詢條件
	criteria.add(Restrictions.gt("age",18));
	//繼續添加條件
	criteria.add(Restrictions.like("name","%測試%" ));
	List<User> list = criteria.list();
	for (User user : list) {
		System.out.println(user);
	}
	tr.commit();
	session.close();
}
           

繼續閱讀