1. 程式人生 > >Hibernate知識點複習之四

Hibernate知識點複習之四

Hibernate知識點複習之四

Hibernate的檢索

檢索方式分類:物件圖導航檢索方式,OID檢索方式,HQL檢索方式,QBC檢索方式,SQL檢索方式

1 物件圖導航檢索方式:

根據已載入的物件,導航到他的關聯物件,它是利用類與類的關係來檢索物件,如要查詢一個聯絡人對應的客戶,
就可以由聯絡人物件自動導航找到聯絡人所屬的客戶物件,前提是必須在對映檔案中配置多對一的關係,其檢索方式如下:
LinkMan linkMan=(LinkMan)session.get(Customer.class,1L); 
Customer customer=linkMan.getCustomer();

2 OID檢索方式

概述:OID檢索方式主要指用Session的get()和load()方法載入某條記錄對應的物件,方式如下:
        Customer customer1 = session.get(Customer.class,3L);
        Customer customer2 = session.load(Customer.class,4L);
底層深入分析get()和load()的區別:

(1)get不支援lazyd(懶載入/延遲載入),load支援lazy
    lazy表示只有在用到的時候才載入資料,
    如:Student student = (Student)session.load(Student.class,1);  //不會發出SQL語句
          student.getName();   //這條語句才會發出SQL語句
     而使用get方法,Student student = (Student)session.get(Student.class,1);  //會發出SQL語句
(2) 採用get載入資料,如果資料庫中不存在相應的資料,那麼返回null;
    採用load載入資料,如果資料庫中不存在相應的資料,那麼丟擲ObjectNotFoundException
 (3)load方法可返回實體的代理類例項,而get方法永遠直接返回實體類。
 (4) load方法可以充分利用內部快取和二級快取中的現有資料
     get方法則僅僅在內部快取中進行資料查詢,如沒有發現對應資料,將越過二級快取,直接呼叫SQL完成資料讀取。
Session在載入實體物件時,將經過的過程:
    首先,Hibernate中維持了兩級快取。第一級快取由Session例項維護,其中保持了Session當前所有關聯實體的資料,也稱為內部快取。
            而第二級快取則存在於SessionFactory層次,由當前所有由本SessionFactory構造的Session例項共享。
            出於效能考慮,避免無謂的資料庫訪問,Session在呼叫資料庫查詢功能之前,會先在快取中進行查詢。
            首先在第一級快取中,通過實體型別和id進行查詢,如果第一級快取查詢命中,且資料狀態合法,則直接返回。
            之後,Session會在當前“NonExists”記錄中進行查詢,如果“NonExists”記錄中存在同樣的查詢條件,則返回null。
         “NonExists”記錄了當前Session例項在之前所有查詢操作中,未能查詢到有效資料的查詢條件(相當於一個查詢黑名單列表)。
            如此一來,如果Session中一個無效的查詢條件重複出現,即可迅速作出判斷,從而獲得最佳的效能表現。
        對於load方法而言
            如果內部快取中未發現有效資料,則查詢第二級快取,如果第二級快取命中,則返回。
            如在快取中未發現有效資料,則發起資料庫查詢操作(Select SQL),如經過查詢未發現對應記錄,
            則將此次查詢的資訊在“NonExists”中加以記錄,並返回null。
            根據對映配置和Select SQL得到的ResultSet,建立對應的資料物件。
            將其資料物件納入當前Session實體管理容器(一級快取)。
            執行Interceptor.onLoad方法(如果有對應的Interceptor)。
            將資料物件納入二級快取。
            如果資料物件實現了LifeCycle介面,則呼叫資料物件的onLoad方法。
            返回資料物件。

3 HQL檢索方式(Hibernate官方推薦的查詢語言):

概述:HQL是面對物件的查詢語言,它和SQL查詢語言相似,但它使用的是類,物件和屬性概念,沒有表和欄位概念。
功能:
         * 在查詢語句中設定查詢條件
          * 支援動態繫結引數
          * 支援分組查詢,允許使用group by和having關鍵字
         * 提供內建的聚合函式,如sum()和min(),max()
          * 支援子查詢,即巢狀查詢
         * 支援分頁查詢
         * 支援投影查詢,即僅檢索出物件的部分屬性
         * 能夠呼叫使用者定義的sql函式
