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。