Hibernate知識點復習之四
阿新 • • 發佈:2018-10-18
查詢優化 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知識點復習之四