Hibernate框架(3) - 對象狀態 / 一級緩存 / 映射

對象的狀態

  • Hibernate中對象的狀態 : 臨時/瞬時狀態、持久化狀態、游離狀態
    • 臨時狀態
      • 特點:
        • 直接new出來的對象;
        • 不處于session的管理;
        • 數據庫中沒有對象的記錄;
    • 持久化狀態 : 當調用session的save/saveOrUpdate/get/load/list等方法的時候,對象就是持久化狀態。處于持久化狀態的對象,當對對象屬性進行更改的時候,會反映到數據庫中!
      • 特點:
        • 處于session的管理;
        • 數據庫中有對應的記錄;
    • 游離狀態
      • 特點
        • 不處于session的管理
        • 數據庫中有對應的記錄
        • Session關閉后,對象的狀態
  • 對象狀態的轉換 :
private static SessionFactory sf;
    static {
        sf = new Configuration()
            .configure()
            .addClass(User.class)   // 測試時候使用
            .buildSessionFactory();
    }
    //1. 對象狀態的轉換
    @Test
    public void testSaveSet() throws Exception {
        Session session = sf.openSession();
        session.beginTransaction();
        
        // 創建對象                     【臨時狀態】
//      User user = new User();
//      user.setUserName("Jack22222");
        // 保存                           【持久化狀態】
//      session.save(user);     
//      user.setUserName("Jack333333");  // 會反映到數據庫
        
        // 查詢
        User user = (User) session.get(User.class, 5);
        user.setUserName("Tomcat");// hibernate會自動與數據庫匹配(一級緩存),如果一樣就更新數據庫
        
        session.getTransaction().commit();
        session.close();
    
        user.setUserName("Jack444444444");
        // 打印                           【游離狀態】
        System.out.println(user.getUserId());
        System.out.println(user.getUserName());
    }

一級緩存

  • 為什么要用緩存?

    • 目的:減少對數據庫的訪問次數!從而提升hibernate的執行效率!
  • Hibernate中緩存分類 :

    • 一級緩存
    • 二級緩存
  • 一級緩存概念 :
    1)Hibenate中一級緩存,也叫做session的緩存,它可以在session范圍內減少數據庫的訪問次數! 只在session范圍有效! Session關閉,一級緩存失效!
    2)當調用session的save/saveOrUpdate/get/load/list/iterator方法的時候,都會把對象放入session的緩存中。
    3)Session的緩存由hibernate維護, 用戶不能操作緩存內容; 如果想操作緩存內容,必須通過hibernate提供的evit/clear方法操作。

  • 特點:

    • 只在(當前)session范圍有效,作用時間短,效果不是特別明顯!
    • 在短時間內多次操作數據庫,效果比較明顯!
  • 緩存相關幾個方法的作用

    • session.flush() : 讓一級緩存與數據庫同步
    • session.evict(arg0) : 清空一級緩存中指定的對象
    • session.clear() : 清空一級緩存中緩存的所有對象
  • 在什么情況用上面方法?

    • 批量操作使用使用:
      • Session.flush(); // 先與數據庫同步
      • Session.clear(); // 再清空一級緩存內容
  • 查詢方法:

    • list : 會放入緩存,但不會從緩存中獲取數據
private static SessionFactory sf;
    static {
        sf = new Configuration()
            .configure()
            .addClass(User.class)   // 測試時候使用
            .buildSessionFactory();
    }
@Test
    public void list() throws Exception {
        Session session = sf.openSession();
        session.beginTransaction();
        // HQL查詢
        Query q = session.createQuery("from User ");
        // list()方法
        List<User> list = q.list();
        
        for (int i=0; i<list.size(); i++){
            System.out.println(list.get(i));
        }
        
        session.getTransaction().commit();  
        session.close();
    }
  • iterator : 會放入緩存,也會從緩存中獲取數據
private static SessionFactory sf;
    static {
        sf = new Configuration()
            .configure()
            .addClass(User.class)   // 測試時候使用
            .buildSessionFactory();
    }
@Test
    public void iterator() throws Exception {
        Session session = sf.openSession();
        session.beginTransaction();
        // HQL查詢
        Query q = session.createQuery("from User ");
        // iterator()方法
        Iterator<User> it = q.iterate();
        while(it.hasNext()){
            // 得到當前迭代的每一個對象
            User user = it.next();
            System.out.println(user);
        }

        session.getTransaction().commit();  
        session.close();
    }

