本章介紹了Hibernate的幾種主要檢索方式:HQL檢索方式、QBC檢索方式、SQL檢索方式。HQL是Hibernate Query Language的縮寫,是官方推薦的查詢語言。QBC是Query By Criteria的縮寫,是Hibernate提供的一個查詢接口。Hibernate是一個輕量級的框架,它允許使用原始SQL語句查詢數據庫。
1.1 HQL基礎
HQL是Hiberante官方推薦的Hibernate檢索方式,它使用類似SQL的查詢語言,以面向對象的方式從數據庫中查詢。可以使用HQL查詢具有繼承、多態和關聯關系的數據。在檢索數據時應優先考慮使用HQL方式。
1.1.1 默認數據庫表和數據
在講解本章時,在沒有特殊說明時,用到的數據庫均為joblog,也就是在第4章建立的數據庫。joblog中添加了3個表:學生表student、課程表course和選課表sc。
學生表student中各字段的結構如圖6-1所示,字段的中文含義在Comment列中。
圖6-1 學生表的數據結構
學生表student中的數據如圖6-2所示,沒有特殊說明時,用到的均為這6條記錄。
此處仍然使用在第4章建立的HibernateProject項目,但是這里新建了一個包hibernate.ch06,這個包存放本章中的所有代碼。在hibernate.ch06包中建立學生表對應的持久化類Student.java,代碼如下。
圖6-2 學生表中的數據
package hibernate.ch06;
// 學生類
public class Student {
private Integer id; // 對象標識符
private Integer sno;// 學號
private String sname; // 姓名
private String ssex;// 性別
private String sdept; // 所在系別
private Integer sage; // 年齡
private String saddress; // 籍貫
…… //省略了所有的get/set訪問器
}
課程表course中的各個字段的結構如圖6-3所示,字段的中文含義在Comment列中。
圖6-3 課程表的結構
課程表中的數據如圖6-4所示,如果沒有特殊說明,用到的均為這4條記錄。
圖6-4 課程表的數據
在hibernate.ch06中新建持久化類Course.java類,代碼如下。
package hibernate.ch06;
// 課程類
public class Course {
private Integer id; // 對象標識符
private Integer cno;// 課程號
private String cname; // 課程名
private Integer Ccredit; // 學分
…… //省略了get/set訪問器
}
選修表sc(sc為student-course的縮寫)的結構如圖6-5所示,字段的中文含義在Comment列中。
圖6-5 選修表的結構
選修表中的數據如圖6-6所示,沒有特殊說明時,用到的均為這5條記錄。
圖6-6 選修表的數據
在hibernate.ch06中新建持久化類SC.java,SC.java的代碼如下。
package hibernate.ch06;
// 選課類
public class SC implements java.io.Serializable {
private Integer id; // id
private Integer sno; // 學號
private Integer cno; // 課程號
private Integer grade; // 成績
public SC() {
}
…… // 省略get/set訪問器
}
后面的章節中將用這3個表和3個持久化類進行講解。
1.1.2 檢索類的所有對象
使用HQL語句可以檢索出一個類的所有對象,如HQL語句“from Student”表示檢索Student類的所有對象。下面的程序檢索學生類的所有對象。
Query query = session.createQuery("from Student"); // 創建Query對象
List list = query.list(); // 執行查詢
// 以下代碼做顯示用,以后不再寫出來
Iterator it = list.iterator();
while (it.hasNext()) {
Student stu = (Student) it.next();
System.out.println("id" + stu.getId());
System.out.println("name" + stu.getSname());
System.out.println("\n");
}
session.createQuery()以HQL查詢語句為參數,生成一個查詢對象。本例中的HQL語句為“from Student”,這是from子句,格式如下。
from 類名
其中,類名可以為類的全限定名,如:
from hibernate.ch06.Student
Hibernate使用自動引入功能(auto import),會自動尋找需要的類,所以不推薦使用類的全限定名。注意,類名區分大小寫,如果寫成from student,將會拋出以下異常。
java.lang.NoClassDefFoundError: hibernate/ch06/student (wrong name: hibernate/ch06/Student)
HQL關鍵字不區分大小寫,FROM、from和From是一樣的。
調用query.list()時,真正開始執行HQL查詢語句,並把查詢的結果放在List中。
本例中查詢的是Student類中的所有屬性,如果查詢Student類中的某一個或某幾個屬性,如查詢所有學生的姓名和所在系,需要用到屬性查詢。
1.1.3 檢索類的某幾個屬性
與SQL語句類似,HQL語句可以檢索類的某一個或者某幾個屬性。以下代碼查詢所有學生的姓名和所在系。
// 創建Query對象
Query query = session.createQuery("select Student.sname,Student.sdept from Student");
List list = query.list(); // 執行查詢
// 以下代碼顯示查詢的信息
Iterator it = list.iterator();
while (it.hasNext()) {
Object[] stu = (Object[]) it.next();
System.out.println("id" + stu[0]);
System.out.println("name" + stu[1]);
System.out.println("\n");
}
屬性查詢使用select關鍵字,屬性查詢的格式如下。
select 屬性1,屬性2,… from 類名
屬性前可以加上類名加以限定,如:
select 屬性1,屬性2,… from 類名
但一般沒有必要。
屬性查詢區分大小寫,上面的代碼中如果寫成:
select SNAME,Sdept from Student
將拋出異常,提示找不到屬性SNAME和屬性Sdept。
查詢結果將只顯示查詢的屬性列。
屬性查詢的結果,對於用it.next()獲得的每條記錄,可以存儲在Object[]數組中,以便進行存取。
1.1.4 指定別名
在查詢時,可以用關鍵字as指定查詢的別名,指定別名可以簡化查詢,有時必需指定別名才能進行查詢。以下代碼查詢學號中含有4的學生的姓名和所在系。
select s.sname,s.sdept from Student as s where s.sno like '%4%'from Student s
s就是類Student的別名。注意as可以省略,即下面的查詢語句和上面的語句是等效的。
select s.sname,s.sdept from Student s where s.sno like '%4%'from Student s
1.1.5 where條件子句
where條件子句跟SQL中的where條件子句類似,它檢索符合條件的對象。例如,查詢所有所在系別為計算機系的學生:
select s.sname,s.sdept from Student s where s.dept=’計算機’
where子句指定查詢的條件,其語法和SQL類似。
在where子句中可以指定比較運算符:>、>=、<、<=、<>,其含義分別為大於、大於等於、小於、小於等於、不等於。
查詢年齡在22到23歲的學生:
from Student s where s.sage>=22 and s.sage<=23
在where子句中指定查詢的屬性是否為null:is null、is not null,其含義分別表示為空和不為空。
查詢所在籍貫為空的學生:
from Student s where s.saddress is null
1.1.6 使用distinct過濾掉重復值
使用distinct關鍵字將去掉結果中的重復值,只檢索符合條件的對象。如下面的例子檢索學生實例中的不重復的年齡。
Session session=Hsf.currentSession();//創建Session
String hql="select distinct s.sage from Student s"; //HQL查詢語句
Query query=session.createQuery(hql);//創建查詢
List list=query.list(); //執行查詢
檢索的結果如下,可見結果中去掉了一個重復的22歲。
20
21
22
23
24
1.1.7 刪除對象
HQL語句可以直接對復合條件的對象進行刪除,可以指定刪除的對象,並在提交后永久持久化到數據庫。HQL使用delete進行刪除,如刪除年齡大於25歲的學生可以使用如下代碼。
Session session=Hsf.currentSession(); //創建Session
Transaction tx=null; //聲明事務
try{
tx=session.beginTransaction(); //開始事務
//創建查詢
String hql="delete Student s where s.sage>25";
Query query=session.createQuery(hql);
query.executeUpdate(); //執行
tx.commit(); //成功,則提交
tx=null;
}catch(Exception e){
e.printStackTrace();
if(tx!=null){
tx.rollback(); //失敗則回滾
}
}finally{
session.close();
}
注意以下兩點。
在刪除對象時,執行query.executeUpdate()進行數據刪除,但只有執行了tx.commit()進行事務提交時,才真正從數據庫中刪除數據。
如果設置了級聯刪除,則與之相關聯的對象實例也被刪除。
1.1.8 更新對象值
更新對象的HQL語句與SQL語法很相似,使用update更新對象的值。如下面例子更新對象的sage屬性。
ransaction tx=null; //聲明事務
try{
tx=session.beginTransaction(); //開始事務
String hql="update Student s set s.sage='22' where s.id=11"; //更新語句
Query query=session.createQuery(hql);
query.executeUpdate(); //執行
tx.commit(); //成功,則提交
tx=null;
}catch(Exception e){
e.printStackTrace();
if(tx!=null){
tx.rollback(); //失敗則回滾
}
}finally{
session.close();
}
1.1.9 查詢計算屬性值
HQL可以查詢經過計算的值,在一些需要計算的地方可以進行計算,例如查詢全體學生的姓名和出生年份。
select s.sname,2006-s.sage from Student as s
select子句十分靈活,幾乎和SQL語句有着同樣的能力,對象的屬性值可以參與運算。
這行代碼假設當前的年份是2006年。
下面是另外幾個查詢計算屬性值的例子。
select s.sname,2006-s.sage from Student as s
1.1.10 使用函數
當需要調用函數時,HQL提供了一些類似SQL的函數。這些函數可以簡化操作。例如查詢學生的姓名、出生日期和性別,其中性別用小寫表示。
select s.sname,2006-s.sage,lower(s.ssex) from Student as sselect s.sname,2006-s. sage from Student as s
1.1.11 between...and...和not between... and...確定查詢范圍
between...and...用來查詢屬性值在指定范圍內的實體對象,not between...and...用來查詢屬性值不在指定范圍內的實體對象。如查詢學生年齡在22到23之間的學生:
select s.sno,s.sname,s.sage from Student s where s.sage between 22 and 23
查詢將返回如下結果。
---------------------------------------------------------------------
1 20040001 李曉梅 22 計算機系
---------------------------------------------------------------------
2 20040002 王蒙 23 外語系
---------------------------------------------------------------------
4 20050004 李文 22 計算機系
between后跟的是查詢范圍的下限,and后跟的是查詢范圍的上限,所以下面的查詢語句總沒有對象返回。
from Student s where s.sage between 23 and 22
1.1.12 in和not in確定查詢集合
關鍵字in用來查詢指定屬性值屬於指定集合的對象,關鍵字not in用來查詢指定屬性值不屬於指定集合的對象。如查詢不是計算機系,也不是數學系的學生。
select s.sno,s.sname,s.sdept from Student s where s.sdept not in ('計算機系','數學系')
查詢將返回如下結果。
---------------------------------------------------------------------
20040002 王蒙 外語系
---------------------------------------------------------------------
20050003 姜浩 化學系
---------------------------------------------------------------------
20050005 薛鵬 生物系
1.1.13 like進行模糊查詢
用like進行模糊查詢時有兩個可用的通配符:“%”和“_”。“%”代表長度大於等於0的字符,“_”代表長度為1的單個字符。
查詢姓李的學生:
select s.sno,s.sname,s.sdept from Student s where s.sname like '%李%'
查詢結果如下。
---------------------------------------------------------------------
20040001 李曉梅 計算機系
---------------------------------------------------------------------
20050004 李文 計算機系
---------------------------------------------------------------------
20050006 李思 數學系
查詢姓名為兩個字符的學生:
select s.sno,s.sname,s.sdept from Student s where s.sname like '__'
查詢結果如下。
---------------------------------------------------------------------
20040002 王蒙 外語系
---------------------------------------------------------------------
20050003 姜浩 化學系
---------------------------------------------------------------------
20050004 李文 計算機系
---------------------------------------------------------------------
20050005 薛鵬 生物系
---------------------------------------------------------------------
20050006 李思 數學系
1.1.14 and邏輯與
當要檢索指定的多個條件,且條件的邏輯關系為與時,使用“and”關鍵字。如檢索計算機系的女生,這個檢索要求包含兩個條件:“計算機系”和“女生”。
select s.sno,s.sname,s.sdept from Student s where s.sdept='計算機系' and s.ssex='F'
檢索的結果如下。
---------------------------------------------------------------------
20040001 李曉梅 計算機系
---------------------------------------------------------------------
20050004 李文 計算機系
1.1.15 or邏輯或
當檢索的多個條件,且條件的邏輯關系為或時,使用“or”關鍵字。如檢索姓王,或者年齡大於22歲的學生:
select s.sno,s.sname,s.sdept from Student s where s.sname like '王%' or s.sage>22
檢索結果如下。
---------------------------------------------------------------------
20040002 王蒙 外語系
---------------------------------------------------------------------
20050005 薛鵬 生物系
1.1.16 order by對結果進行排序
“order by”關鍵字對結果進行排序,默認為升序。“order by asc”為升序,“order by desc”為降序。例如將學生表中的學生按照年齡升序排序:
from Student s order by s.sage
檢索結果如下。
---------------------------------------------------------------------
6 20050006 李思 20 數學系
---------------------------------------------------------------------
3 20050003 姜浩 21 化學系
---------------------------------------------------------------------
1 20040001 李曉梅 22 計算機系
---------------------------------------------------------------------
4 20050004 李文 22 計算機系
---------------------------------------------------------------------
2 20040002 王蒙 23 外語系
---------------------------------------------------------------------
5 20050005 薛鵬 24 生物系
將學生表中的學生按照年齡降序排列,按照所在系升序排列。
from Student s order by s.sage,s.sdept desc
1.1.17 group by對記錄進行分組
對查詢進行分組可以對查詢進行細化。分組經常和聚集函數一起使用,這樣聚集函數將作用於每個分組。
group by的用法為:
select 屬性1,屬性2,屬性3 ,…,屬性n from 類名 group by 屬性m
其中屬性1,屬性2,…,屬性n必須滿足下列條件。
要么作為聚集函數的參數,要么為屬性m。例如,檢索各個系的學生的平均年齡:
select avg(s.sage),s.sdept from Student s group by s.sdept
其中字段s.sage作為平均值函數的參數,s.sdept是group by后的一個屬性。檢索的結果如下。
---------------------------------------------------------------------
化學系 21.0
---------------------------------------------------------------------
計算機系 22.0
---------------------------------------------------------------------
生物系 24.0
---------------------------------------------------------------------
數學系 20.0
---------------------------------------------------------------------
外語系 23.0
檢索各個課程號與對應的選課人數。
select cno,count(sno) from SC s group by s.cno
1.1.18 having關鍵字
having關鍵字和group by關鍵字搭配使用,它對分組后的記錄進行篩選,輸出符合having指定條件的組。例如查詢人數超過1000人的系。
select s.sdept from Student s group by s.sdept having count(*)>1000
查詢男生人數多於500人的系。
select s.sdept from Student s where s.ssex=’M’ group by s.sdept having count(*)>500
以上面查詢男生人數多於500人的系為例:
select s.sdept from Student s where s.ssex=’M’ group by s.sdept having count(*)>500
查詢過程中同時使用group by和having關鍵字時,查詢步驟如下。
(1)檢索符合s.ssex=‘M’的所有男生。
(2)根據s.sdept分組成不同的系。
(3)對於每一個分組,計算分組中的記錄條數大於500的系。
(4)將符合上述條件的s.sdept選出來。
where和having的區別在於作用對象不同。where作用於基本表,而having作用於分組后的組。
1.1.19 聚集函數
聚集函數包括count()、avg()、sum()、max()、min(),其含義如表6-1所示。
表6-1 聚集函數及其含義
聚 集 函 數 |
含 義 |
count() |
計算符合條件的記錄條數 |
avg() |
計算符合條件的平均值 |
sum() |
計算符合條件的和 |
max() |
計算符合條件的最大值 |
min() |
計算符合條件的最小值 |
各個函數的用法舉例如下。
檢索學生實例的對象個數:
select count(*) from Student
檢索計算機系的人數:
select count(*) from Student s where s.sdept='計算機系'
檢索學生實例的平均年齡:
select avg(s.sage) from Student s
檢索課程表course的所有課程的學分的總和:
select sum(c.ccredit) from Course c
檢索課程號為“1”的課程的最高成績:
select max(s.grade) from SC s where s.cno=1
檢索課程號為“1”的課程的最低成績:
select min(s.grade) from SC s where s.cno=1
聚集函數經常和group by分組關鍵字搭配使用。
檢索各門課程的平均成績:
select s.cno,avg(s.grade) from SC s group by s.cno
檢索各科不及格人數:
select s.cno,count(*) from SC s where s.grade<60 group by s.cno
1. 查詢整個映射對象所有字段
//直接from查詢出來的是一個映射對象,即:查詢整個映射對象所有字段
String hql =
"from Users"
;
Query query = session.createQuery(hql);
List<Users> users = query.list();
for
(Users user : users){
System.out.println(user.getName() +
" : "
+ user.getPasswd() +
" : "
+ user.getId());
}
輸出結果為:
name1 : password1 :
1
name2 : password2 :
2
name3 : password3 :
3
|
2.查詢字段
//查詢其中幾個字段
String hql =
" select name,passwd from Users"
;
Query query = session.createQuery(hql);
//默認查詢出來的list里存放的是一個Object數組
List<Object[]> list = query.list();
for
(Object[] object : list){
String name = (String)object[
0
];
String passwd = (String)object[
1
];
System.out.println(name +
" : "
+ passwd);
}
輸出結果為:
name1 : password1
name2 : password2
name3 : password3
|
3.修改默認查詢結果(query.list())不以Object[]數組形式返回,以List形式返回
//查詢其中幾個字段,添加new list(),注意list里的l是小寫的。也不需要導入包,這樣通過query.list()出來的list里存放的不再是默認的Object數組了,而是List集合了
String hql =
" select new list(name,passwd) from Users"
;
Query query = session.createQuery(hql);
//默認查詢出來的list里存放的是一個Object數組,但是在這里list里存放的不再是默認的Object數組了,而是List集合了
List<List> list = query.list();
for
(List user : list){
String name = (String)user.get(
0
);
String passwd = (String)user.get(
1
);
System.out.println(name +
" : "
+ passwd);
}
/**
輸出結果為:
name1 : password1
name2 : password2
name3 : password3
*/
|
4.修改默認查詢結果(query.list())不以Object[]數組形式返回,以Map形式返回
//查詢其中幾個字段,添加new map(),注意map里的m是小寫的。也不需要導入包,這樣通過query.list()出來的list里存放的不再是默認的Object數組了,而是map集合了
String hql =
" select new map(name,passwd) from Users"
;
Query query = session.createQuery(hql);
//默認查詢出來的list里存放的是一個Object數組,但是在這里list里存放的不再是默認的Object數組了,而是Map集合了
List<Map> list = query.list();
for
(Map user : list){
//一條記錄里所有的字段值都是map里的一個元素,key是字符串0,1,2,3....,value是字段值
//如果將hql改為:String hql = " select new map(name as username,passwd as password) from Users";,那么key將不是字符串0,1,2...了,而是"username","password"了
String name = (String)user.get(
"0"
);
//get("0");是get(key),注意:0,1,2...是字符串,而不是整形
String passwd = (String)user.get(
"1"
);
System.out.println(name +
" : "
+ passwd);
}
/**
輸出結果為:
name1 : password1
name2 : password2
name3 : password3
*/
|
5.修改默認查詢結果(query.list())不以Object[]數組形式返回,以自定義類型返回
6.條件查詢
//條件查詢,參數索引值從0開始,索引位置。通過setString,setParameter設置參數
String hql =
"from Users where name=? and passwd=?"
;
Query query = session.createQuery(hql);
//第1種方式
// query.setString(0, "name1");
// query.setString(1, "password1");
//第2種方式
query.setParameter(
0
,
"name1"
,Hibernate.STRING);
query.setParameter(
1
,
"password1"
,Hibernate.STRING);
List<Users> list = query.list();
for
(Users users : list){
System.out.println(users.getId());
}
|
//條件查詢,自定義索引名(參數名):username,:password.通過setString,setParameter設置參數
String hql =
"from Users where name=:username and passwd=:password"
;
Query query = session.createQuery(hql);
//第1種方式
// query.setString("username", "name1");
// query.setString("password", "password1");
//第2種方式,第3個參數確定類型
query.setParameter(
"username"
,
"name1"
,Hibernate.STRING);
query.setParameter(
"password"
,
"password1"
,Hibernate.STRING);
List<Users> list = query.list();
for
(Users users : list){
System.out.println(users.getId());
}
|
//條件查詢,通過setProperties設置參數
String hql =
"from Users where name=:username and passwd=:password"
;
Query query = session.createQuery(hql);
//MyUser類的2個屬性必須和:username和:password對應
MyUser myUser =
new
MyUser(
"name1"
,
"password1"
);
query.setProperties(myUser);
List<Users> list = query.list();
for
(Users users : list){
System.out.println(users.getId());
}
|
7.update 數據
執行SQL語句(為什么要用SQL語句,我想是為了執行某些復雜的SQL語句吧)
String sql=
"update Table set field = 'test'"
Session session = HibernateSessionFactory.getSession();
session.createSQLQuery(sql).executeUpdate();
ts.commit();
|
執行HQL語句
String hql=
"update Table set field = 'test'"
|
Session session = HiberanteSessionFactory.getSession();
Transaction ts = session.beginTransaction();
Query query = session.createQuery(hql);
query.executeUpdate();
ts.commit();
|