完整的hql語句結構:
    select ...from...where...group by ...having... order by ...asc/desc
HQl檢索API示例:
    1 hql基本檢索
        String hql="from Customer";
        //String hql="select * from Customer";此句Hibernate無法識別*萬用字元
        //String hql="select c from Customer c;使用select 需要為Customer起別名
        Query query = session.createQuery(hql);
        List<Customer> list = query.list();
    2 排序檢索
        String hql="from Customer order by cust_id desc";
        Query query = session.createQuery(hql);
        List<Customer> list = query.list();
    3 條件查詢:
        按位置繫結引數
        String hql1="from Customer where cust_id = ?";
        Query query1 = session.createQuery(hql1);
        query1.setParameter(0,9L);
        Customer c1 = (Customer) query1.uniqueResult();
        按名稱繫結引數
        String hql2="from Customer where cust_id =:myId";
        Query query2 = session.createQuery(hql2);
        query2.setParameter("myId",8L);
        Customer c2 = (Customer) query2.uniqueResult();
    4 分頁檢索
        String hql="from Customer";
        Query query = session.createQuery(hql);
        //設定分頁
        query.setFirstResult(0);
        query.setMaxResults(5);
        List<Customer> list = query.list();
    5 統計檢索:count()總記錄,sum()算術總和,avg()算術平均值,min()算術最小值,max()算術最大值
        String hql="select count(*) from Customer";
        String hql3="select sum(cust_id) from Customer";
        String hql4="select avg(cust_id) from Customer";
        String hql5="select max(cust_id) from Customer";
        String hql6="select min(cust_id) from Customer";
    6 投影查詢--也就是查詢物件的某一個屬性
        查詢單個屬性
        String hql="select cust_name from cn.itheima.domain.Customer"; //所有Customer的cust_name屬性
        查詢多個屬性
        String hql2="select cust_name,cust_id from cn.itheima.domain.Customer";
    7 投影的構造方式查詢:
        結果識別為Customer實體的屬性
        但前提是:Customer實體中一定要存在 相應的構造方法(空參和實參都要有)
        String hql3="select new Customer(cust_id,cust_name) from cn.itheima.domain.Customer";