懶加載(lazy)

  • 概念:當用到數據的時候才向數據庫查詢,這就是hibernate的懶加載特性。
  • 目的:提供程序執行效率!
  • lazy 值
    • true : 使用懶加載
    • false : 關閉懶加載
    • extra : 在集合數據懶加載時候提升效率
      • 在真正使用數據的時候才向數據庫發送查詢的sql;如果調用集合的size() / isEmpty()方法,只是統計,不真正查詢數據!
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="cn.itcast.b_one2Many">
    <class name="Dept" table="t_dept" >
        <id name="deptId">
            <generator class="native"></generator>
        </id>   
        <property name="deptName" length="20"></property>
        
        <!-- 
            集合屬性,默認使用懶加載 
            lazy
                true 懶加載
                extra 懶加載(智能)
                false 關閉懶加載
        
        -->
         <set name="emps" lazy="extra">
             <key column="dept_id"></key>
             <one-to-many class="Employee"/>
         </set>
    </class>
</hibernate-mapping>
//1. 集合的查詢()
    @Test
    public void set() {
        Session session = sf.openSession();
        session.beginTransaction();
        Dept dept = (Dept) session.get(Dept.class, 10);
        System.out.println(dept.getDeptName());
        System.out.println("------");
        System.out.println(dept.getEmps().isEmpty());  //  SQL
        
        session.getTransaction().commit();
        session.close();    
    }
  • 懶加載異常
    • Session關閉后,不能使用懶加載數據; 如果session關閉后,使用懶加載數據報錯:
      org.hibernate.LazyInitializationException: could not initialize proxy - no Session
    • 如何解決session關閉后不能使用懶加載數據的問題?
      • 方式1: 先使用一下數據 : dept.getDeptName();
      • 方式2:強迫代理對象初始化 : Hibernate.initialize(dept);
      • 方式3:關閉懶加載, 設置lazy=false;
      • 方式4: 在使用數據之后,再關閉session!
  • get與load方法:
private static SessionFactory sf;
    static {
        sf = new Configuration()
            .configure()
            .addClass(Dept.class)   
            .addClass(Employee.class)   // 測試時候使用
            .buildSessionFactory();
    }
    //1. 主鍵查詢,及區別
    @Test
    public void get_load() {
        
        Session session = sf.openSession();
        session.beginTransaction();
        Dept dept = new Dept();
        // get: 及時查詢
//      dept = (Dept) session.get(Dept.class, 9);
//      System.out.println(dept.getDeptName());
        
        // load,默認懶加載, 及在使用數據的時候,才向數據庫發送查詢的sql語句!
        dept = (Dept)session.load(Dept.class, 9);
        // 方式1: 先使用一下數據
        //dept.getDeptName();
        // 方式2:強迫代理對象初始化
        Hibernate.initialize(dept);
        // 方式3:關閉懶加載
        
        session.getTransaction().commit();
        session.close();
        
        // 在這里使用
        System.out.println(dept.getDeptName());
    }

一對一映射

  • 基于外鍵的映射
    • IdCard.hbm.xml :
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="cn.itcast.c_one2one">
    <class name="IdCard" table="t_IdCard">
        <id name="cardNum">
            <generator class="assigned"></generator>
        </id>   
        <property name="place" length="20"></property>
        
        <!-- 
            一對一映射,有外鍵的類
            unique="true"   給外鍵字段添加唯一約束
         -->
         <many-to-one name="user" unique="true" column="user_id" class="User" cascade="save-update"></many-to-one>
    </class>
</hibernate-mapping>
  • User.hbm.xml :
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="cn.itcast.c_one2one">
    <class name="User" table="t_user">
        <id name="userId">
            <generator class="native"></generator>
        </id>   
        <property name="userName" length="20"></property>
        <!-- 
            一對一映射: 沒有外鍵的類
         -->
         <one-to-one name="idCard" class="IdCard"></one-to-one> 
    </class>
</hibernate-mapping>
  • java類
