亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

Hibernate 查詢語言(HQL)

1. 前言

本節課程和大家一起學習 Hibernate 中的 HQL ( Hibernate 查詢語言)。通過本節課程的學習,你將了解到:

  • HQL 基礎語法;
  • HQL 查詢的具體實現。-

2. HQL

查詢?前面不是講過?用過嗎?

但是,前面的查詢都是簡單查詢,真實項目中的查詢需求要遠比這個復雜。僅僅依靠 get()、load() 是遠遠達不到要求。

Hibernate 提供了靈活多樣的查詢機制,幾乎能做到無死角查詢。

  1. 標準化對象查詢 (Criteria Query): 以對象的方式進行查詢,將查詢語句封裝為對象操作。
  • 優點: 可讀性好,符合 Java 程序員的編碼習慣。
  • 缺點: 不夠成熟,不支持投影(projection)或統計函數(aggregation)
  1. Hibernate 語言查詢(Hibernate Query Language,HQL): 它是完全面向對象的查詢語句,查詢功能非常強大,具有繼承、多態和關聯等特性 。Hibernate 官方推薦使用 HQL 進行查詢。

  2. Native SQL Queries(原生 SQL 查詢): 直接使用數據庫提供的 SQL 語句進行查詢。

原生 SQL 的查詢能力是最強的,當其它查詢不能達到完成任務要求時,可使用它。

本次課程主要是介紹 HQL 查詢。

2.1 HQL 基礎語法

還是從查詢需求入手吧。

查詢需求:查詢所有學生。

前提:確定數據庫中有測試數據。

查詢流程:

首先編寫 HQL 語句,如:

select stuName,stuId from  Student

HQL 整體結構上類似于 SQL,可以認為 HQL 語句由兩部分組成,一部分直接借用 SQL 中的關鍵字,如 select、from、where 等,和 SQL 中要求是一樣的,編寫時可不區分大小寫。

另一部分就是純 JAVA 概念。

在 HQL 語句中,原生 SQL 語法中的表名類名替換,字段名屬性名替換,類和屬性是 Java 概念,所以要區分大小寫。

下面的 HQL 語句用來查詢所有學生信息,在 HQL 中同樣有別名一說。

from  Student s

HQL 是面向對象的、類似于 SQL 的查詢語言,本質上是不能直接交給數據庫的,在提交之前,需要把 HQL 轉譯成 SQL,在轉譯時把類名換成表名、把屬性名換成字段名。

雖然替換工作由 Hibernate 自己完成,但是,你需要有所了解。

從中可得出一個結論,HQL 查詢是沒有原生 SQL 查詢快的。

Hibernate 提供了 Query 組件執行 HQL 語句。

Query query=session.createQuery(hql);

Query 與原生 JDBC 中的 Statement 組件很相似,但其功能更高級、強大。

調用 Query 對象中 list() 查詢方法,就能查詢出開發者所需要的數據。

List<Student> stus= query.list();

最后,享受數據的時刻:

for (Student student : stus) {
	System.out.println(student);
}

完整的實例:

HibernateTemplate<Student> hibernateTemplate = new HibernateTemplate<Student>();
hibernateTemplate.template(new Notify<Student>() {
@Override
public Student action(Session session) {	
	String hql="from  Student s";
	Query query=session.createQuery(hql);
	List<Student> stus= query.list();
	for (Student student : stus) {
		System.out.println(student);
}
	return null;
}
});

執行實例,在控制臺可看到輸出了所有學生信息。簡直是簡單得不要不要。

如果只想查詢某幾個屬性的值,又該如何查詢呢?

很簡單,重構一下 HQL 語句:

String hql="select s.stuId,s.stuName from  Student s";

此時查詢出來的數據不是一個完整的對象。list() 方法查詢出來的結果不能直接封裝到 Student 類型中。

Hibernate 會把查詢出來的每一行數據封裝到一個數組中,所以 List 應該 是一個數組的集合,完整實例代碼如下:

