1. 程式人生 > >hibernate 學習筆記3

hibernate 學習筆記3

1. 多對多關聯:

在雙方都要用一個型別為Set的屬性儲存對方的資訊,並在對映配置檔案中指定這個屬性的名字,並指定中間表。還需要通過<key column=””>來指定自己在中間表中對應的外來鍵。在<many-to-many>標籤中,要配置對方的類,並且指定對方類在中間表中的對應的外來鍵

*學生表配置檔案:

    <class name="edu.whu.swe.lxl.learn.hibernate.model.stucourse.Student" table="student" schema="dbforlearn">

        <id name="id" column="id" >

            <generator class="native"/>

        </id>

        <property name="num" column="num"/>

        <property name="name" column="name"/>

        <!--配置set集合描述類Student中的courses屬性和中間表資訊-->

        <set name="courses" table="stu_course" >

            <!--設定本類在連線表中對應的外來鍵-->

            <key column="student"/>

            <!--配置關聯類以及關聯類在中間表中的外來鍵-->

            <many-to-many class="edu.whu.swe.lxl.learn.hibernate.model.stucourse.Course"

                          column="course"/>

        </set>

</class>

 

*課程表的對映檔案:
    <class name="edu.whu.swe.lxl.learn.hibernate.model.stucourse.Course" table="course" schema="dbforlearn">

        <id name="id" column="id">

            <generator class="native"

        </id>

        <property name="num" column="num"/>

        <property name="name" column="name"/>

        <!--配置關聯類在本類中對應的屬性以及中間表-->

        <set name="students" table="stu_course">

            <!--設定本類在連線表中對應的外來鍵-->

            <key column="course"/>

            <!--配置關聯類以及關聯類在中間表中對應的外來鍵-->

            <many-to-many class="edu.whu.swe.lxl.learn.hibernate.model.stucourse.Student"

                          column="student"/>

        </set>

    </class>

*一般在多對多關係中,不存在級聯刪除的需求。在多對多中,千萬不要加入cascade=delete,否則會是災難性的。

 

2.查詢

1)唯一標識OID的檢索方式(重要)

Session.get(obj.class,OID)

2) 物件的導航方式(重要)

一對多查詢時,利用裡面包含的set

3)HQL的檢索方式(重要)

Hibernate Query Language  ——hibernate的查詢語言

4)QBC的檢索方式(重要)

Query By Criteria  ——條件查詢

5)SQL查詢

 

3.HQL查詢

* HQL是面向java物件的,和SQL有相似之處

*是hibernate中使用最廣泛的檢索方式

*hibernate負責解析HQL,然後根據ORM配置的對映關係,生成相應的SQL語句

*HQL操作的是類以及屬性,而不是資料表和欄位

 

 

4.延遲載入技術——在class標籤上配置

1)延遲載入先獲取到代理物件,當真正使用到該物件中的屬性且快取裡沒有值時,才會傳送SQL語句,是hibernate框架提升效能的主要方式。

 

2)類級別的延遲載入

*Session物件的load方法預設就是延遲載入

*Customer c=session.load(Customer.class,1L),沒有傳送SQL語句,當使用該物件的屬性時,才傳送SQL語句

【例項】

    /**

       <class name="edu.whu.swe.lxl.learn.hibernate.model.Customer" table="customer" >

        <id name="id" column="id">

            <generator class="identity"/>

        </id>

        <property name="name" column="name"/>

        <property name="address" column="address"/>

        <property name="age" column="age"/>

        <property name="registerDate" column="register_date"/>

    </class>     */

   public void testLoad(){

        Customer customer = session.load(Customer.class, 3);

        log.trace(customer.getId().toString());//沒有傳送SQL,因為ID已經由程式碼提供了,此時開始變成持久態

        log.trace("+++++++++++++++++");

        log.trace(customer.getName());//傳送SQL語句,因為此時快取裡沒有name資訊,必須要傳送SQL從資料庫中查詢

    }

 

*使類級別的延遲載入失效:

    **在<class>標籤上配置lazy=”false”

    **Hibernate.initialize(Object proxy)

【例項】

