Hibernate框架學習day02(主鍵生成策略、一級緩存、事務管理)

1. 持久化類的編寫規則

1.1 持久化和持久化類

  • 持久化:將內存中的一個對象持久化到數據庫的過程,Hibernate框架就是用來進行持久化的框架。
  • 持久化類:一個Java對象與數據庫的表建立了映射關係,那麼這個類在Hibernate中稱爲持久化類。
  • 持久化 = Java類 + 映射文件

1.2 持久化類的編寫規則

  • 對持久化提供一個無參的構造方法:HIbernate底層需要使用反射生成實例。
  • 屬性需要私有化,對私有屬性提供public 的get和set方法:Hibernate中獲取,設置對象的值。
  • 對持久化提供一個唯一標識OID與數據庫主鍵對應:Java中通過對象的地址區分是否是同一個對象,數據庫中通過主鍵確定是否是同一個記錄,在Hibernate中通過持久化類的OID的屬性區分是否是同一個對象。
  • 持久化類中屬性儘量使用包裝類型:因爲基本數據類型的默認值是0,那麼0就會有很多歧義。包裝類型默認值是null。
  • 持久化類不要使用final進行修飾:延遲就在本身是Hibernate一個優化手段。返回的是一個代理對象,可以對沒有實現接口的類產生代理,這種代理使用了非常底層的字節碼增強技術,繼承這個類進行代理。如果不能被繼承,不能產生代理對象,延遲就在也就失效了。load和get方法就一致了

2. 主鍵生成策略

2.1 主鍵的分類

  • 自然主鍵:
    主鍵的本身就是表中的一個字段(實體中的一個具體的屬性)。
    創建一個人員表,人員都會有一個身份證號(唯一不可重複的),使用了身份證號作爲主鍵,這種主鍵稱爲是自然主鍵。

  • 代理主鍵:
    主鍵的本身不是表中必須的一個字段(不是實體中的某個具體的屬性)
    創建一個人員表,沒有使用人員中的身份證號,用了一個與這個表不相關的字段ID。這種主鍵稱爲代理主鍵。

在實際開發中,儘量使用代理主鍵
一旦自然主鍵參與到業務邏輯中,後期有可能需要修改源代碼
好的程序設計滿足OCP原則,對程序的擴展時open的,對修改源代碼時close的。

2.2 主鍵生成策略

在實際開發中一般不允許用戶手動設置主鍵,一般將主鍵交給數據庫,手動編寫程序進行設置。在Hibernate中爲了減少程序的編寫,提供了很多種的主鍵生成策略。

  • increment:Hibernate中提供的自動增長機制,適用short、int、long類型的主鍵。在單線程程序中使用。
    • 首先發送一條Sql語句:select max(id) from 表;,然後讓id +1作爲下一條的記錄的主鍵
  • identity:適用於short、int、long類型的主鍵,使用的是數據庫底層的自動增長機制。適用於有自動增長機制的數據庫(Mysql、MSSQL),但是Oracle沒有自動增長。
  • sequence:適用於short、int、long類型的主鍵,採用的時序列的方式。Oracle支持序列,但Mysql不能使用sequence。
  • uuid: 適用於字符串類型主鍵。使用Hibernate中隨機方式生成字符串主鍵。
  • native:本地策略,可以在identity和sequence之間進行自動切換。
  • assigned:hibernate放棄外鍵的管理,需要手動編寫程序或者自己設置。
  • foreign:外部的。一對一的一種關聯映射的情況下使用。(瞭解)

3. 持久化類的三種狀態【瞭解】

3.1 持久化類的三種狀態

  • Hibernate時持久層框架,通過持久化類完成ORM操作。Hibernate爲了更好的管理持久化類,將持久化類分爲三種狀態。
    持久化類 = Java類 + 映射
  • 瞬時態:這種對象沒有唯一的標識OID,沒有被session管理,稱爲瞬時態對象。
  • 持久態:這種對象有唯一標識OID,被session管理,稱爲持久態對象。
    持久化類的持久態的對象,可以自動更新數據庫
  • 託管態:這種對象有唯一標識OID,沒有被session管理,稱爲託管態對象。

3.2區分三種持久化狀態

@Test
	//三種狀態的區分
	public void demo1() {
		Session session = HibernateUtils.openSession();
		Transaction tx = session.beginTransaction();
		
		Customer customer = new Customer();	// 瞬時態對象:沒有唯一標識OID,沒有被session管理
		customer.setCust_name("王曉東");
		
		Serializable id = session.save(customer); // 持久態對象:有唯一標識OID,被session管理
		
		tx.commit();
		session.close();
		
		System.out.println("客戶名稱: " + customer.getCust_name()); // 託管態對象:有唯一標識OID,沒有被session管理
	}