4 QBC檢索:

    1 概述:QBC(Query By Criteria)它由Criteria介面,Criterion介面,Expression類組成。
        Criteria介面是HibernateAPI的一個查詢介面,它由Session進行建立。
        Criterion是Criteria的查詢條件,在Criteria中提供add(Criterion criterion)方法來新增查詢條件
        而Criterion查詢條件的建立是通過Restrictions工具類的靜態方法:
                Restrictions.eq                     等於
                Restrictions.allEq              物件是Map,使用key/value進行多個等於比較
                Restrictions.gt                 大於>
                Restrictions.ge                 大於等於>=
                Restrictions.lt                 小於
                Restrictions.le                 小於等於
                Restrictions.between            對應sql的between子句
                Restrictions.like                   對應sql的like子句
                Restrictions.in                 對應sql的in子句
                Restrictions.and                and關係
                Restrictions.or                 or關係
                Restrictions.sqlRestriction  sql限定查詢
    2 QBC檢索API示例:
        (1)QBC基本查詢
                //建立Criteria查詢物件
                Criteria criteria = session.createCriteria(Customer.class);//相當於select * from customer
                //獲取查詢結果
                List list = criteria.list();
        (2)Criteria條件查詢
                //建立Criteria查詢物件
                Criteria criteria = session.createCriteria(Customer.class);
                //設定查詢條件
                //Criterion criterion2 = Restrictions.idEq(2l);
                Criterion criterion = Restrictions.eq("cust_id",2l);
                //把查詢條件criterion新增進Criteria物件中
                criteria.add(criterion);
                //獲取查詢結果
                List list = criteria.list();
        (3)Criteria的分頁查詢
                建立Criteria查詢物件
                Criteria criteria = session.createCriteria(Customer.class);
                設定分頁引數
                criteria.setFirstResult(0);
                criteria.setMaxResults(1);
                List list = criteria.list();
        (4)Criteria的排序查詢
                獲取Criteria查詢物件
                Criteria criteria = session.createCriteria(Customer.class);
                排序
                //criteria.addOrder(Order.asc("cust_id")); //升序
                criteria.addOrder(Order.desc("cust_id")); //降序
                List list = criteria.list();
        (5)criteria的統計
            criteria.setProjection(Projections.rowCount());  
            //criteria.setProjection(Projections.max("cust_id")); 
            //criteria.setProjection(Projections.min("cust_id"));
            //criteria.setProjection(Projections.avg("cust_id"));
            //criteria.setProjection(Projections.sum("cust_id"));
            List list = criteria.list();

    3 離線的QBC條件查詢DetachedCriteria(SSH整合後經常用)
        概述:DetachedCriteria是一種脫離Session來使用的條件查詢物件(傳統的Criteria物件必須由Session建立)
                  其不受限於session,易於在不同架構層中封裝資料進行傳遞
        API:
                離線DetachedCriteria的建立
                    DetachedCriteria dc=DetachedCriteria.forClass(Xxx.class);
                離線DetachedCriteria轉換為正常Criteria,接收Session
                Criteria criteria =dc.getExecutableCriteria(session);
        示例:
                模擬此處在service/web層
                //建立離線DetachedCriteria
                DetachedCriteria dtCriteria = DetachedCriteria.forClass(Customer.class);
                //進行條件 id查詢(和普通的查詢方式一樣)
                dtCriteria.add(Restrictions.idEq(2l));

                模擬此處在dao層
                //獲取session物件
                Session session = HibernateUtils.openSession();
                //開啟事務
                Transaction tx = session.beginTransaction();
                //操作-------------------------
                //離線DetachedCriteria傳遞到  dao層,接收session變成正常的 Criteria查詢物件
                Criteria criteria = dtCriteria.getExecutableCriteria(session);
                List list = criteria.list();
                //事務提交,資源釋放------------------------
                tx.commit();
                session.close();

5 Hibernate本地SQL檢索方式:

    概述:採用HQL或QBC檢索方式時,Hiberante底層會生成標準的SQL查詢語句,適用於所有資料庫平臺,它們是跨平臺的。
              但有的應用程式需根據底層資料庫sql方言來生成一些特殊查詢語句,這就需用到Hibernate提供的SQL檢索方式。
    本地SQL檢索的示例程式碼:
            SQLQuery sqlQuery=session.createSQLQuery("xxxx");

6 Hibernate的多表查詢

(1)原生的SQL多表查詢:
    & 連線查詢:
        <1>交叉連線(開發一般不使用)
                概述:返回的結果是被連線的兩個表中所有資料行的笛卡爾積,如A表有10條資料,B表有8條資料,返回的結果就是10*8。
                語法格式
                        格式一:select * from 表1 cross join 表2;
                        格式二:select * from 表1,表2;
        <2>內連線(又稱簡單連線或自然連線)
                概述:內連線使用比較運算子對兩個表的資料進行比較,並列出與連線條件相匹配的資料行,組合成新的記錄。
                語法格式:select  查詢欄位  from  表1  [inner]  join  表2  on 連線條件 
                內連線可再細分兩類
                    隱式內連線(隱式就是看不見inner join關鍵字,用where關鍵字代替)
                        select * from 表1,表2   where     表1.關係欄位=表2.關係欄位 
                    顯示內連線(存在inner join關鍵字)
                        select * from 表1 inner join 表2  on  表1.關係欄位=表2.關係欄位
        <3>外連線
                概述:返回的結果不僅包含符合查詢條件的資料,而且還包含左表(左外連線),右表(右外連線),或兩個表(全外連線)中所有資料
                外連線完整格式:
                    select * from  表1   left | right   outer  join  表2  on  表1.關係欄位=表2.關係欄位 where 條件
                左連線:返回左表中所有記錄和右表中符合連線條件的記錄
                    select * from  表1  left  outer  join  表2  on  表1.關係欄位=表2.關係欄位   where 條件
                右連線:返回右表中所有記錄和左表中符合連線條件的記錄
                    select * from  表1  right  outer  join  表2  on  表1.關係欄位=表2.關係欄位  where 條件