HibernateTemplate<Student> hibernateTemplate = new HibernateTemplate<Student>();
hibernateTemplate.template(new Notify<Student>() {
@Override
public Student action(Session session) {	
String hql="select s.stuId,s.stuName from  Student s";
Query query=session.createQuery(hql);
List<Object[]> stus= query.list();
for (Object[] student : stus) {
	System.out.println(student[0]+":"+student[1]);
}
return null;
}
});

控制臺輸出結果:

Hibernate: 
    select
        student0_.stuId as col_0_0_,
        student0_.stuName as col_1_0_ 
    from
        Student student0_
1:Hibernate
2:Session
3:SessionFactory
24:Configuration

如果一定要使用 Student 類型接收查詢數據則必須保證 Student 類中存在如下的構造方法:

public Student(Integer stuId, String stuName) {
	this.stuId = stuId;
	this.stuName = stuName;
}

然后修改 HQL 語句:

String hql="select new Student(s.stuId,s.stuName) from  Student s";
	Query query=session.createQuery(hql);
	List<Student> stus= query.list();
	for (Student student : stus) {
		System.out.println(student);
	   }

建議大家使用上面的方案,畢竟很 OOP 嘛。其實也可以使用 Map 封裝查詢出來的數據。

String hql="select new Map(s.stuId as sid,s.stuName as sname) from  Student s";
Query query=session.createQuery(hql);
List<Map<String,Object>> stus= query.list();
for (Map<String,Object> map : stus) {
			System.out.println(map.get("sid")+":"+map.get("sname"));
}

道路千萬條,選擇在你手上。

2.2 HQL 高級查詢

強參數查詢

使用 SQL 查詢時,可以指定查詢條件,這個地球人都知道。HQL 中同樣能使用條件查詢:

from  Student s where s.stuId> 2

HQL 中,如果查詢條件中的數據需要通過參數傳遞,則會有兩種方案:

  • 匿名方案,已經司空見慣,對不對;
from  Student s where s.stuId> ?
  • 命名參數方案。
from  Student s where s.stuId> :id

參數名前面一定要有一個冒號 :id。

完整實例獻上:

String hql="from  Student s where s.stuId> :id";
Query query=session.createQuery(hql);
query.setInteger("id", 2);
List<Student> stus= query.list();
for (Student student : stus) {
	ystem.out.println(student);
}
return null;

可自行查看控制臺上的輸出結果。強命名參數和 ? 占位符作用是一樣的,但是,強命名參數可減少指定實參時的出錯率。

分頁查詢

分頁查詢是很實用的查詢機制。

使用原生 SQL 分頁查詢時,需要自己構建查詢 SQL 語句,不同的數據庫中的分頁查詢語句編寫也有差異性。Hibernate 通過其提供的分頁查詢功能很好地避開了這些問題。

分頁查詢之前,先搞清楚幾個與查詢有關的參數:

  • pageSize: 每一頁大??;
  • pageNum: 頁碼。

假如數據庫中有 20 行數據,分頁查詢時指定 pageSize5,則每 5 條數據為一個邏輯頁,總共有 4 頁。

如果要查詢第 3 頁數據,即 pageNum=3。

則需要跳過去的記錄數為:(pageNum-1)*pageSize=(3-1)*5=10 ,也就是從第 11 條數據開始查詢。

現在直接上實例代碼:

String hql = "from  Student s order by stuId" ;
Query query = session.createQuery(hql);
int pageNum=3;
int pageSize=5;
int passNum=(pageNum-1)*pageSize;
query.setFirstResult(passNum);
query.setMaxResults(pageSize);
List<Student> stus = query.list();
for (Student student : stus) {
	System.out.println(student.getStuName());
	}
return null;

HIbernate 會從第 11 條記錄開始,查詢出 5 條記錄。針對不同的數據庫系統,Hibernate 會給出最佳的 SQL 分頁方案。

聯合查詢

程序中所需要的數據可不一定在同一張表中,往往都是在多張表中。原生 SQL 通過多表連接或子查詢方式解決這個問題。

