ssh框架hibernate 2持久化物件和關係關聯對映
阿新 • • 發佈:2019-02-19
什麼是持久化類
1. 持久化類:就是一個Java類(編寫的JavaBean),這個Java類與表建立了對映關係就可以成為是持久化類。
* 持久化類 = JavaBean + xxx.hbm.xml
持久化類的編寫規則
1. 提供一個無引數 public訪問控制符的構造器 -- 底層需要進行反射. 2. 提供一個標識屬性,對映資料表主鍵欄位 -- 唯一標識OID.資料庫中通過主鍵.Java物件通過地址確定物件.持久化類通過唯一標識OID確定記錄 3. 所有屬性提供public訪問控制符的 set或者get 方法 4. 標識屬性應儘量使用基本資料型別的包裝型別
區分自然主鍵和代理主鍵
1. 建立表的時候
* 自然主鍵:物件本身的一個屬性.建立一個人員表,每個人都有一個身份證號.(唯一的)使用身份證號作為表的主鍵.自然主鍵.(開發中不會使用這種方式)
* 代理主鍵:不是物件本身的一個屬性.建立一個人員表,為每個人員單獨建立一個欄位.用這個欄位作為主鍵.代理主鍵.(開發中推薦使用這種方式)
2. 建立表的時候儘量使用代理主鍵建立表
主鍵的生成策略
1. increment:適用於short,int,long作為主鍵.不是使用的資料庫自動增長機制. * Hibernate中提供的一種增長機制. * 先進行查詢 :select max(id) from user; * 再進行插入 :獲得最大值+1作為新的記錄的主鍵. * 問題:不能在叢集環境下或者有併發訪問的情況下使用. 2. identity:適用於short,int,long作為主鍵。但是這個必須使用在有自動增長資料庫中.採用的是資料庫底層的自動增長機制. * 底層使用的是資料庫的自動增長(auto_increment).像Oracle資料庫沒有自動增長. 3. sequence:適用於short,int,long作為主鍵.底層使用的是序列的增長方式. * Oracle資料庫底層沒有自動增長,想自動增長需要使用序列. 4. uuid:適用於char,varchar型別的作為主鍵. * 使用隨機的字串作為主鍵. 5. native:本地策略.根據底層的資料庫不同,自動選擇適用於該種資料庫的生成策略.(short,int,long) * 如果底層使用的MySQL資料庫:相當於identity. * 如果底層使用Oracle資料庫:相當於sequence. 6. assigned:主鍵的生成不用Hibernate管理了.必須手動設定主鍵.
Hibernate持久化物件的狀態
持久化物件的狀態
1. Hibernate的持久化類 * 持久化類:Java類與資料庫的某個表建立了對映關係.這個類就稱為是持久化類. * 持久化類 = Java類 + hbm的配置檔案 2. Hibernate的持久化類的狀態 * Hibernate為了管理持久化類:將持久化類分成了三個狀態 * 瞬時態:Transient Object * 沒有持久化標識OID, 沒有被納入到Session物件的管理. * 持久態:Persistent Object * 有持久化標識OID,已經被納入到Session物件的管理. * 脫管態:Detached Object * 有持久化標識OID,沒有被納入到Session物件的管理.
Hibernate持久化物件的狀態的轉換
1. 瞬時態 -- 沒有持久化標識OID, 沒有被納入到Session物件的管理
* 獲得瞬時態的物件
* User user = new User()
* 瞬時態物件轉換持久態
* save()/saveOrUpdate();
* 瞬時態物件轉換成脫管態
* user.setId(1)
2. 持久態 -- 有持久化標識OID,已經被納入到Session物件的管理
* 獲得持久態的物件
* get()/load();
* 持久態轉換成瞬時態物件
* delete(); --- 比較有爭議的,進入特殊的狀態(刪除態:Hibernate中不建議使用的)
* 持久態物件轉成脫管態物件
* session的close()/evict()/clear();
3. 脫管態 -- 有持久化標識OID,沒有被納入到Session物件的管理
* 獲得託管態物件:不建議直接獲得脫管態的物件.
* User user = new User();
* user.setId(1);
* 脫管態物件轉換成持久態物件
* update();/saveOrUpdate()/lock();
* 脫管態物件轉換成瞬時態物件
* user.setId(null);
4. 注意:持久態物件有自動更新資料庫的能力!!!
Session物件的一級快取
1. 什麼是快取?
* 其實就是一塊記憶體空間,將資料來源(資料庫或者檔案)中的資料存放到快取中.再次獲取的時候 ,直接從快取中獲取.可以提升程式的效能。
2. Hibernate框架提供了兩種快取
* 一級快取 -- 自帶的不可解除安裝的.一級快取的生命週期與session一致.一級快取稱為session級別的快取.
* 二級快取 -- 預設沒有開啟,需要手動配置才可以使用的.二級快取可以在多個session中共享資料,二級快取稱為是sessionFactory級別的快取.
3. Session物件的快取概述
* Session介面中,有一系列的java的集合,這些java集合構成了Session級別的快取(一級快取).將物件存入到一級快取中,session沒有結束生命週期,那麼物件在session中存放著
* 記憶體中包含Session例項 --> Session的快取(一些集合) --> 集合中包含的是快取物件!
控制Session的一級快取
1. 學習Session介面中與一級快取相關的方法
* Session.clear() -- 清空快取。
* Session.evict(Object entity) -- 從一級快取中清除指定的實體物件。
* Session.flush() -- 刷出快取
事務相關的概念
1. 什麼是事務
* 事務就是邏輯上的一組操作,組成事務的各個執行單元,操作要麼全都成功,要麼全都失敗.
* 轉賬的例子:冠希給美美轉錢,扣錢,加錢。兩個操作組成了一個事情!
2. 事務的特性
* 原子性 -- 事務不可分割.
* 一致性 -- 事務執行的前後資料的完整性保持一致.
* 隔離性 -- 一個事務執行的過程中,不應該受到其他的事務的干擾.
* 永續性 -- 事務一旦提交,資料就永久保持到資料庫中.
3. 如果不考慮隔離性:引發一些讀的問題
* 髒讀 -- 一個事務讀到了另一個事務未提交的資料.
* 不可重複讀 -- 一個事務讀到了另一個事務已經提交的update資料,導致多次查詢結果不一致.
* 虛讀 -- 一個事務讀到了另一個事務已經提交的insert資料,導致多次查詢結構不一致.
4. 通過設定資料庫的隔離級別來解決上述讀的問題
* 未提交讀:以上的讀的問題都有可能發生.
* 已提交讀:避免髒讀,但是不可重複讀,虛讀都有可能發生.
* 可重複讀:避免髒讀,不可重複讀.但是虛讀是有可能發生.
* 序列化:以上讀的情況都可以避免.
5. 如果想在Hibernate的框架中來設定隔離級別,需要在hibernate.cfg.xml的配置檔案中通過標籤來配置
* 通過:hibernate.connection.isolation = 4 來配置
* 取值
* 1—Read uncommitted isolation
* 2—Read committed isolation
* 4—Repeatable read isolation
* 8—Serializable isolation
丟失更新的問題
1. 如果不考慮隔離性,也會產生寫入資料的問題,這一類的問題叫丟失更新的問題。
2. 例如:兩個事務同時對某一條記錄做修改,就會引發丟失更新的問題。
* A事務和B事務同時獲取到一條資料,同時再做修改
* 如果A事務修改完成後,提交了事務
* B事務修改完成後,不管是提交還是回滾,如果不做處理,都會對資料產生影響
3. 解決方案有兩種
* 悲觀鎖
* 採用的是資料庫提供的一種鎖機制,如果採用做了這種機制,在SQL語句的後面新增 for update 子句
* 當A事務在操作該條記錄時,會把該條記錄鎖起來,其他事務是不能操作這條記錄的。
* 只有當A事務提交後,鎖釋放了,其他事務才能操作該條記錄
* 樂觀鎖
* 採用版本號的機制來解決的。會給表結構新增一個欄位version=0,預設值是0
* 當A事務在操作完該條記錄,提交事務時,會先檢查版本號,如果發生版本號的值相同時,才可以提交事務。同時會更新版本號version=1.
* 當B事務操作完該條記錄時,提交事務時,會先檢查版本號,如果發現版本不同時,程式會出現錯誤。
4. 使用Hibernate框架解決丟失更新的問題
* 悲觀鎖
* 使用session.get(Customer.class, 1,LockMode.UPGRADE); 方法
* 樂觀鎖
* 1.在對應的JavaBean中新增一個屬性,名稱可以是任意的。例如:private Integer version; 提供get和set方法
* 2.在對映的配置檔案中,提供<version name="version"/>標籤即可。
繫結本地的Session
1. 之前在講JavaWEB的事務的時候,需要在業務層使用Connection來開啟事務,
* 一種是通過引數的方式傳遞下去
* 另一種是把Connection繫結到ThreadLocal物件中
2. 現在的Hibernate框架中,使用session物件開啟事務,所以需要來傳遞session物件,框架提供了ThreadLocal的方式
* 需要在hibernate.cfg.xml的配置檔案中提供配置
* <property name="hibernate.current_session_context_class">thread</property>
* 重新HibernateUtil的工具類,使用SessionFactory的getCurrentSession()方法,獲取當前的Session物件。並且該Session物件不用手動關閉,執行緒結束了,會自動關閉。
public static Session getCurrentSession(){
return factory.getCurrentSession();
}
* 注意:想使用getCurrentSession()方法,必須要先配置才能使用。
1. JavaWEB中一對多的設計及其建表原則
2. 先匯入SQL的建表語句
* 建立今天的資料庫:create database hibernate_day03;
* 在資料中找到客戶和聯絡人的SQL指令碼
3. 編寫客戶和聯絡人的JavaBean程式(注意一對多的編寫規則)
* 客戶的JavaBean如下
public class Customer {
private Long cust_id;
private String cust_name;
private Long cust_user_id;
private Long cust_create_id;
private String cust_source;
private String cust_industry;
private String cust_level;
private String cust_linkman;
private String cust_phone;
private String cust_mobile;
private Set<Linkman> linkmans = new HashSet<Linkman>();
}
* 聯絡人的JavaBean如下
public class Linkman {
private Long lkm_id;
private String lkm_name;
private String lkm_gender;
private String lkm_phone;
private String lkm_mobile;
private String lkm_email;
private String lkm_qq;
private String lkm_position;
private String lkm_memo;
private Customer customer;
}
4. 編寫客戶和聯絡人的對映配置檔案(注意一對多的配置編寫)
* 客戶的對映配置檔案如下
<class name="com .domain.Customer" table="cst_customer">
<id name="cust_id" column="cust_id">
<generator class="native"/>//主鍵增長策略為自動
</id>
<property name="cust_name" column="cust_name"/>
<property name="cust_user_id" column="cust_user_id"/>
<property name="cust_create_id" column="cust_create_id"/>
<property name="cust_source" column="cust_source"/>
<property name="cust_industry" column="cust_industry"/>
<property name="cust_level" column="cust_level"/>
<property name="cust_linkman" column="cust_linkman"/>
<property name="cust_phone" column="cust_phone"/>
<property name="cust_mobile" column="cust_mobile"/>
***
重點 其實一對多主要的配置就是在這裡 一的方面有一個set屬性的linkmans 相當於把這個多存在這個集合裡面 而這時 配置檔案中的配置應該這麼寫
</class>
* 聯絡人的對映配置檔案如下
<class name="com.domain.Linkman" table="cst_linkman">
<id name="lkm_id" column="lkm_id">
<generator class="native"/>
</id>
<property name="lkm_name" column="lkm_name"/>
<property name="lkm_gender" column="lkm_gender"/>
<property name="lkm_phone" column="lkm_phone"/>
<property name="lkm_mobile" column="lkm_mobile"/>
<property name="lkm_email" column="lkm_email"/>
<property name="lkm_qq" column="lkm_qq"/>
<property name="lkm_position" column="lkm_position"/>
<property name="lkm_memo" column="lkm_memo"/>
<many-to-one name="customer" class="com.domain.Customer" column="lkm_cust_id"/>
</class>
同樣 這邊是多的配置
級聯儲存
1.如果現在程式碼只插入其中的一方的資料
* 如果只儲存其中的一方的資料,那麼程式會丟擲異常。
* 如果想完成只儲存一方的資料,並且把相關聯的資料都儲存到資料庫中,那麼需要配置級聯!!
* 級聯儲存是方向性
2. 級聯儲存效果
* 級聯儲存:儲存一方同時可以把關聯的物件也儲存到資料庫中!!
* 使用cascade="save-update"
級聯刪除
1. 先來給大家在資料庫中演示含有外來鍵的刪除客戶功能,那麼SQL語句是會報出錯誤的
* 例如:delete from customers where cid = 1;
2. 如果使用Hibernate框架直接刪除客戶的時候,測試發現是可以刪除的
3. 上述的刪除是普通的刪除,那麼也可以使用級聯刪除,注意:級聯刪除也是有方向性的!!
* <many-to-one cascade="delete" />
級聯的取值(cascade的取值)和孤兒刪除
1. 需要大家掌握的取值如下
* none -- 不使用級聯
* save-update -- 級聯儲存或更新
* delete -- 級聯刪除
* delete-orphan -- 孤兒刪除.(注意:只能應用在一對多關係)
* all -- 除了delete-orphan的所有情況.(包含save-update delete)
* all-delete-orphan -- 包含了delete-orphan的所有情況.(包含save-update delete delete-orphan)
2. 孤兒刪除(孤子刪除),只有在一對多的環境下才有孤兒刪除
* 在一對多的關係中,可以將一的一方認為是父方.將多的一方認為是子方.孤兒刪除:在解除了父子關係的時候.將子方記錄就直接刪除。
* <many-to-one cascade="delete-orphan" />
技術分析之讓某一方放棄外來鍵的維護,為多對多做準備
1. 先測試雙方都維護外來鍵的時候,會產生多餘的SQL語句。
* 想修改客戶和聯絡人的關係,進行雙向關聯,雙方都會維護外來鍵,會產生多餘的SQL語句。
* 產生的原因:session的一級快取中的快照機制,會讓雙方都更新資料庫,產生了多餘的SQL語句。
2. 如果不想產生多餘的SQL語句,那麼需要一方來放棄外來鍵的維護!
* 在<set>標籤上配置一個inverse=”true”.true:放棄.false:不放棄.預設值是false
* <inverse="true">
技術分析之cascade和inverse的區別
1. cascade用來級聯操作(儲存、修改和刪除)
2. inverse用來維護外來鍵的
Hibernate的關聯關係對映之多對多對映
多對多的建表原則
1. JavaWEB的多對多
技術分析之多對多JavaBean的編寫
1. 編寫使用者和角色的JavaBean
* 使用者的JavaBean程式碼如下
public class User {
private Long user_id;
private String user_code;
private String user_name;
private String user_password;
private String user_state;
private Set<Role> roles = new HashSet<Role>();
}
* 角色的JavaBean程式碼如下
public class Role {
private Long role_id;
private String role_name;
private String role_memo;
private Set<User> users = new HashSet<User>();
}
2. 使用者和角色的對映配置檔案如下
* 使用者的對映配置檔案如下
<class name="com.itheima.domain.User" table="sys_user">
<id name="user_id" column="user_id">
<generator class="native"/>
</id>
<property name="user_code" column="user_code"/>
<property name="user_name" column="user_name"/>
<property name="user_password" column="user_password"/>
<property name="user_state" column="user_state"/>
<set name="roles" table="sys_user_role">
<key column="user_id"/>
<many-to-many class="com.domain.Role" column="role_id"/>
</set>
</class>
* 角色的對映配置檔案如下
<class name="com.domain.Role" table="sys_role">
<id name="role_id" column="role_id">
<generator class="native"/>
</id>
<property name="role_name" column="role_name"/>
<property name="role_memo" column="role_memo"/>
<set name="users" table="sys_user_role">
<key column="role_id"/>
<many-to-many class="com.domain.User" column="user_id"/>
</set>
</class>
3. 多對多進行雙向關聯的時候:必須有一方去放棄外來鍵維護權