Hibernate知識點複習之四
阿新 • • 發佈:2018-11-11
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>標籤內配置