使用 HQL 一樣能表達出多表連接的意圖。

可能你會問:

前面的一對一、一對多、多對多映射關聯關系后,不就已經能夠查詢出多張表中的數據嗎。

如下面表數據:
圖片描述

在學生類中采用立即查詢策略:

@ManyToOne(targetEntity = ClassRoom.class, cascade = CascadeType.REMOVE,fetch=FetchType.EAGER)
@JoinColumn(name = "classRoomId")
public ClassRoom getClassRoom() {
	return classRoom;
}

查詢所有學生:

String hql = "from  Student s";
Query query = session.createQuery(hql);
List<Student> stus = query.list();
System.out.println("-----------------------------");
for (Student student : stus) {
System.out.println("學生姓名:"+student.getStuName());
System.out.println("班級名稱: "+student.getClassRoom().getClassRoomName());				
}
return null;

不要懷疑,結果一定是會出現的。但是,可以看到控制臺輸出了很多 SQL 語句。

那是因為,Hibernate 會先查詢出所有學生,然后根據班級 ID 再進入班級表進行查詢,這就是 Hibernate 查詢過程的 1+N 問題。

可改成下面的關聯查詢方式:

String hql = "select s.stuName,c.classRoomName from  Student s,ClassRoom c where s.classRoom=c";
Query query = session.createQuery(hql);
List<Object[]> stus = query.list();
System.out.println("-----------------------------");
for (Object[] student : stus) {
	System.out.println("學生姓名:"+student[0]);
	System.out.println("班級名稱: "+student[1]);				
}
return null;

控制臺輸入結果:

Hibernate: 
    select
        student0_.stuName as col_0_0_,
        classroom1_.classRoomName as col_1_0_ 
    from
        Student student0_ cross 
    join
        ClassRoom classroom1_ 
    where
        student0_.classRoomId=classroom1_.classRoomId

Hibernate 僅構建了一條 SQL 語句,直接查詢出來了所有數據,看得出來,其性能要大于 1+N 方案。

HQL 比想象中要簡單,比你預期的功能要強大。有了它,再也不怕查詢不到我們需要的數據。

2.3 HQL 與函數

先把上一節課程中遺留的內容向大家介紹一下。

使用原生 SQL 查詢時,可以在查詢語句中嵌入聚合函數,HQL 查詢中也可以使用聚合函數。

數據的用途之一是用于邏輯,產生新的數據。另一個用途是可產生報表,用于決策。

聚合函數的作用在于數據統計和分析,其現實意義很大。

HQL 中能使用哪些聚合函數?

答案是:SQL 中的聚合函數全部可照搬過來。

實例:統計學生相關信息。

String hql = "select count(*),sum(s.stuId),avg(s.stuId),max(s.stuId),min(s.stuId)  from  Student s";
Query query = session.createQuery(hql);
Object[] stus = (Object[]) query.uniqueResult();
System.out.println("學生總人數:" + stus[0]);
System.out.println("學生編號求和:" + stus[1]);
System.out.println("學生編號平均值:" + stus[2]);
System.out.println("學生編號最大值:" + stus[3]);
System.out.println("學生編號最小值:" + stus[4]);

使用過程很簡單,上面實例中用到了 Query 對象的 uniqueResult() 方法,當確定查詢結果只有一行記錄時,可以使用此方法。

好了,有句話說得好,師傅引進門,學藝在個人。因為 HQL 是對 SQLOOP 封裝,其內涵是一樣。對于很熟悉 SQL 語法的你們來講,全完掌握 HQL 也只時間的問題,不會存在技術上的難點。

3. 小結

又到了要總結的時候,本課程給大家介紹了 HQL 查詢語法,還有更多細節留待后面慢慢研究。

HQL 語法是一種類似于 SQL 語法,形式上與 SQL 一樣,但本質有很大區別。HQL 是面向對象的查詢語法結構。注意語句中哪些地方不區分大小,哪些地方 區分大小寫。

本節課也聊到了 HQL 的關聯查詢語法,很好解決了 1+N 問題。