3.3 持久化的三種狀態的轉換

3.3.1 持久態的三種狀態轉換圖

在這裏插入圖片描述

3.3.2 瞬時態對象
  • 獲得:
    Customer customer = new Customer();

狀態轉換

  • 瞬時---->持久
    save(Obejct obj)、saveOrUpdate(Object obj);

  • 瞬時----->託管
    customer.setCust_id(1);

3.3.3 持久態
  • 獲得
    get()、load()、find()、iterate()
    Customer customer = session.get(Customer.class,1L);

狀態轉換

  • 持久----->瞬時
    delete()

  • 持久----->託管
    close()、clear()、evict(Object obj)

3.3.4 託管態對象
  • 獲得
    Customer customer = new Customer();
    customer.setCust_id(1L);

狀態轉換

  • 託管---->持久
    update()、 saveOrUpdate()

  • 託管----->瞬時
    customer.setCust_id(null)

3.3.5 持久態對象的特性
@Test	//	持久態對象自動更新數據庫
	public void demo2() {
		Session session = HibernateUtils.openSession();
		Transaction transaction = session.beginTransaction();
		// 獲得持久態對象:
		Customer customer = session.get(Customer.class, 1L);
		customer.setCust_name("王喇嘛");
		//session.update(customer);
        //	不寫這句話也可以將數據保存到數據庫,這是持久態對象的特徵,底層是一級緩存
		
		transaction.commit();
		session.close();
	}

4. Hibernate的一級緩存

4.1 緩存概述

  • 緩存時一種優化的方式,將數據存儲到內存中,使用的時候直接從緩存中獲取,不用通過存儲源。

4.2 hibernate的緩存

  • Hibernate框架中提供了很多優化手段,比如緩存、抓取策略。Hibernate中提供了兩種緩存機制,一級緩存和二級緩存。

  • Hibernate的一級緩存就是指Session緩存,Session緩存是一塊內存空間,用來存放互相管理的Java對象,在使用Hibernate查詢對象的時候,首先會使用對象屬性的OID值在Hibernate的一級緩存中進行查找,如果找到匹配的OID值得對象,就直接將該對象從一級緩存中取出使用,不會再查詢數據庫;如果沒有找到相同得OID值得對象,則會去數據庫中查找相應得數據。當從數據中查詢到到所需數據時,該數據信息也會放置到一級緩存中。Hibernate得一級緩存的作用就是減少對數據庫的訪問次數。

  • 再在Session接口的實現中包含了一系列的Java集合,這些Java集合構成了Session緩存。只要Session實例沒有結束生命週期,存放在它緩存中的對象也不會結束生命週期。所以一級緩存也被稱爲是Session基本的緩存。

  • Hibernate的二級緩存是SessionFactory級別的緩存,需要配置的緩存。二級緩存已經被Redis取代,實際開發過程不會使用。

  • 一級緩存是自帶的不可卸載的。

4.3 證明一級緩存的存在

package com.cmt.hibernate.demo1;

import java.io.Serializable;

import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;

import com.itzhouq.hibernate.utils.HibernateUtils;

public class Demo3 {
	// 證明一級緩存的存在
	@Test
	public void test() {
		Session session = HibernateUtils.openSession();
		Transaction transaction = session.beginTransaction();
//		Customer customer1 = session.get(Customer.class, 1L); // 發送SQL語句
//		System.out.println(customer1);
//		
//		Customer customer2 = session.get(Customer.class, 1L); // 不發送SQL語句
//		System.out.println(customer2);
		Customer customer = new Customer();
		customer.setCust_name("鳳姐");
		Serializable id = session.save(customer);
		
		Customer customer2 = session.get(Customer.class, id); // 不發送SQL語句
		System.out.println(customer2);
		
		transaction.commit();
		session.close();
	}
}

4.4 一級緩存的結構

一級緩存中的特殊區域:快照區
在這裏插入圖片描述

5. Hibernate事務管理

5.1 什麼是事務

  • 事務:事務是指邏輯上的一組操作,這組操作的各個邏輯單元要麼全部成功,要麼全都失敗。

5.2 事務的特性

  • 原子性:代表事務不可分割。
  • 一致性:代表事務執行的前後,數據的完整性保持一致。
  • 隔離性:代表一個事務的執行過程中,不應該受到其他事務的干擾。
  • 持久性:代表事務執行完成後,數據就持久化到數據庫中。

5.3 不考慮隔離性,引發安全問題

