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

Hibernate知識點復習之四

查詢優化 res detach sql語句 sele 就會 action ram 獲取

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>標簽內配置

Hibernate知識點復習之四