Hibernate持久化類與主鍵生成策略
Hibernate持久化類
什麼是持久化類呢?在Hibernate中持久化類的英文名稱是Persistent Object(簡稱PO),PO=POJO+hbm對映配置檔案。
對於Hibernate中的PO,有如下編寫規則:
- 必須提供一個無引數的public構造方法。
- 所有屬性要用private修飾,對外提供public的get/set方法。
- 在PO類必須提供一個標識屬性,讓它與資料庫中的主鍵對應,我們管這個屬性叫OID。
- PO類中的屬性儘量使用基本資料型別的包裝類。
- PO類不能使用final修飾符。
對於第1、2點,勿須多言,下面我著重解釋一下後面3點。
為何PO類必須提供一個標識屬性OID,讓它與資料庫中的主鍵對應呢?
OID指的是與資料庫中表的主鍵對應的屬性。Hibernate框架是通過OID來區分不同的PO物件,如果在記憶體中有兩個相同的OID物件,那麼Hibernate認為它們是同一個物件。大家理解起來不是很好理解,它涉及到關於Hibernate快取的概念,因為Hibernate是對資料庫直接操作,那麼我們為了優化它呢,肯定提供一些快取的策略。那麼在快取裡面我們怎麼知道這個物件重不重複呢?我們是通過OID來區分的。
為何PO類中的屬性應儘量使用基本資料型別的包裝類?
使用基本資料型別是沒有辦法去描述不存在的概念的,如果使用包裝型別,它就是一個物件,對於物件它的預設值是null,我們知道如果它為null,就代表不存在,那麼它就可以幫助我們去描述不存在的概念。
為何PO類不能使用final修飾符?
要回答這個問題,必須要知道Hibernate中的get/load方法的區別。這也是Hibernate中常考的面試題。我先給出答案:
雖然get/load方法它們都是根據id去查詢物件,但他倆的區別還是蠻大的:
1. get方法直接得到一個持久化型別物件,它就是立即查詢操作,也即我要什麼就查到什麼。load方法它得到的是持久化類的代理型別物件(子類物件)。它採用了一種延遲策略來查詢資料。這時如果PO類使用final修飾符,就會報錯,因為final修飾的類不可以被繼承。
2. get方法在查詢時,如果不存在返回null;load方法在查詢時,如果不存在,會產生異常——org.hibernate.ObjectNotFoundException。
現在就來程式設計釋疑以上這段話,首先我們要搭好Hibernate的開發環境,讀過我前面文章的童鞋,應該可以快速搭建好的,在此不做過多贅述。
在cn.itheima.test包下新建一個單元測試類——HibernateTest.java,我們首先測試Hibernate中的get()方法。
public class HibernateTest {
// 測試get/load方法的區別
@Test
public void test1() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
// 操作
Customer customer = session.get(Customer.class, 3);
System.out.println(customer.getClass()); // cn.itheima.domain.Customer
session.getTransaction().commit();
session.close();
}
}
測試test1()方法,可以發現Eclipse控制檯列印:
class cn.itheima.domain.Customer
這已說明get()方法直接得到是一個持久化型別物件。
再將get方法改置為load方法,可以發現Eclipse控制檯列印:
class cn.itheima.domain.Customer_$$_jvstd48_0
這似乎說明了load方法得到的是持久化類的代理型別物件(即子類物件)。
現在在這一行上加上一個斷點:
Customer customer = session.get(Customer.class, 3);
然後以斷點模式執行test1()方法,可發現get方法是立即查詢,也即我要什麼就查到什麼。
再將get方法改置為load方法,以斷點模式執行test1()方法,可發現只有當我們訪問物件的get方法時才向資料庫傳送select語句。這已然說明它採用了一種延遲策略來查詢資料。
資料庫中的t_customer表中顯然是沒有id=100的客戶的,而我們就是要查詢這個客戶,可分別試試get/load()方法。這裡也是先測試Hibernate中的get()方法。
public class HibernateTest {
// 測試get/load方法的區別
@Test
public void test1() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
// 操作
Customer customer = session.get(Customer.class, 100);
System.out.println(customer);
session.getTransaction().commit();
session.close();
}
}
測試test1()方法,可以發現Eclipse控制檯列印null
,這就說明了get方法在查詢時,如果不存在則返回null
。
再將get方法改置為load方法,可發現報如下異常:
這已然說明了load方法在查詢時,如果不存在,會產生異常org.hibernate.ObjectNotFoundException
。
Hibernate主鍵生成策略
定義hbm.xml對映檔案和pojo類時都需要定義主鍵,Hibernate中定義的主鍵型別包括自然主鍵和代理主鍵:
- 自然主鍵(業務主鍵)
具有業務含義的欄位作為主鍵,比如:學號、身份證號。 - 代理主鍵(邏輯主鍵)
不具有業務含義的欄位作為主鍵(例如自增id),比如:mysql自增主鍵,oracle序列生成的主鍵、uuid()方法生成的唯一序列串。
建議:企業開發中使用代理主鍵!
主鍵生成器 | 描述 |
---|---|
increment | 代理主鍵。由Hibernate維護一個變數,每次生成主鍵時自動以遞增。問題:如果有多個應用訪問一個數據庫,由於每個應用維護自己的主鍵,所以此時主鍵可能衝突。建議不採用。優點:可以方便跨資料庫平臺。缺點:不適合高併發訪問。 |
identity | 代理主鍵。由底層資料庫生成識別符號。條件是資料庫支援自動增長資料型別。比如:mysql的自增主鍵,oracle不支援主鍵自動生成。如果資料庫支援自增建議採用。優點:由底層資料庫維護,和Hibernate無關。缺點:只能對支援自動增長的資料庫有效,例如mysql。 |
sequence | 代理主鍵。Hibernate根據底層資料庫序列生成識別符號。條件是資料庫支援序列,比如oracle的序列。如果資料庫支援序列建議採用。優點:由底層資料庫維護,和Hibernate無關。缺點:資料庫必須支援sequence方案,例如oracle。 |
native | 代理主鍵。根據底層資料庫自動選擇identity、sequence、hilo,由於生成主鍵策略的控制權由Hibernate控制,所以不建議採用。優點:在專案中如果存在多個數據庫時使用。缺點:效率比較低。 |
uuid | 代理主鍵。Hibernate採用128位的UUID演算法來生成識別符號。該演算法能夠在網路環境中生成唯一的字串識別符號。此策略可以保證生成主鍵的唯一性,並且提供了最好的資料庫插入效能和資料庫平臺的無關性。建議採用。優點:與資料庫無關,方便資料庫移植,效率高,不訪問資料庫就可以直接生成主鍵值,並且它能保證唯一性。缺點:uuid長度大(32位十六進位制數),佔用空間比較大,對應資料庫中char/varchar型別。 |
assigned | 自然主鍵。由java程式負責生成識別符號。不建議採用。儘量在操作中避免手動對主鍵操作。 |
瞭解上面的知識之後,我來告訴大家怎麼來配置。
increment
<id name="id" column="id" type="int"> <!-- java資料型別 --> <!-- 主鍵生成策略 --> <generator class="increment"></generator> </id>
identity
<id name="id" column="id" type="int"> <!-- java資料型別 --> <!-- 主鍵生成策略 --> <generator class="identity"></generator> </id>
sequence
<id name="id" column="id" type="int"> <!-- java資料型別 --> <!-- 主鍵生成策略 --> <generator class="sequence"></generator> </id>
如果這樣配置,則預設使用的序列是hibernate_id。但你也可以給其指定一個序列,比如說在Oracle資料庫裡面手動建立了一個序列,在Oracle資料庫中建立一個序列的語法:
create sequence 序列名稱;
,則這時就該這麼配置:<id name="id" column="id" type="int"> <!-- java資料型別 --> <!-- 主鍵生成策略 --> <generator class="sequence"> <param name="sequence">序列名稱</param> </generator> </id>
native
<id name="id" column="id" type="int"> <!-- java資料型別 --> <!-- 主鍵生成策略 --> <generator class="native"></generator> </id>
uuid
<id name="id" column="id" type="string"> <!-- java資料型別 --> <!-- 主鍵生成策略 --> <generator class="uuid"></generator> </id>
注意:主鍵的type應是string。
assigned
<id name="id" column="id" type="int"> <!-- java資料型別 --> <!-- 主鍵生成策略 --> <generator class="assigned"></generator> </id>
注意:儘量在操作中避免手動對主鍵操作。
持久化物件的三種狀態
Hibernate中持久化物件有三種狀態:
- 瞬時態:也叫做臨時態或自由態,它一般指我們new出來的物件,它不存在OID,與Hibernate Session無關聯,在資料庫中也無記錄。它使用完成後,會被JVM直接回收掉,它只是用於資訊攜帶。
簡單說:無OID且與資料庫中的資訊無關聯,不在Session管理範圍內。 - 持久態:在Hibernate Session管理範圍內,它具有持久化標識OID。它的特點是在事務未提交前一直是持久態,當它發生改變時,Hibernate是可以檢測到的。
簡單說:有OID且由Session管理,在資料庫中有可能有,也有可能沒有。 - 脫管態:也叫做遊離態或離線態,它是指持久態物件失去了與Session的關聯,脫管態物件它存在OID,在資料庫中有可能存在,也有可能不存在。對於脫管態物件,它發生改變時Hibernet不能檢測到。
接著來測試一下持久化物件的三種狀態,程式碼如下:
public class HibernateTest {
// 測試持久化物件的三種狀態
@Test
public void test2() {
// 1.得到session
Session session = HibernateUtils.openSession();
session.beginTransaction();
Customer c = new Customer(); // 瞬時態(無OID,與session無關聯)
c.setName("張三");
c.setSex("男");
session.save(c); // 建立c與session的關聯關係,它就是持久態的了(有OID)
// 2.事務提交,並關閉session
session.getTransaction().commit();
session.close();
System.out.println(c.getId()); // 斷開了與session的關聯,它就是脫管態的了(有OID)
}
}
持久化類三種狀態之間的切換
判斷持久化類物件三種狀態的依據:
- 是否有OID
- 判斷是否與Session關聯
持久化類物件三種狀態之間的切換可參考下圖:
我稍微做一下解釋:
瞬時態(new出來的)
瞬時→持久:save()、saveOrUpdate()方法
瞬時→脫管(遊離):可手動設定oid,但不建議這麼做。如下:public class HibernateTest { // 測試持久化物件的三種狀態 @Test public void test2() { // 1.得到session Session session = HibernateUtils.openSession(); session.beginTransaction(); Customer c = new Customer(); // 瞬時態(無OID,與session無關聯) c.setName("張三"); c.setSex("男"); c.setId(7); // 瞬時→脫管(遊離) System.out.println(c.getId()); } }
持久態,它是由Session管理。
持久→瞬時:delete()——這麼操作以後相當於資料庫裡面就沒有這個記錄了,被刪除後的持久化物件不在建議使用了。
持久→脫管:注意Session本身是有快取的,它的快取就是所說的一級快取。- evict:清除一級快取中指定的一個物件
- clear:清空一級快取
- close:關閉,也即清空一級快取
脫管態(我們要知道它是無法直接獲取的)
脫管→瞬時:直接將oid刪除(不建議這麼做,因為我們不建議操作脫管態的物件)。如:public class HibernateTest { // 測試持久化物件的三種狀態 @Test public void test2() { // 1.得到session Session session = HibernateUtils.openSession(); session.beginTransaction(); Customer c = new Customer(); // 瞬時態(無OID,與session無關聯) c.setName("張三"); c.setSex("男"); c.setId(7); // 瞬時→脫管(遊離) c.setId(null); // 脫管(遊離)→瞬時 System.out.println(c.getId()); } }
脫管→持久:update、saveOrUpdate、lock(過時),也是不建議這麼做。
相關推薦
(轉) Hibernate持久化類與主鍵生成策略
bject 規則 修飾符 cti arc arch 斷點 可能 策略 http://blog.csdn.net/yerenyuan_pku/article/details/65462930 Hibernate持久化類 什麽是持久化類呢?在Hibernate中持久化類的英
Hibernate持久化類與主鍵生成策略
Hibernate持久化類 什麼是持久化類呢?在Hibernate中持久化類的英文名稱是Persistent Object(簡稱PO),PO=POJO+hbm對映配置檔案。 對於Hibernate中的PO,有如下編寫規則: 必須提供一個無引數的publi
【Hibernate(二)】持久化類、主鍵生成策略和一級快取
1.2 持久化類的編寫規則 1.2.1 持久化類的概述 什麼是持久化類 持久化:將記憶體中的一個物件持久化到資料庫中過程。Hibernate框架就是用來進行持久化的框架。 持久化類:一個Java物件與資料庫的表建立了對映關係,那麼這個類在Hibernate中稱為是持久化類。
Hibernate工具類和主鍵生成策略
建立hibernate的好處 1.方便獲取session繪畫,用來操作資料庫 2.用來檢測所有的對映檔案配置是否準確 package com.two.util; import org.hibernate.Session; import org.hibernate.SessionFact
使用註解風格學習Hibernate和JPA的主鍵生成策略
主鍵是關係資料庫中的一個基本概念,它用來保證記錄的唯一性。簡單來說,就是同一張資料庫表中,不允許存在多條相同主鍵的記錄。主鍵生成策略,就是當向資料庫表中插入記錄的時候,這個記錄的主鍵該如何生成。絕大部分情況下,主鍵都是沒有業務含義的,所以開發者不會、也不需要,顯示地
Hibernate--Increment和Hilo主鍵生成策略原理
最近專案中遇到叢集問題,比如我們有兩個叢集節點,在正常情況下只有一個節點工作(A),當出現異常時切換到另一個叢集節點(B)上。專案中使用Hibernate的increment作為資料庫主鍵生成策略。它的原理如下: Hibernate初始化完成後,當獲取主鍵時,
hibernate實體類(主鍵生成、date日期、列舉型別)
package com.xueyoucto.xueyou; import org.hibernate.annotations.GenericGenerator; import javax.persistence.*; import java.util.Date; /** * Created by Ad
Hibernate各種主鍵生成策略與配置詳解
文檔 最簡 重啟 如果 自定義 早期 出錯 lsp 當地時間 1、assigned 主鍵由外部程序負責生成,在 save() 之前必須指定一個。Hibernate不負責維護主鍵生成。與Hibernate和底層數據庫都無關,可以跨數據庫。在存儲對象前,必須要使用主鍵的sett
hibernate基礎(三)——主鍵生成策略與flush快取清理
在瞭解此文前,請首先閱讀: 在使用hibernate時,我們還是很有必要將hibernate的內部實現原理來搞清楚一下的。比如,hibernate在儲存一個物件時, 它的
Hibernate主鍵生成策略與save()方法是否發sql語句的研究
public class SessionFlushTest extends TestCase { /** * 測試uuid主鍵生成策略 */ public void testSave1() {
Hibernate 主鍵生成策略
關閉 min conn mine 數據 xml文件 ive orm 刪除 第一步:引入jar包 省略 第二部:創建實體類 package cn.hibernate.bean; public class Student { private Integer sId;
Hibernate 之主鍵生成策略小總結
rem 時間值 多線程 class 判斷 acl 選擇 hiberna bsp 主鍵生成策略大致分兩種: 手工控制策略 自動生成策略【框架自動生成和數據庫自動生成】 手工控制策略: assigned:類型是任意的,需要在 save() 到數據庫前,編碼人員手工設置主鍵
Hibernate主鍵生成策略
要求 mage src ble 配置 生成策略 padding pac 之前 Hibernate主鍵生成策略 1 hibernate要求實體類裏面有一個屬性作為唯一值,對應表主鍵,主鍵可以不同生成策略 2 hibernate主鍵生成策略有很多的值 3 在cl
Hibernate框架的主鍵生成策略
如果 維護 自己 db2 取值 identity 返回 nat 數字 在Hibernate中,id元素的<generator>子元素用於生成持久化類的對象的唯一標識符,也就是主鍵。Hibernate框架中定義了許多主鍵生成策略類,也叫生成器類。所有的生成器類
hibernate框架學習筆記4:主鍵生成策略、對象狀態
alt rri gen 線程安全 理論 微軟 unit conf lose 創建一個實體類: package domain; public class Customer { private Long cust_id; private Stri
Hibernate主鍵生成策略strategy = "increment"報錯違反唯一性約束
block 一個 blog tps 發現 rate 51cto image 主鍵 背景2018年7月份,系統爆出一條bug。就是支持Excel導入的功能,導入第二次同模板不同數據時,報錯,違反唯一性約束。就死活用不了了,重啟Tomcat才能恢復使用。但只能到一個文件 分析需
Hibernate(2)——IDEA+maven+hibernate主鍵生成策略
Maven版本hibernate程式 Pom.xml <dependencies> <dependency>
Hibernate自定義主鍵生成策略
在沒使用hibernate註解式之前,我們是需要建立hbm.xml的這樣一個用於對映的配置檔案,而我們的主鍵生成的方式則需要在這個xml的檔案內區定義。 黃色框框內就是我們主鍵生成的多種方式 1. <generator class="cok.zking
hibernate入門2主鍵生成策略
1. hibernate的主鍵生成器: generator元素:表示了一個主鍵生成器,它用來為持久化類例項生 成唯一的標識 。 1.1 程式設計師自己控制:assigned 1.2 資料庫控制: identity(標識列/自動增長) sequence 1.3 h
hibernate:主鍵生成策略
1.assigned 程式設計師自己控制:不受資料庫的影響(sid自增長也沒用,得程式設計師自己定義) <generator class="assigned"></generator> 資料型別不限、儲存前必須賦值 2.identity