private static SessionFactory sf;
    static {
        sf = new Configuration()
            .configure()
            .addClass(IdCard.class)   
            .addClass(User.class)   // 測試時候使用
            .buildSessionFactory();
    }

    @Test
    public void getSave() {
        
        Session session = sf.openSession();
        session.beginTransaction();
        
        // 用戶
        User user = new User();
        user.setUserName("Jack");
        // 身份證
        IdCard idCard = new IdCard();
        idCard.setCardNum("441202XXX");
        idCard.setPlace("廣州XXX");
        // 關系
        idCard.setUser(user);
        
        // ----保存----
        session.save(idCard);
        
        session.getTransaction().commit();
        session.close();    
    }
  • 基于主鍵的映射
    • IdCard.hbm.xml :
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="cn.itcast.c_one2one2">
    <class name="IdCard" table="t_IdCard">
        <id name="user_id">
            <!-- 
                id 節點指定的是主鍵映射, 即user_id是主鍵
                主鍵生成方式: foreign  即把別的表的主鍵作為當前表的主鍵;
                        property (關鍵字不能修改)指定引用的對象     對象的全名 cn..User、  對象映射 cn.User.hbm.xml、   table(id)
             -->
            <generator class="foreign">
                <param name="property">user</param>
            </generator>
        </id>   
        <property name="cardNum" length="20"></property>
        <property name="place" length="20"></property>
        
        <!-- 
            一對一映射,有外鍵方
            (基于主鍵的映射)
             constrained="true"  指定在主鍵上添加外鍵約束
         -->
        <one-to-one name="user" class="User" constrained="true"  cascade="save-update"></one-to-one>
            
    </class>
</hibernate-mapping>
  • User.hbm.xml :
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="cn.itcast.c_one2one">
    <class name="User" table="t_user">
        <id name="userId">
            <generator class="native"></generator>
        </id>   
        <property name="userName" length="20"></property>
        <!-- 
            一對一映射: 沒有外鍵的類
         -->
         <one-to-one name="idCard" class="IdCard"></one-to-one> 
    </class>
</hibernate-mapping>
  • java類
private static SessionFactory sf;
    static {
        sf = new Configuration()
            .configure()
            .addClass(IdCard.class)   
            .addClass(User.class)   // 測試時候使用
            .buildSessionFactory();
    }
    @Test
    public void getSave() {
        
        Session session = sf.openSession();
        session.beginTransaction();
        
        // 用戶
        User user = new User();
        user.setUserName("Jack");
        // 身份證
        IdCard idCard = new IdCard();
        idCard.setCardNum("441202XXX");
        idCard.setPlace("廣州XXX");
        // 關系
        idCard.setUser(user);
        
        // ----保存----
        session.save(idCard);
        
        session.getTransaction().commit();
        session.close();    
    }

組件映射與繼承映射

  • 類的關系

    • 組合關系 : 一個類中包含了另外一個類, 這兩個類就是組合關系 (汽車與車輪)
    • 繼承關系 : 一個類繼承另外一個類, 這兩個類就是繼承關系
  • 組件映射 : 類組合關系的映射, 也叫做組件映射 (組件類和被包含的組件類,共同映射到一張表)

    • javaBean
public class Car {
          private int id;
          private String name;
          // 車輪
          private Wheel wheel;
}
public class Wheel {
          private int count;
          private int size;
}
  • Car.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<!-- 
    組件映射
 -->
<hibernate-mapping package="cn.itcast.d_component">
    <class name="Car" table="t_car">
        <id name="id">
            <generator class="native"></generator>
        </id>   
        <property name="name" length="20"></property>
        
        <!-- 組件映射 -->
        <component name="wheel">
            <property name="size"></property>
            <property name="count"></property>
        </component>         
    </class>
</hibernate-mapping>
  • java
private static SessionFactory sf;
    static {
        sf = new Configuration()
            .configure()
            .addClass(Car.class)   
            .buildSessionFactory();
    }
    @Test
    public void getSave() {
        
        Session session = sf.openSession();
        session.beginTransaction();
        
        // 輪子
        Wheel wheel = new Wheel();
        wheel.setSize(38);
        wheel.setCount(4);
        // 汽車
        Car car = new Car();
        car.setName("BMW");
        car.setWheel(wheel);
        
        // 保存
        session.save(car);
        
        session.getTransaction().commit();
        session.close();    
    }
  • 繼承映射
    • 簡單繼承映射 : 有多少個子類,寫多少個映射文件!
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<!-- 
    簡單繼承
 -->