(2)HQL連線查詢:
    & 交叉查詢
    & 內連線
        顯示內連線
        隱式內連線
        迫切內連線
    & 外連線
        左外連線
        右外連線
        迫切外連線
    &查詢語法:
        * 概述:HQL連線查詢語法和原生的SQL語法差別不大,區別在於HQL連線查詢作用的是物件而不是表
        * SQL和HQL內連線示例比較
            SQL顯示內連線:
                select  *  from  cst_customer c  inner join  cst_linkman l on c.cust_id = l.lkm_id
            HQL內連線:
                from Customer c inner join c.linkMans
        * HQL的連線不用寫關聯欄位,不用寫具體的on條件,直接寫關聯屬性即可。
        * 迫切內連線(在內連線 inner  join 後新增一個fetch關鍵字。)
                fetch關鍵字分析:我們會發現無論是內連線還是迫切內連線生成的sql語句是一樣的,fetch關鍵字不會出現在sql語句中,
                                           因為sql語句沒有fetch關鍵字,fetch只能在hql中使用,生成sql後就消失。
                fetch的作用:
                        普通內連線封裝資料時,會將屬於客戶的資料封裝到Customer實體中,會將屬於聯絡人的資料封裝到LinkMan實體中
                            所以封裝後獲得的資料為List<Object[]{Customer,LinkMan}>
                            示例: 
                                    String hql="from Customer c inner join c.linkMans";
                                    //返回的List集合儲存著一個數組,儲存著客戶和聯絡人兩個物件 Object[]{Customer,LinkMan}
                                    List<Object[]> list = session.createQuery(hql).list();
                                    for (Object[] objects : list) {
                                        Customer c= (Customer) objects[0];
                                        LinkMan l=(LinkMan) objects[1];
                                        System.out.println(c);
                                        System.out.println(l);
                                    }
                        迫切內連線封裝資料時,會將屬於客戶的資料封裝到Customer實體中,會將屬於聯絡人的資料封裝到Customer實體中的聯絡人集合裡
                           所以封裝後獲得的資料為List<Customer>,但要注意的是,迫切內連線會出現重複的記錄,這就需我們用到 distinct關鍵字進行解決
                           示例:
                                    String hql2="select distinct c from Customer c inner join fetch c.linkMans";
                                    List<Customer> list2 = session.createQuery(hql2).list();
                                    for (Customer customer : list2) {
                                        System.out.println(customer);
                                    }
        * Hibernate的內連線和迫切內連線總結:
                兩者主要區別在於封裝資料的方式,但查詢的結果集是一樣的,底層生成的sql也一樣

Hibernate的查詢優化

概述:在很多CRM案例中要用到查詢操作,但Hibernate本身的查詢效率不是很好,特別在獲取關聯物件的方面,我們需要對查詢語句進行一些優化

Hibernate的抓取策略