/**

     <class name="edu.whu.swe.lxl.learn.hibernate.model.Customer" table="customer" lazy="false">

     <id name="id" column="id">

     <generator class="identity"/>

     </id>

     <property name="name" column="name"/>

     <property name="address" column="address"/>

     <property name="age" column="age"/>

     <property name="registerDate" column="register_date"/>

     </class>

     */

    @Test

    public void testLoadNotLazy() {

        Customer customer = session.load(Customer.class, 3);

        log.trace(customer.getId().toString());//沒有傳送SQL,因為ID已經由程式碼提供了,此時開始變成持久態

        log.trace("+++++++++++++++++");

        log.trace(customer.getName());//傳送SQL語句,因為此時快取裡沒有name資訊,必須要傳送SQL從資料庫中查詢

    }

3)關聯級別的延遲載入。

* CustCustomer customer=session.get(CustCustomer.class,1L)不是延遲載入,但是它不會把代理類CustCustomer的關聯類CstLinkman載入進來,當使用CstLinkman linkmans=customer.getLinkmans()獲取關聯類物件的時候,因為沒有用到CstLinkman的任何屬性,也不需要知道有幾個linkman,所以還是不會載入。直到使用了linkmans裡的屬性時,才會傳送SQL載入關聯類物件。

【例項】

        /**

         * <set name="linkMans" cascade="delete-orphan" inverse="true">

         <!--外來鍵-->

         <key column="lkm_cust_id"/>

         <!--對應關係-->

         <one-to-many class="edu.whu.swe.lxl.learn.hibernate.model.CstLinkman" />

         </set>

         */

        CustCustomer customer = session.get(CustCustomer.class, 19L);//直接傳送SQL查詢CustCustomer,但不查詢CstLinkman

        log.trace(customer.getCustId().toString());//

        log.trace("++++++++++++++++++++++++++");

        Set linkmans=customer.getLinkMans();//還是不傳送SQL,因為還沒有必要查詢CstLinkman的屬性

        log.trace("++++++++++++++++++++++++++");

        log.trace(""+linkmans.size());//這個時候傳送SQL,因為不傳送,就不知道有幾個linkman了。

 

5.延遲載入技術——在set標籤上配置策略

1)在<set>標籤上使用fetch和lazy屬性

*fetch的取值       ——控制SQL語句的生成格式

    ** select       ——預設值,傳送查詢語句

    **join            ——連線查詢,傳送的是一條左外連結查詢,此時,關聯表延遲載入失效(因為不需要再發起一次SQL查詢關聯表了,而是在一次外連結查詢中完成了所有表的載入)

    **subselect    ——子查詢,傳送一條子查詢完成關聯物件的查詢。(需要使用list()方法進行測試,如Criteria.list()或者Query.list())

 

 

*lazy的取值        ——查詢關聯物件的時候是否採用延遲載入

    **true         ——預設,延遲

    **false         ——不延遲

    **extra         ——極其懶惰,根據需要查詢,而不是查詢所有欄位,比如用一寫聚合函式,或者只查詢某一個欄位

一般都採用預設值即可

 

 

【示例】

*fetch=”join”

public void testJoinSet(){

        /**

         <set name="linkMans" cascade="delete-orphan" inverse="true" fetch="join>

         <!--外來鍵-->

         <key column="lkm_cust_id"/>

         <!--對應關係-->

         <one-to-many class="edu.whu.swe.lxl.learn.hibernate.model.CstLinkman" />

         </set>

         */

        CustCustomer customer = session.get(CustCustomer.class, 19L);//直接傳送join SQL 左外連線查詢,一次性獲取所有的資料

        log.trace(customer.getCustId().toString());

        log.trace("++++++++++++++++++++++++++");

 

    }

 

 