<hibernate-mapping package="cn.itcast.e_extends1">
    <class name="Cat" table="t_Cat">
        <!-- 簡單繼承映射: 父類屬性直接寫 -->
        <id name="id">
            <generator class="native"></generator>
        </id>
        <property name="name"></property>
        
        <property name="catchMouse"></property>                  
    </class>
</hibernate-mapping>
- 總結:寫法較為簡單, 所有子類用一個映射文件, 且映射到一張表; 但數據庫設計不合理!(不推薦用)
  • 一個映射文件存儲所有子類, 子類父類都對應表(所有子類映射到一張表)
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<!-- 繼承映射, 所有的子類都映射到一張表 -->
<hibernate-mapping package="cn.itcast.e_extends2">
    <class name="Animal" table="t_animal">
        <id name="id">
            <generator class="native"></generator>
        </id>
        <!-- 指定鑒別器字段(區分不同的子類) -->
        <discriminator column="type_"></discriminator>
        
        <property name="name"></property>
        
        <!-- 
            子類:貓
                每個子類都用subclass節點映射
                注意:一定要指定鑒別器字段,否則報錯!
                鑒別器字段:作用是在數據庫中區別每一個子類的信息, 就是一個列
            discriminator-value="cat_"
                指定鑒別器字段,即type_字段的值
                如果不指定,默認為當前子類的全名
         -->
         <subclass name="Cat" discriminator-value="cat_">
            <property name="catchMouse"></property>
         </subclass>
         
         <!-- 
            子類:猴子
          -->
          <subclass name="Monkey" discriminator-value="monkey_">
            <property name="eatBanana"></property>
          </subclass>
    </class>
</hibernate-mapping>
- 總結 : 寫法較為簡單, 所有子類用一個映射文件, 且映射到一張表!但數據庫設計不合理(不推薦用)
  • 每個類都映射一張表
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<!-- 繼承映射, 每個類對應一張表(父類也對應表) -->
<hibernate-mapping package="cn.itcast.e_extends3">
    <class name="Animal" table="t_animal">
        <id name="id">
            <generator class="native"></generator>
        </id>
        <property name="name"></property>
        
        <!-- 
            子類:貓  t_cat
            key 指定_cat表的外鍵字段
        -->
        <joined-subclass name="Cat" table="t_cat">
            <key column="t_animal_id"></key>
            <property name="catchMouse"></property>
        </joined-subclass>
        
        <!-- 子類:猴子  t_monkey -->
        <joined-subclass name="Monkey" table="t_monkey">
            <key column="t_animal_id"></key>
            <property name="eatBanana"></property>
        </joined-subclass>
    </class>
</hibernate-mapping>
- 總結 : 一個映射文件,存儲所有的子類; 子類父類都對應表;
- 缺點:表結構比較復雜,插入一條子類信息,需要用2條sql(子類, 父類都需要插入數據)
  • 推薦 : 每個子類映射一張表, 父類不對應表
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<!-- 繼承映射, 每個類對應一張表(父類不對應表) -->
<hibernate-mapping package="cn.itcast.e_extends4">
    <!-- abstract="true"  指定實體類對象不對應表,即在數據庫段不生成表 -->
    <class name="Animal" abstract="true">
        <!-- 如果用union-subclass節點,主鍵生成策略不能為自增長! -->
        <id name="id">
            <generator class="uuid"></generator>
        </id>
        <property name="name"></property>
        
        <!-- 
            子類:貓  t_cat
            union-subclass  
                table 指定為表名, 表的主鍵即為id列
        -->
        <union-subclass name="Cat" table="t_cat">
            <property name="catchMouse"></property>
        </union-subclass>
        
        <!-- 子類:猴子  t_monkey -->
        <union-subclass name="Monkey" table="t_monkey">
            <property name="eatBanana"></property>
        </union-subclass>
    </class>
</hibernate-mapping>
- 注意:主鍵不能是自增長!

Hibernate中映射分類:

  • 多對一
  • 一對多
  • 多對多
  • 一對一 (多對一的特殊應用)
  • 組件
  • 繼承
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,837評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,196評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,688評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,654評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,456評論 6 406
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,955評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,044評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,195評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,725評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,608評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,802評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,318評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,048評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,422評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,673評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,424評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,762評論 2 372

推薦閱讀更多精彩內容