1. 程式人生 > >ssh框架hibernate 2持久化物件和關係關聯對映

ssh框架hibernate 2持久化物件和關係關聯對映

什麼是持久化類

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. 多對多進行雙向關聯的時候:必須有一方去放棄外來鍵維護權