1 抓取策略概述:Hibernate的抓取策略是一種提升Hibernate效能的手段,在Hibernate獲取關聯物件時,對傳送語句進行優化,需使用到延遲載入。
2 延遲載入(lazy load 懶載入)概述:Hibernate使用load方法關聯物件預設的載入方式,所謂的延遲載入就是當真正需要資料時,才真正執行資料載入操作。
3 延遲載入分類
    <1> 類級別延遲:
            概述:類級別延遲指的是查詢某個物件時,是否採用延遲,通常在<class>標籤上配置lazy屬性
            分析:& Hibernate查詢某個物件時,預設選擇類級別延遲(<class lazy="true">),所以使用load方法檢索某個物件時,
                            不會馬上傳送sql語句,真正呼叫呼叫該物件時才會傳送sql語句
                     & 若不想使用延遲載入,可以直接在對映檔案上設定<class lazy="false">,也可以用final修飾持久類,
                          使之無法生成代理類,    就會使延遲載入失效。  
                     & 值得一提的是,類級別的延遲載入我們一般不進行修改,採用預設值 lazy="true"
    <2> 關聯級別延遲
            概述:關聯級別延遲指的是,查詢一個物件的關聯物件時是否採用延遲載入,這個通常在<set>或<many-to-one>上配置lazy屬性
            分析:
                    & <set>標籤上的lazy取值
                        % true:預設值,採用延遲載入
                        % false:檢索關聯物件的時候,不採用延遲載入
                        % extra:極其懶惰
                    &<many-to-one>標籤上的lazy取值
                        % proxy:預設值,是否採用延遲取決於一方上的延遲載入lazy值
                        % false:檢索關聯物件時,不採用延遲載入
                        % no-proxy:不用研究
    <3>延遲載入總結:
             * 延遲載入:僅僅獲得沒有使用,不進行查詢,使用才進行查詢
             * 是否對類進行延遲載入:可以通過在實體類的class元素配置lazy屬性來控制
             * lazy配置只作用於 load方法,執行該方法時,不傳送任何sql語句,只返回一個物件,使用該物件才執行查詢
             * lazy="true"--進行延遲載入:載入時不執行,使用時才查詢
             * lazy="false"--不進行延遲載入:載入時立即執行
             * 延遲載入的原理是:
             * 它會把獲取到的Customer實體物件變成一個 具有加強功能的超級代理型別物件 Customer_$$
             * (增強的功能就是:使用這個物件才執行查詢,若不使用則不執行查詢) 
             * 值得注意的是:使用懶載入呼叫屬性時要確保 session還在開啟,因為它生成的代理物件是根據關聯的 session
             *  查詢資料庫的,否則會報異常 
             * 結論:建議使用延遲載入,因為它可以把節省不必要浪費的資源,提高效率
3 抓取策略指的是查詢某個物件時,如何抓取其關聯物件,也可通過配置實現:
    * <set>標籤上的fetch取值:
        & select :預設值,傳送的是普通的select語句
        & join:傳送一條迫切左外連線去查詢,值得一提的是,set上設定了fetch=join,lazy就會失效。
        & subselect:傳送一條子查詢語句查詢其關聯物件
    * <many-to-one>標籤上的fetch有兩個取值
        & select :預設值,傳送的是普通的select語句
        & join:傳送一條迫切左外連線去查詢
4 抓取策略fetch和延遲載入lazy配合使用分析:
    <1>單表查詢載入:fetch="select"
           延遲載入:lazy="true"
    <2>單表查詢載入:fetch="select"
           立即載入:lazy="false"
    <3>單表查詢載入:fetch="select"
           極其懶載入:lazy="extra",與懶載入效果基本一致,如果想獲得集合size(),則僅用count語句進行查詢
    <4>多表查詢載入:fetch="join"  一次性查詢所有的資料,使懶載入失效
            延遲載入:lazy="true"
    <5>子查詢:fetch="subselect"  主要用於重複繁雜查詢資料過程中,否則和 select效果一樣
            延遲載入:lazy="true"
    <5>子查詢:fetch="subselect"  主要用於重複繁雜查詢資料過程中,否則和 select效果一樣
             立即載入:lazy="false"
5 <set>集合上的fetch和lazy特點總結
    (1)fetch:控制的是 查詢其關聯物件時採用的sql語句格式
        * select:預設值,傳送一條select語句查詢其關聯物件
        * join:傳送一條迫切左外連線查詢其關聯物件
        * subselect:傳送一條子查詢查詢其關聯物件
    (2)lazy:控制的是 查詢其關聯物件時採用的延遲載入策略
        * true:延遲載入
        * false:立即載入
        * extra:極其懶惰
6 批量抓取(同時查詢多個物件的關聯物件)
    (1)實現批量查詢一方關聯的多方:
        <set>標籤內配置 batch-size屬性,如:
        <!--批量抓取 查詢一次,抓取集合數量為3
            抓取客戶的集合時,一次抓取多個客戶聯絡人的集合
        -->
        <set name="linkMans" batch-size="3" >
            <key column="lkm_cust_id" ></key>
            <one-to-many class="LinkMan"/>
        </set>
    (2)實現批量查詢多方關聯的一方:
        在多方的<class>標籤中配置  batch-size即可
        注意,並不是在<many-to-one>標籤內配置