Hibernate檢索方式
Hibernate提供了以下幾種檢索對象的方式
- 導航對象圖檢索方式:根據已經加載的對象導航到其他對象
例如前面我們學到的通過Customer
類來獲取集合的方式來獲取Order
對象 - OID檢索方式:按照對象的OID來檢索對象
- HQL檢索方式:使用面向對象的HQL查詢語言
- QBC檢索方式:使用QBC API來檢索對象,這種API封裝了基於字符串形式的查詢語句,提供了更加面向對象的查詢接口。
- 本地SQL檢索方式:使用本地數據庫的SQL查詢語句。
HQL檢索方式
HQL是面向對象的查詢語言,在Hibernate提供的各種檢索方式中,HQL是使用最廣的一種檢索方式。它有如下功能:
- 在查詢語句中設定各種查詢條件
- 支持投影查詢,即僅檢索出對象的部分屬性
- 支持分頁查詢
- 支持連接查詢
- 支持分組查詢,運行使用HAVING和GROUP BY關鍵字
- 提供內置聚集函數,如
sum()
,min()
,max()
- 支持子查詢
- 支持動態綁定參數
- 能夠調用用戶定義的SQL函數或標準的SQL函數
HQL檢索方式包括以下步驟:
- 通過Session的
createQuery()
創建一個Query
對象,它包括一個HQL查詢語句,HQL查詢語句中可以包含命名參數。 - 動態綁定參數
- 調用
Query
的相關方法執行查詢語句
Query接口支持方法鏈編程風格,它的setParameter()
方法返回自身實例,而不是void
類型
綁定參數
- Hibernate的參數綁定機制依賴於JDBC API中的PreparedStatement的預定義SQL語句功能。
- HQL的參數綁定有兩種形式
按參數名字綁定:在HQL查詢語句中定義命名參數,命名參數以:
開頭。
按參數位置綁定:在HQL查詢語句中用?編號
來定義參數位置。 - 相關方法
setParameter()
:綁定任意類型的參數,該方法的第三個參數顯式指定Hibernate映射類型。
setEntity()
:把參數與一個持久化類綁定。但該方法在Hibernate5中已過時,推薦使用第一個方法setParameter()
。 - HQL採用ORDER BY關鍵字對查詢結果排序。
假設有兩張數據表如下:
department
表和employees
表爲一對多關係。
兩個實體類如下:
package com.cerr.hibernate.entities;
import java.util.Set;
public class Department {
private Integer id;
private String name;
private Set<Employee> emps;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set < Employee > getEmps() {
return emps;
}
public void setEmps(Set < Employee > emps) {
this.emps = emps;
}
}
package com.cerr.hibernate.entities;
public class Employee {
private Integer id;
private String name;
private float salary;
private String email;
private Department dept;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public float getSalary() {
return salary;
}
public void setSalary(float salary) {
this.salary = salary;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Department getDept() {
return dept;
}
public void setDept(Department dept) {
this.dept = dept;
}
@Override
public String toString() {
return "Employee{" +
"id=" + id +
", name='" + name + '\'' +
", salary=" + salary +
", email='" + email + '\'' +
", dept=" + dept +
'}';
}
}
可以使用?編號
來作爲參數:
@org.junit.Test
public void test(){
//創建Query對象
String hql = "FROM Employee WHERE salary > ?0 AND email LIKE ?1 ";
Query query = session.createQuery(hql);
Department dept = session.get(Department.class,1);
//綁定參數
query.setParameter(0,(float)6000).setParameter(1,"%9%");
//查詢
List<Employee> emps = query.list();
System.out.println(emps.size()+":"+emps);
}
也可以使用:標識符
作爲參數,還可以將實體類也作爲參數(前提是存在映射關係)
@org.junit.Test
public void test(){
//創建Query對象
String hql = "FROM Employee WHERE salary > :sal AND dept = :d AND email LIKE :ema ";
Query query = session.createQuery(hql);
Department dept = session.get(Department.class,1);
//綁定參數
query.setParameter("sal",(float)6000).setParameter("ema","%%").setParameter("d",dept);
//查詢
List<Employee> emps = query.list();
System.out.println(emps.size()+":"+emps);
}
分頁查詢
setFirstResult(int firstResult)
設定從哪個對象開始檢索,參數firstResult
表示這個對象在查詢結果中的索引位置,索引位置的起始值爲0,默認情況下,Query
從查詢結果中的第一個對象開始檢索setMaxResults(int maxResults)
設定一次最多檢索出的對象的數目。在默認情況下,Query
和Criteria
接口檢索出查詢結果中所有的對象。
@org.junit.Test
public void testPageQuery(){
String hql = "FROM Employee";
Query query = session.createQuery(hql);
int pageNo = 3;//頁數
int pageSize = 2;//每頁最大數量
//查詢第pageNo頁的pageSize條記錄
List<Employee> employees = query.setFirstResult((pageNo-1)*pageSize).setMaxResults(pageSize).list();
System.out.println(employees);
}
在映射文件中定義命名查詢語句
- Hibernate允許在映射文件中定義字符串形式的查詢語句。
-
<query>
元素用於定義一個HQL查詢語句,它和<class>
元素並列。 - 在程序中通過
Session
的getNameQuery()
獲取查詢語句對應的Query
對象。
我們在Employee.hbm.xml
文件中定義命名查詢語句<query name="salaryEmps"><![CDATA[FROM Employee WHERE salary > :minSalary AND id < :maxID ]]></query>
。
然後測試類代碼:
@org.junit.Test
public void testNameQuery(){
Query query = session.getNamedQuery("salaryEmps");
List<Employee> employees = query.setParameter("minSalary",(float)5000).setParameter("maxID",4).list();
System.out.println(employees);
}
投影查詢
查詢結果僅包含實體的部分屬性,可以通過SELECT關鍵字實現。
Query
的list()
返回的集合中包含的是數組類型的元素,每個對象數組代表查詢結果的一條記錄。可以在持久化類中定義一個對象的構造器來包裝投影查詢返回的記錄。
可以通過DISTINCT關鍵字來保證查詢結果不會返回重複元素。
/**
* 返回一個對象的集合
*/
@org.junit.Test
public void testFieldQuery2(){
String hql = "SELECT new Employee(salary,email) FROM Employee WHERE dept = :dept ";
Query query = session.createQuery(hql);
Department dept = new Department();
dept.setId(1);
List<Employee> result = query.setParameter("dept",dept).list();
//打印
for(Employee emp : result){
System.out.println(emp.getEmail());
}
}
/**
* 返回一個字符串的集合
*/
@org.junit.Test
public void testFieldQuery(){
String hql = "SELECT email,salary FROM Employee WHERE dept = :dept ";
Query query = session.createQuery(hql);
Department dept = new Department();
dept.setId(1);
List<Object[]> result = query.setParameter("dept",dept).list();
//打印
System.out.println(result);
for (Object[] res : result){
System.out.println(Arrays.asList(res));
}
}
注意在使用返回對象集合的時候,在實體類中需要有對應的構造器方法,並且如果加的是有參的構造器,那麼一定要加上一個無參構造器。
報表查詢
報表查詢用於對數據分組和統計,與SQL一樣,HQL利用GROUP BY關鍵字對數據分組,用HAVING關鍵字對分組數據設定約束條件。
在HQL查詢語句中可以調用以下的聚集函數
count()
min()
max()
sum()
avg()
示例:查詢最低工資(大於5000)和最高工資
@org.junit.Test
public void testGroupBy(){
String hql = "SELECT min(salary),max(salary) FROM Employee GROUP BY dept HAVING min(salary) >:minSal";
Query query = session.createQuery(hql).setParameter("minSal",(float)5000);
List<Object[]> res = query.list();
for (Object[] r : res){
System.out.println(Arrays.asList(r));
}
}
HQL的(迫切)左外連接
迫切左外連接
-
LEFT JOIN FETCH
關鍵字表示迫切左外連接檢索策略 -
list()
返回的集合中存放實體對象的引用。每個Department
對象關聯的Employee
集合都被初始化,存放所有關聯的Employee
的實體對象。 - 查詢結果中可能會包含重複元素,可以通過一個HashSet來過濾重複元素。也可以在查詢語句中加入
distinct
關鍵字。
左外連接
-
LEFT JOIN
關鍵字表示左外連接查詢 list()
返回的集合中存放的是對象數組類型- 根據配置文件來決定
Employee
集合的檢索策略 - 如果希望
list()
返回的集合僅僅包含Department
對象,可以在HQL查詢語句中使用SELECT
關鍵字。
//左外連接
@org.junit.Test
public void testLeftJoin(){
String hql = "SELECT distinct d FROM Department d LEFT JOIN d.emps";
Query query = session.createQuery(hql);
//加上SELECT後
List<Department> res = query.list();
for (Department department : res){
System.out.println(department);
}
//沒加SELECT
// List<Object[]> result = query.list();
// System.out.println(result);
// for (Object[] objects : result){
// System.out.println(Arrays.asList(objects));
// }
}
//迫切左外連接
@org.junit.Test
public void testLeftJoinFetch(){
String hql = "SELECT DISTINCT d FROM Department d LEFT JOIN FETCH d.emps";
Query query = session.createQuery(hql);
List<Department> departments = query.list();
System.out.println(departments.size());
for (Department d : departments){
System.out.println(d.getId()+" "+d.getEmps().size());
}
}
HQL(迫切)內連接
迫切內連接
-
INNER JOIN FETCH
關鍵字表示迫切內連接,也可以省略INNER關鍵字 -
list()
返回的集合中存放實體類(Department
)對象的引用,每個實體類對象的關聯的集合(Employee
集合)都被初始化,存放所有關聯的Employee
對象。
內連接:
-
INNER JOIN
關鍵字表示內連接,也可以省略INNER關鍵字 -
list()
的集合中存放的每個元素對應查詢結果的一條記錄,每個元素都是對象數組類型 - 如果希望
list()
返回的集合僅僅包含Department
對象,可以在HQL查詢語句中使用SELECT
關鍵字。
//內連接
@org.junit.Test
public void testLeftJoin(){
String hql = "SELECT distinct d FROM Department d INNER JOIN d.emps";
Query query = session.createQuery(hql);
//加上SELECT後
List<Department> res = query.list();
for (Department department : res){
System.out.println(department);
}
//沒加SELECT
// List<Object[]> result = query.list();
// System.out.println(result);
// for (Object[] objects : result){
// System.out.println(Arrays.asList(objects));
// }
}
//迫切內連接
@org.junit.Test
public void testINNERJoinFetch(){
String hql = "SELECT DISTINCT d FROM Department d INNER JOIN FETCH d.emps";
Query query = session.createQuery(hql);
List<Department> departments = query.list();
System.out.println(departments.size());
for (Department d : departments){
System.out.println(d.getId()+" "+d.getEmps().size());
}
}
內連接和左外連接的區別是內連接不會返回左表不符合條件的記錄,而左外連接會返回左表中不符合條件的記錄,比如下面的表中
表的記錄只有DEPT_ID爲1的,而在
departments
表中有兩條記錄:那麼如果用上面的左外連接的代碼查詢
Department
則會查到兩個部門(其中第二個部門爲空),如果用上面的內連接的代碼查詢Department
則會查詢到一個部門。