讀問題:

  • 髒讀:一個事務讀取到另一個事務未提交的數據。
    不可重複讀:一個事務讀取到另一個事務已經提交的update數據,導致在前一個事務多次查詢結果不一致。
  • 虛讀(幻讀):一個事務讀取到另一個事務已經提交的insert數據,導致在前一個事務多次查詢結果不一致。

寫問題【瞭解】
引發兩類丟失更新

5.4 讀問題的解決

設置事務的隔離級別

  • Read uncommitted:以上讀問題都會發生
  • Read committed:解決髒讀,但是不可重複讀和虛讀有可能發生(Oracle)
  • Repeatable read:解決髒讀和不可重複讀,但是虛讀有可能發生(Mysql)
  • Serlializable:解決所有讀問題

5.5 設置事務的隔離級別

在主配置文件中添加以下配置

<!-- 設置事務的隔離級別 -->
		<property name="hibernate.connection.isolation">4</property>

4爲MySQL數據庫的默認級別

數字和隔離級別的對應關係

1----------Read uncommitted isolation
2----------Read committed isolation
4----------Repeatable read isolation
8----------Serializable isolation

5.6 Hibernate解決Service事務管理

Service層的事務
在這裏插入圖片描述

代碼和配置

改寫工具類,添加getCurrentSession()訂單

package com.cmt.hibernate.utils;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

/*
 * Hibernate的工具類
 */
public class HibernateUtils {
	public static final Configuration cfg;
	public static final SessionFactory sf;
	
	static {
		cfg = new Configuration().configure();
		sf = cfg.buildSessionFactory();
	}
	
	public static Session openSession() {
		return sf.openSession();
	}
	
	public static Session getCurrentSession() {
		return sf.getCurrentSession();
	}
}

在主配置文件中配置當前線程綁定的Session

<!-- 配置當前線程綁定的Session -->
		<property name="hibernate.current_session_context_class">thread</property>

測試

package com.cmt.hibernate.demo1;

import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;

import com.itzhouq.hibernate.utils.HibernateUtils;

/*
 *  	測試當前線程綁定的Session
 */
public class Demo4 {
	@Test
	public void test() {
		Session session = HibernateUtils.getCurrentSession();
		Transaction transaction = session.beginTransaction();
		Customer customer = new Customer();
		customer.setCust_name("王希");
		session.save(customer);
		
		transaction.commit();
		//session.close();//不需要關閉
	}
}

注意:綁定線程的session不需要手動關閉。因爲當前線程自動獲得與當前線程綁定的Session,線程結束之後,自動關閉Sesson,不需要手動關閉。

6. HIbernate的其他API

6.1 Query

Query接口使用接收HQL,查詢多個對象

HQL:Hibernate Query Language:HIbernate查詢語言,這種語言與SQL的語法及其類似,面向對象的查詢語言。

代碼

package com.cmt.hibernate.demo1;

import java.util.List;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;

import com.cmt.hibernate.utils.HibernateUtils;

/*
 * 	HIbernate的其他API
 */
public class Demo5 {
	
	@Test	// Query
	public void test() {
		Session session = HibernateUtils.getCurrentSession();
		Transaction transaction = session.beginTransaction();
				
		// 通過Session獲得Query接口
		//簡單查詢
		//String hql = "from Customer";
		//條件查詢
		//String hql = "from Customer wehre cust_name like ?";
		//分頁查詢
		String hql = "from Customer";
		Query query = session.createQuery(hql);
		//設置條件
		//query.setParameter(0, "王%");
		//設置分頁
		query.setFirstResult(0);
		query.setMaxResults(3);
		
		List<Customer> list = query.list();
		for (Customer customer : list) {
			System.out.println(customer);
		}
		
		transaction.commit();
	}
}

6.2 Criteria

條件查詢:Query By Criteria

更加面向對象的一種查詢方式
代碼

@Test	// Criteria
	public void test2() {
		Session session = HibernateUtils.getCurrentSession();
		Transaction transaction = session.beginTransaction();
		//通過Session獲得Criteria對象
		Criteria criteria = session.createCriteria(Customer.class);
		//List<Customer> list = criteria.list();
		
		//條件查詢
//		Criteria add = criteria.add(Restrictions.like("cust_name", "王%"));
//		List<Customer> list = criteria.list();
		
		//分頁查詢
		criteria.setFirstResult(0);
		criteria.setMaxResults(2);
		List<Customer> list = criteria.list();
		
		for (Customer customer : list) {
			System.out.println(customer);
		}
		
		transaction.commit();
	}

6.3 SQLQuery【瞭解】

SQLQuery用於接收SQL。特別複雜情況下使用SQL。

發佈了73 篇原創文章 · 獲贊 67 · 訪問量 4521
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章