Hibernate 中的 Criteria 查詢
1. 前言
今天給大家介紹一個(gè)絕對(duì)純正的 OOP 查詢方案:Criteria 查詢。通過本節(jié)課程的內(nèi)容,你將了解到:
- 什么是 Criteria 查詢
- Criteria 實(shí)現(xiàn)復(fù)雜查詢;
2. Criteria 查詢
什么是 Criteria 查詢?
Criteria 查詢從字面翻譯就是標(biāo)準(zhǔn)查詢。所謂 標(biāo)準(zhǔn)查詢,指的是 HIbernate 提供了純正的 OOP API 查詢方案。不像 HQL 還摻雜了一些 SQL 層面的內(nèi)容。
來一個(gè)查詢需求:查詢所有的學(xué)生。
想必這學(xué)生會(huì)很生氣,總是被搬來搬去的。
上實(shí)例之前,先認(rèn)識(shí) Hibernate 兄弟會(huì)中的一名新成員:Criteria。
在使用 Criteria 查詢之前,必須先創(chuàng)建 Criteria 對(duì)象:
Criteria cr = session.createCriteria(Student.class);
List<Student> stus = cr.list();
是不是很 OOP。使用 HQL 時(shí),會(huì)有一種時(shí)空穿越的感覺 ,OOP 和 SQL 語法交替出現(xiàn),很容易犯暈。使用 Criteria 進(jìn)行查詢時(shí)則不會(huì)。
而且,Criteria 不是一個(gè)人在戰(zhàn)斗,它也有屬于自己的兄弟會(huì),為開發(fā)者提供了更強(qiáng)有力的支持。
先介紹一下它的幾個(gè)兄弟,并且它們的作用已經(jīng)從字面告訴了你。
- Criterion: 這位兄弟長得好生面熟,其實(shí)它就 Criteria 的單數(shù)存在形式;
- Oder: 提供排序功能;
- Restrictions: 限制、約束的意思,和 SQL 中的 where 關(guān)鍵字的作用是一樣。所以,它提供了很多類似于運(yùn)算符的方法,可以對(duì)查詢數(shù)據(jù)進(jìn)行過濾。
Criteria 面子上很 OOP ,但是無論你怎么逃,都是在 SQL 的手掌心,也就是說 Criteria 查詢最終還是會(huì)被 Hibernate 轉(zhuǎn)譯成 SQL 語句。
只要是使用關(guān)系型數(shù)據(jù)庫,SQL 就是逃不掉的宿命。只是直接、間接使用的區(qū)別。
所以,Criteria 查詢中總會(huì)找到 SQL 的影子。
2.1 基礎(chǔ)查詢
為了更好地理解它們,來一個(gè)實(shí)例:查詢姓名叫 “Hibernate” 的學(xué)生。
Criteria criteria = session.createCriteria(Student.class);
Criterion criterion = Restrictions.eq("stuName", "Hibernate");
criteria.add(criterion);
Student student = (Student) criteria.uniqueResult();
System.out.println(student);
Criteria 查詢封裝了關(guān)系型數(shù)據(jù)庫的概念,所以,一定要注意,使用方法進(jìn)行數(shù)據(jù)過濾時(shí),都是屬性進(jìn)行比較。
確認(rèn)查詢出來的數(shù)據(jù)只有一條記錄時(shí),可以使用 uniqueResult() 方法。條件查詢的關(guān)鍵是了解 Restrictions,它所提供的很多類似于邏輯運(yùn)算符的方法:
- Restrictions.eq(): 相當(dāng)于 =;
- Restrictions.not(Exprission.eq()) : 相當(dāng)于 <>;
- Restrictions.le(): 相當(dāng)于 <=;
- Restrictions.gt(): 相當(dāng)于 >;
- Restrictions.ge(): 相當(dāng)于 >=;
- Restrictions.lt(): 相當(dāng)于 <;
- Restrictions.isnull(): 相當(dāng)于 is null;
- Restrictions.isNotNull(): 相當(dāng)于 is not null ;
- Restrictions.like(): 相當(dāng)于 like;
- Restrictions.and(): 相當(dāng)于 and;
- Restrictions.conjunction(): 相當(dāng)于 and;
- Restrictions.or(): 相當(dāng)于 or;
- Restrictions.disjunction() : 相當(dāng)于 or;
- Restrictions.not(): 相當(dāng)于 not;
- Restrictions.in(): 相當(dāng)于 in;
- Restrictions.not(Restrictions.in()): 相當(dāng)于 not in;
- Restrictions.between(): 相當(dāng)于 between x and y;
- Restrictions.not(Restrictions…between()) : 相當(dāng)于 not between x and y。
如上方法,幾乎涵蓋了所有 SQL 條件運(yùn)算符,任意組合上面方法,沒有查詢不出來的結(jié)果。
如查詢學(xué)生編號(hào)是 1 或 2 或 4 的學(xué)生。
使用 SQL,則是:
select * from student where stuId in (1,2,4)
使用 Criteria 查詢,則如下所示:
Criterion criterion = Restrictions.in("stuId",new Integer[] {1,2,4} );
criteria.add(criterion);
如查詢學(xué)生編號(hào)大于 2 且班級(jí)編號(hào)為 1 的學(xué)生。
使用 SQL:
select * from student where stuId>2 and classRommId=1
如果使用 Criteria 查詢,則先構(gòu)建兩個(gè)約束對(duì)象:
Criterion criterion = Restrictions.gt("stuId", 2);
Criterion criterion1 = Restrictions.eqOrIsNull("classRoom.classRoomId", 1);
再把這兩個(gè)約束作為參數(shù),構(gòu)建一條聯(lián)合約束:
LogicalExpression logicalExpression = Restrictions.and(criterion, criterion1);
criteria.add(logicalExpression);
LogicalExpression API 用來表示一個(gè)邏輯表達(dá)式。是 Criterion 的子類。
比較原生 SQL 和 Criteria 查詢,會(huì)發(fā)現(xiàn)原生 SQL 語句要簡(jiǎn)單很多,使用 Criteria 查詢需要掌握很多 API,而且代碼量也比較大,這也可能是 Criteria 查詢得不到普及的原因吧。
但是,Hibernate 既然推出了這種查詢方案,想必也有它的考慮。比如說,創(chuàng)建動(dòng)態(tài)查詢語句,這點(diǎn)原生 SQL 或 HQL 都沒有 Criteria 好。
還是那句話,存在就是合理的。
如果,你對(duì)原生 SQL 有情懷,Criteria 查詢中也是可以用的。
criteria.add( Restrictions.sqlRestriction("stuId>2 and clasRoomId=1"));
注意,不要在 Sql 片段中使用 where 關(guān)鍵字。既然是原生 SQL,所以語句中是字段概念,而不是屬性概念。
前面講解 HQL 時(shí),提到了分頁查詢。Criteria 一樣可以實(shí)現(xiàn)分頁查詢,和 HQL 中分頁方法一樣:
Criteria criteria = session.createCriteria(Student.class);
criteria.setFirstResult(1);
criteria.setMaxResults(5);
List results = criteria.list();
2.2 高級(jí)查詢
排序查詢使用 order API 實(shí)現(xiàn):
criteria.addOrder(Order.desc("stuId"));
criteria.addOrder(Order.asc("stuName"));
一樣,可以多字段排序。
使用聚合函數(shù):聚合函數(shù)的功能封裝在 projections API 中:
criteria.setProjection(Projections.rowCount());
criteria.setProjection(Projections.avg("stuId"));
criteria.setProjection(Projections.max("stuId"));
criteria.setProjection(Projections.min("stuId"));
criteria.setProjection(Projections.sum("stuId"));
Criteria 也能實(shí)現(xiàn)關(guān)聯(lián)查詢:
Criteria criteria = session.createCriteria(Student.class);
criteria.add(Restrictions.like("stuName", "Hibernate%"));
Criteria criteria01 = criteria.createCriteria("classRoom");
criteria01.add(Restrictions.like("classRoomName", "c19%"));
List<Student> students = criteria.list();
可以把一個(gè) Criteria 實(shí)例看成對(duì)一張表的查詢,如果需要關(guān)聯(lián)多張表,則可以通過一個(gè) Criteria 再創(chuàng)建一個(gè) Criteria 對(duì)象。
Hibernate 為 Criteria 查詢提供各種各樣的 API,適應(yīng)于任何查詢需求,相比較使用的已經(jīng)很普遍的 SQL 查詢,Criteria 查詢充滿了雞肋的味道。但對(duì)于動(dòng)態(tài)查詢需求,Criteria 查詢的優(yōu)勢(shì)又很明顯。
3. 原生 SQL 查詢
Hibernate 支持原生 SQL 查詢,對(duì)于熟悉并鐘情于 SQL 語句的開發(fā)者來講,是一個(gè)很大的福音。
實(shí)例:
String sql="select * from student";
SQLQuery sqlQuery= session.createSQLQuery(sql);
Hibernate 提供了一個(gè)與原生 SQL 有關(guān)的 SQLQuery 對(duì)象。SQLQuery 是 Query 的子類,可適應(yīng)不同的原生 SQL 語句查詢。
4. 小結(jié)
本節(jié)課和大家聊到了 Criteria 查詢,HQL 查詢,原生 SQL 查詢。原生 SQL 查詢無所不能,HQL 查詢是面向?qū)ο蟮?SQL ,具有混血身份。據(jù)說,混血的總是很美,也是建議大家選擇的一種查詢方案。
Criteria 查詢是 Hibernate 提供的一種純面向?qū)ο蟮慕鉀Q方案,但是,為了構(gòu)建一條 SQL 語句需要寫許多代碼,其應(yīng)用領(lǐng)域會(huì)相對(duì)較窄。
本節(jié)課給大家介紹 Criteria 的目的,一是擴(kuò)展學(xué)生的學(xué)習(xí)范圍,如果需要使用動(dòng)態(tài)查詢,Criteria 則有著無可比擬的優(yōu)越感。