Hibernate 主鍵生成策略、一級緩存及事務管理
1.持久化類的編寫規則
- 對持久化類提供一個無參的構造方法 底層:反射生成實例
- 屬性需要私有,提供public 的set和get方法 :Hibernate 中獲取,設置對象的值。
- 對持久化類提供一個唯一標識的 OID 與數據庫主鍵對應 :java中通過對象地址區分是否是同一對象,數據庫中通過主鍵確認是否是同一個記錄,在hibernate中通過持久化類的OID的屬性區分是否是同一對象。
- 持久化類中的屬性儘量使用包裝類屬性,包裝類型默認值爲null,基本數據類型默認值爲0,有歧義。
- 持久化類不要使用final修飾 :延遲加載本來就是Hibernate 的一種優化手段,返回的是一個代理對象(javassist,可以對沒有實現接口的類產生代理—是用來非常底層字節碼增強技術,繼承這個類進行代理)。如果類被final 修飾不能被繼承了,就不能產生代理對象,延遲加載也就失效,此時的get 方法和load方法 一致。
2.主鍵生成策略
2.1主鍵的分類
2.1.1自然主鍵
- 主鍵本身就是表中的一個字段(實體中的一個具體的屬性)
- 創建一個人員表,人員都會有一個身份證號(唯一不可重複),人員的身份證號作爲主鍵,這種主鍵爲自然主鍵。
2.1.2代理主鍵(儘量使用)
- 主鍵的本身不是表中必須的一個字段(不是實體類的牧歌具體的屬性)
- 創建一個人員表,沒有使用人員中的身份證號,用了一個與這個表不相關的字段ID(PNO),這種主鍵就是代理主鍵。
- 在實際開發中,儘量使用主鍵。、
- 一旦主鍵參與到業務邏輯中,後期可能要修改源代碼。
- 好的程序設計滿足OCP 原則:對程序的擴展是open的,對修改源碼是close的。
2.1.3主鍵生成策略
2.1.3.1 Hibernate 的主鍵生成策略
- 在實際開發中一般不容許用戶手動設置主鍵,一般將主鍵交給數據庫,手動編寫程序進行設置,在Hibernate中爲減少程序編寫,提供了很多種主鍵的生成策略。
<hibernate-mapping>
<!--建立類與表的影射-->
<!--當類名與表名同名時,可省略表名的映射-->
<class name="com.sunlong.hibernate01.domain.Custom" table="cst_customer">
<!--建立類中的屬性與表中的主鍵對應-->
<id name="cust_id" column="cust_id">
<!--主鍵的生成策略-->
<generator class="native"/>
</id>
</hibernate-mapping>
- increment
Hibernate 中提供的自動增長機制,適用 short、int、long類型的主鍵,在單線程中使用。 - identity
適用short、int、long類型的主鍵,使用的是數據庫底層的自動增強機制。適用於有自動增器機制數據庫(MySQL、MSSQL),Oracle 沒有自增長機制。 - sequence
適用short、int、long類型的主鍵,採用的是序列的方式。(Oracle支持序列)MySQL 不能使用。 - uuid
適用於字符串類型主鍵。使用Hibernate 中的隨機方式生成字符串主鍵。 - native
本地策略,根據數據庫的能力選擇 identity、sequence。 - assigned
Hibernate 放棄外鍵的管理,需要通過手動編寫程序或者用戶自己設置。 - foreign
外部的,一對一的一種關聯映射的情況下使用。
2.2 持久化類的三種狀態
- Hibernate 是持久層框架,通過持久化類完成ORM操作。Hibernate 爲了更好的管理持久化類,將持久化類分爲三種狀態。 持久化類 = java類 + 映射。
2.2.1 瞬時態
-
這種對象沒有唯一的標識OID ,沒有被session 管理,稱爲瞬時態對象。
-
瞬時態對象
-
獲得
Customer c = new Custonmer(); -
狀態轉化
- 瞬時—>持久
save() saveOrUpdate() - 瞬時—>脫管
- 瞬時—>持久
2.2.2持久態*
- 這種對象有唯一的標識OID ,被session管理,稱爲持久態對象。
- 可以自動更新到數據庫。
- 獲得
get() load() find() iterte()
Customer c = session.get(Custonmer.class,1L);
- 狀態轉化
- 持久—>瞬時
-delete() - 持久—>脫管
-close() clear() evict()
- 持久—>瞬時
- 獲得
Custonmer c = new Constmer();
c.setCust_id(1L); - 狀態轉化
- 脫管—>持久
update() saveOrUpdate() - 脫管—>瞬時
customer.setCust_id(null);
- 脫管—>持久
2.2.3 脫管態
- 這種對象有唯一的標識OID,沒有被session管理,稱爲脫管態對象。
2.2.4 持久態對象可以自動更新數據庫
@Test
//修改操作
public void demo03() {
Session session = HibernateUtils.openSession();
Transaction transaction = session.beginTransaction();
//直接創建對象進行修改
/*Custom custom = new Custom();
custom.setCust_id( 1L );
custom.setCust_name( "悟空" );
session.update( custom );*/
//先查詢,在修改(推薦)
Custom custom1 = session.get( Custom.class, 1L );
custom1.setCust_name( "八戒" );
//持久化對象可以自動更新數據庫數據,如果更新數據和數據庫信息一樣,那就只進行查詢,不進行更新語句操作
//一級緩存
//session.update( custom1 );//不寫也更新
transaction.commit();
session.close();
}
2.3 Hibernate 的一級緩存
2.3.1 緩存概述
2.3.1.1 什麼是緩存
- 是一種優化方式,將數據存入到內存中,使用的時候直接從緩存中獲取,不用通過存儲源。
2.3.2 Hibernate 的緩存
2.3.2.1 Hibernate 的一級緩存
- Hibernate 框架中提供了優化手段:緩存、抓取策略。
- Hibernate 提供了兩種緩存機制:一級緩存 二級緩存
- Hibernate 的一級緩存:稱爲session緩存
2.3.2.2 一級緩存證明
@Test
public void demo01() {
/*
* 一級緩存
* */
Configuration cfg = new Configuration().configure( "com/sunlong/hibernate02/hibernate.cfg.xml" );
SessionFactory sessionFactory = cfg.buildSessionFactory();
Session session = sessionFactory.getCurrentSession();
Transaction transaction = session.beginTransaction();
Customer customer = session.get( Customer.class, 2L );
System.out.println( customer );
//清空所有緩存,控制檯打印兩次查詢語句,否則第二次查詢同一對象,將會從一級緩存中獲取
//session.clear();
//清空指定緩存
session.evict( customer );
Customer customer1 = session.get( Customer.class, 2L );
System.out.println( customer1 );
transaction.commit();
}
2.4 Hibernate 的事務管理
2.4.1 事務回顧
2.4.1.1什麼是事務
- 事務:邏輯上的一組操作,組成這組操作的各個邏輯單元要麼成功要麼失敗。
2.4.1.2事物特性
- 原子性:事務不可分割
- 一致性:事務執行的前後,數據的完整性保持一致
- 隔離性:一個事務執行過程中,不應該是受到其他事務的干預。
- 持久性:事務執行完成後,數據就持久到數據庫中。
2.4.1.3 如果不考慮隔離性,引發安全性問題
- 讀問題
- 髒讀:一個事務讀到另一個事務未提交的事務.
- 不可重複讀:一個事務讀到另一個事務已經提交的update數據,導致在前一個事務對此查詢的結果不一致。
- 幻讀:一個事務讀到另一個事務已經提交的insert 數據,導致在前一個事務多次查詢結果不一致。
- 寫問題
- 引發兩類丟失更新
2.4.1.4 讀問題的解決
- 設置事物的隔離級別
- 未提交讀 :以上讀問題都會發生
- 讀已提交 :解決髒讀,但是不可重複讀和虛讀有可能發生
- 可重複讀 ;解決髒讀和不可重複讀,但是幻讀有可能發生
- 可串行化 :解決所有讀問題
2.4.2 Hibernate 中設置事務的隔離級別
- 1 :讀未提交
- 2 :讀已提交
- 4 :可重複讀
- 8 :可串行化
<!--設置事務隔離級別-->
<property name="hibernate.connection.isolation">4</property>
2.4.3 Service 事務管理
2.4.3.1 Hibernate 解決service 的事務管理
- 事務管理的方式
<1> 可以在業務層獲取到 Session ,並將 Session 作爲參數傳遞到 DAO。
<2> (最優方案) 可以使用 ThreadLocal 將業務層獲取的 Session 綁定到當前線程中,然後在 DAO 中獲取 Session 的時候,都從當前線程中獲取。
-Hibernate 提供 sessionFactory.getCurrentSession() 創建一個 session 和 ThreadLocal 綁定方法。
<!--配置當前線程綁定的Session-->
<property name="hibernate.current_session_context_class">thread</property>
- 配置成功後即可使用 SessionFactory的getCurrentSession() 方法
2.5 Hibernate 的其他API
2.5.1 Query (HQL)
- Query 代表面向對象的一個 Hibernate 查詢操作。在 Hibernate 中,通常使用 session.createQuery() 方法接收一個 HQL 語句,然後調用 Query 的 list() 或 uniqueResult() 方法執行查詢。所謂的HQL 是 Hibernate Query Language 的縮寫,其語法很像SQL 語句,但它是完全面向對象的。
//Query
public void demo02() {
Session session = HibernateUtils.getCurrentSession();
Transaction transaction = session.beginTransaction();
//簡單查詢
//String hql = "from Customer";
//條件查詢
//String hql = "from Customer where cust_name like ?";
//分頁查詢
String hql = "from Customer";
Query query = session.createQuery( hql );
//設置分頁
query.setFirstResult( 3 );
query.setMaxResults( 3 );
//以下兩種設置佔位符的方式都可以
//query.setParameter( 0 ,"孫%");
//query.setString( 0,"孫%" );
List<Customer> list = query.list();
for (Customer customer : list) {
System.out.println( customer );
}
transaction.commit();
}
2.5.2 Criteria :QBC (Query By Criteria)
- 更加面向對象的一種方式查詢
- 通過session獲得 session.createCriteria()
//Criteria更加面向對象的一種方式查詢
public void demo03() {
Session session = HibernateUtils.getCurrentSession();
Transaction transaction = session.beginTransaction();
//查詢所有數據
Criteria criteria = session.createCriteria( Customer.class );
//條件查詢
criteria.add( Restrictions.like( "cust_name", "孫%" ) );
//分頁查詢
criteria.setFirstResult( 3 );
criteria.setMaxResults( 3 );
List<Customer> list = criteria.list();
for (Customer customer : list) {
System.out.println( customer );
}
transaction.commit();
}
2.5.3 SQLQuery
-
SQLQuery 用於接收SQL ,在特別複雜的情況下使用,一般建議使用以上兩種。
-
以上簡單介紹了三種檢索方式,後續會在專門章節詳細介紹。