個人部落格原文連結
持久化類和主鍵
持久化類
- 持久化類的概念
- 相當于一個Java類(咱們編寫的JavaBean),這個Java類與表建立了映射關系就可以成為持久化類。持久化類 = JavaBean + 映射(xxx.hbm.xml)。
- 持久化類編寫規範
- 提供一個無參public通路控制符的構造器:底層需要進行反射。
- 提供一個辨別屬性,映射資料表主鍵字段:唯一辨別OID。資料庫中通過主鍵。Java對象通過位址确定對象。持久化類通過唯一辨別OID确定記錄。
- 所有屬性提供public通路控制符的set或者get方法。
- 辨別屬性應盡量使用基本資料類型的包裝類型。
- 基本上與JavaBean的編寫規範一緻
主鍵
- 自然主鍵
- 對象本身的一個屬性。例如建立一個人員表,每個人都有一個身份證号(唯一的)。使用身份證号作為表的主鍵。即自然主鍵。(開發中不會使用這種方式)
- 代理主鍵
- 不是對象本身的一個屬性。例如建立一個人員表,為每個人員單獨建立一個字段。用這個字段作為主鍵,這個字段沒有任何實際含義隻用來當主鍵。即代理主鍵。(實際開發中使用)
- 主鍵生成政策
- 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自動管理主鍵,自己手動設定主鍵。
持久化對象
持久化對象的三個狀态
- 瞬時态:Transient Object
- 沒有持久化辨別OID, 沒有被納入到Session對象的管理。
- 持久态:Persistent Object
- 有持久化辨別OID,已經被納入到Session對象的管理。
- 脫管态:Detached Object
- 有持久化辨別OID,沒有被納入到Session對象的管理。
- 案例
@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持久化對象的狀态的轉換
- 瞬時态
- 擷取瞬時态對象:
User user = new User()
- 瞬時态——>持久态:
save()/saveOrUpdate()
- 瞬時态——>脫管态:
user.setId(1)
- 擷取瞬時态對象:
- 持久态
- 擷取持久态對象:
get()/load()
- 持久态——>瞬時态:
(進入特殊的狀态删除态:Hibernate中不建議使用)delete()
- 持久态——>脫管态:
session.[close()/evict()/clear()]
- 擷取持久态對象:
- 脫管态
- 擷取脫管态對象:
(不建議直接擷取脫管态對象)User user = new User();user.setId(1);
- 脫管态——>持久态:
update()/saveOrUpdate()/lock()
- 脫管态——>瞬時态:
user.setId(null)
- 擷取脫管态對象:
- 注意:持久态對象具有自動更新資料庫的能力!!!!!!!!
Session的一級緩存
Session的一級緩存機制(重點)
-
緩存的概念
-其實就是一塊記憶體空間,将資料源(資料庫或者檔案)中的資料存放到緩存中。再次擷取的時候 ,直接從緩存中擷取,可以提升程式的性能!
- Hibernate的緩存機制
- 一級緩存:自帶的不可解除安裝的。一級緩存的生命周期與session一緻。一級緩存稱為session級别的緩存。
- 二級緩存:預設沒有開啟,需要手動配置才可以使用的。二級緩存可以在多個session中共享資料,二級緩存稱為是sessionFactory級别的緩存。
- Session對象的緩存(一級緩存)概述
- Session接口中,有一系列的java的集合,這些java集合構成了Session級别的緩存(一級緩存).将對象存入到一級緩存中,session沒有結束生命周期,那麼對象在session中存放着。
- 記憶體中包含Session執行個體——>Session的緩存(一些集合)——> 集合中包含的是緩存對象!
- 證明一級緩存的存在
- 在同一個Session對象内連續進行兩次查詢,隻有第一次有sql語句,後面的沒有sql語句,這是因為第一次從資料庫擷取資料已經把對象緩存到session中了,後面的隻要從session緩存中直接擷取資料即可。
- 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的一級緩存
- Session.clear():清空緩存
- Session.evict(Object entity):清空指定對象的緩存
- Session.flush():重新整理緩存
Hibernate中的事務與并發
事務
- 事務的概念
- 事務就是邏輯上的一組操作,組成事務的各個執行單元,操作要麼全都成功,要麼全都失敗。例如銀行轉賬。
- 事務的特性
- 原子性 :事務不可分割。
- 一緻性 :事務執行的前後資料的完整性保持一緻。
- 隔離性 :一個事務執行的過程中,不應該受到其他事務的幹擾。
- 持久性 :事務一旦送出,資料就永久保持到資料庫中。
- 如果不考慮隔離性會引發的問題
- 髒讀:一個事務讀到了另一個事務未送出的資料.
- 不可重複讀: 一個事務讀到另一個事務已送出的update資料,導緻多次查詢結果不一緻。
- 虛讀:一個事務讀到了另一個事務已經送出的insert資料,導緻多次查詢結果不一緻。
- 通過設定資料庫隔離級别來解決上述問題
- 未送出讀:以上的讀的問題都有可能發生。
- 已送出讀:避免髒讀,但是不可重複讀,虛讀都有可能發生。
- 可重複讀:避免髒讀,不可重複讀.但是虛讀是有可能發生。
- 串行化:以上讀的情況都可以避免。
- 在Hibernate架構中設定隔離級别
- 通過在hibernate.cfg.xml的配置檔案中,通過
标簽來配置hibernate.connection.isolation
- 1:Read uncommitted isolation
- 2:Read committed isolation
- 4:Repeatable read isolation
- 8:Serializable isolation
- 通過在hibernate.cfg.xml的配置檔案中,通過
丢失更新
- 丢失更新的概念
- 不考慮隔離性,也會産生寫入資料的問題,這一類的問題叫丢失更新的問題。
- 兩個事務同時對某一條記錄做修改,就會引發丢失更新的問題。
- A事務和B事務同時擷取到一條資料,同時再做修改
- 如果A事務修改完成後,送出了事務
- B事務修改完成後,不管是送出還是復原,如果不做處理,都會對資料産生影響,B事務會覆寫掉A事務的修改操作,使A事務的操作丢失。
- 解決方案
- 悲觀鎖(不推薦)
- 采用的是資料庫提供的一種鎖機制,如果采用做了這種機制,在SQL語句的後面添加
子句for update
- 當A事務在操作該條記錄時,會把該條記錄鎖起來,其他事務是不能操作這條記錄的。
- 隻有當A事務送出後,鎖釋放了,其他事務才能操作該條記錄
- 采用的是資料庫提供的一種鎖機制,如果采用做了這種機制,在SQL語句的後面添加
- 樂觀鎖
- 采用版本号的機制來解決的。會給表結構添加一個字段version=0,預設值是0
- 當A事務在操作完該條記錄,送出事務時,會先檢查版本号,如果發生版本号的值相同時,才可以送出事務。同時會更新版本号version=1.
- 當B事務操作完該條記錄時,送出事務時,會先檢查版本号,如果發現版本不同時,程式會出現錯誤。
- 采用版本号的機制來解決的。會給表結構添加一個字段version=0,預設值是0
- 悲觀鎖(不推薦)
- 使用Hibernate架構解決丢失更新的問題
- 悲觀鎖(不推薦)
- 使用
方法session.get(Customer.class, 1,LockMode.UPGRADE)
- 使用
- 樂觀鎖
- 1.在對應的JavaBean中添加一個屬性,名稱可以是任意的。例如:
生成對應的get和set方法。private Integer version;
- 2.在映射的配置檔案中,提供
标簽即可。<version name="version"/>
- 1.在對應的JavaBean中添加一個屬性,名稱可以是任意的。例如:
- 悲觀鎖(不推薦)
綁定本地的Session
- 在Hibernate架構中提供了ThreadLocal的方式來傳遞Session對象,使用Session對象來開啟事務
- 修改hibernate.cfg.xml配置檔案
-
<property name="hibernate.current_session_context_class">thread</property>
-
- 修改HibernateUtil的工具類,使用SessionFactory的getCurrentSession()方法,擷取目前的Session對象。并且該Session對象不用手動關閉,線程結束了,會自動關閉。
- 注意:getCurrentSession()方法必須配置後才可以使用。
- 流程:在業務層從ThreadLocal擷取Session對象,然後開啟事務,在捕獲異常中復原事務,不需要在finally塊中手動關閉session了。在資料層直接從ThreadLocal擷取Session對象,然後把相應對象儲存到Session對象中即可。
- 修改hibernate.cfg.xml配置檔案
Hibernate架構的查詢方式
Query查詢接口
- 查詢所有資料
/**
* 測試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();
}
- 通過
條件查詢?
/**
* 測試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();
}
- 通過
條件查詢:字元串
/**
* 測試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查詢接口(适合條件查詢)
- 查詢所有記錄
/**
* 測試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();
}
- 多條件查詢
/**
* 測試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();
}