*fetch=”select” lazy=”extra”

    @Test

    public void testExtraLazy() {

        /**

         <set name="linkMans" cascade="delete-orphan" inverse="true" fetch="select" lazy="extra">

         <!--外來鍵-->

         <key column="lkm_cust_id"/>

         <!--對應關係-->

         <one-to-many class="edu.whu.swe.lxl.learn.hibernate.model.CstLinkman" />

         </set>

         */

        CustCustomer customer = session.get(CustCustomer.class, 19L);//直接傳送SQL查詢CustCustomer,但不查詢CstLinkman

        log.trace(customer.getCustId().toString());//

        log.trace("++++++++++++++++++++++++++");

        Set linkmans = customer.getLinkMans();//還是不傳送SQL,因為還沒有必要查詢CstLinkman的屬性

        log.trace("++++++++++++++++++++++++++");

        log.trace("" + linkmans.size());//傳送select count(lkm_id) form cst_linkman where lkm_cust_id=?,而不是查詢所有欄位

 

    }

 

*fatch=”subselect”

    public void testSubselectSet() {

        /**

         <set name="linkMans" cascade="delete-orphan" inverse="true" fetch="subselect">

         <!--外來鍵-->

         <key column="lkm_cust_id"/>

         <!--對應關係-->

         <one-to-many class="edu.whu.swe.lxl.learn.hibernate.model.CstLinkman" />

         </set>

         */

        Query query = session.createQuery("from CustCustomer ");

        List<CustCustomer> customers = query.list();//傳送SQL查詢CustCustomer,但不查詢CstLinkman

        log.trace("++++++++++++++++++++++++++++++++++");

        for(CustCustomer customer:customers){

            Set linkmans

                    = customer.getLinkMans();//不傳送SQL

            log.trace("++++++++++++++++++++++++++++++++++");

 

            log.trace(""+linkmans.size());//在第一次迴圈時候,就傳送子查詢把List所有中所有使用者的所有聯絡人都一次性查詢出來,下次迴圈就不用傳送SQL了。如果不適用subselect,那麼迴圈一次,就傳送一次SQL查詢該客戶的聯絡人

        }

 

    }

 

6.優化技術——在<many-to-one>標籤中配置策略

1)在<many-to-one>標籤上使用fetch和lazy屬性

  *fetch的取值                            ——控制SQL語句的格式

      **select                   ——預設,傳送基本的select語句查詢

      **join                            ——無效,所以fetch只能是select,所以不用設定fetch

  *lazy的取值

      **false                                   ——不採用延遲載入

           **proxy                         ——預設值,是否延遲載入取決於一方set 標籤的lazy取值,多方和一方的lazy值一樣。

 

7.當fetch=select,lazy=true的時候,使用list時,對從表的查詢是一次迴圈獲取進行一次的,此時如果不想設定subselect進行子查詢,那麼可以設定set標籤的batch-size=10,從而達到批量獲取從表資料的目的。當設定batch-size=10時,hibernate會一次就取10個客戶的聯絡人。具體工作原理這樣的:

1)先獲取所有的客戶

2)再用select xxx xxx xxx from cst_linkman where cst_linkman.lkm_cust_id in( 10個客戶的id  )批量獲取10個客戶的聯絡人。

 

總結:session.load採取的是類級別的延遲載入,並且在class標籤上配置當前類的延遲載入策略;session.get採取的是連線上的延遲載入,並且在set標籤上配置載入策略,在many-to-one上也可以進行配置。

 

8.延遲載入策略的優缺點及適用範圍:
1)有點

有應用程式決定要載入哪些物件,可以避免執行多餘的SQL語句,避免載入不程式不會訪問的物件,提高效能和空間利用率。

2)缺點

應用程式如果試圖訪問處於遊離態的代理類例項,必須保證它在持久化狀態是已經被初始化了。而延遲載入策略往往導致代理類例項相應的set沒有值,從而需要把遊離態物件通過session.update方法變成持久態,再去訪問set中的物件從而載入物件。

3)適用範圍

——一對多或者多對多

——應用程式不需要立即訪問或者根本不需要訪問的物件。

 

9.在配置檔案中配置的載入策略影響全域性,但是應用程式的需求可能是多樣的,在不同的時候需要不同的載入策略,因此,hibernate提供了在應用程式中指定載入策略的方式,應用程式中指定的載入方式將覆蓋配置檔案中的載入方式(區域性優先於整體原則)

 

10.一對一的載入預設是使用左外連線查詢。