1. 程式人生 > >Hibernate - HQL物件檢索詳解

Hibernate - HQL物件檢索詳解

Hibernate 提供了以下幾種檢索物件的方式:

  • 導航物件圖檢索方式: 根據已經載入的物件導航到其他物件
  • OID 檢索方式: 按照物件的 OID 來檢索物件
  • HQL 檢索方式: 使用面向物件的 HQL 查詢語言
  • QBC 檢索方式: 使用 QBC(Query By Criteria) API 來檢索物件。這種 API 封裝了基於字串形式的查詢語句, 提供了更加面向物件的查詢介面。
  • 本地 SQL 檢索方式: 使用本地資料庫的 SQL 查詢語句

【1】HQL檢索

HQL(Hibernate Query Language) 是面向物件的查詢語言, 它和 SQL 查詢語言有些相似。在 Hibernate 提供的各種檢索方式中, HQL 是使用最廣的一種檢索方式。

① 它有如下功能:

  • 在查詢語句中設定各種查詢條件
  • 支援投影查詢, 即僅檢索出物件的部分屬性
  • 支援分頁查詢
  • 支援連線查詢
  • 支援分組查詢, 允許使用 HAVING 和 GROUP BY 關鍵字
  • 提供內建聚集函式, 如 sum(), min() 和 max()
  • 支援子查詢
  • 支援動態繫結引數
  • 能夠呼叫 使用者定義的 SQL 函式或標準的 SQL 函式

② HQL 檢索方式包括以下步驟:

  • 通過 Session 的 createQuery() 方法建立一個 Query 物件, 它包括一個 HQL 查詢語句. HQL 查詢語句中可以包含命名引數
  • 動態繫結引數
  • 呼叫 Query 相關方法執行查詢語句。

Qurey 介面支援方法鏈程式設計風格, 它的 setXxx() 方法返回自身例項, 而不是 void 型別。


③ HQL vs SQL:

HQL 查詢語句是面向物件的, Hibernate 負責解析 HQL 查詢語句, 然後根據物件-關係對映檔案中的對映資訊, 把 HQL 查詢語句翻譯成相應的 SQL 語句。 HQL 查詢語句中的主體是域模型中的類及類的屬性。

SQL 查詢語句是與關係資料庫繫結在一起的。SQL 查詢語句中的主體是資料庫表及表的欄位。


④ 繫結引數

Hibernate 的引數繫結機制依賴於 JDBC API 中的 PreparedStatement 的預定義 SQL 語句功能。

HQL 的引數繫結由兩種形式:

  • 按引數名字繫結: 在 HQL 查詢語句中定義命名引數, 命名引數以“:”開頭
  • 按引數位置繫結: 在 HQL 查詢語句中用 “?”來定義引數位置

相關方法:

  • setEntity(): 把引數與一個持久化類繫結
  • setParameter(): 繫結任意型別的引數。 該方法的第三個引數顯式指定 Hibernate 對映型別

HQL 採用 ORDER BY 關鍵字對查詢結果排序。

這裡以Department:Employee=1:N雙向一對多為例。

Department類如下:

public class Department {

	private Integer id;
	private String name;
	
	private Set<Employee> emps = new HashSet<>();

	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;
	}

	@Override
	public String toString() {
		return "Department [id=" + id + "]";
	}
	
}

Department.hbm.xml如下:

<hibernate-mapping package="com.jane.model">

    <class name="Department" table="GG_DEPARTMENT">
        
        <id name="id" type="java.lang.Integer">
            <column name="ID" />
            <generator class="native" />
        </id>
    
        <property name="name" type="java.lang.String">
            <column name="NAME" />
        </property>
        
        <set name="emps" table="GG_EMPLOYEE" inverse="true" lazy="true">
            <key>
                <column name="DEPT_ID" />
            </key>
            <one-to-many class="Employee" />
        </set>
        
    </class>
</hibernate-mapping>

Employee類如下:

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 + "]";
	}

	public Employee(String email, float salary, Department dept) {
		super();
		this.salary = salary;
		this.email = email;
		this.dept = dept;
	}
	
	public Employee() {
		// TODO Auto-generated constructor stub
	}
	
}

Employee.hbm.xml如下:

<hibernate-mapping package="com.jane.model">

    <class name="Employee" table="GG_EMPLOYEE">
    	
    	<!--  
    	<cache usage="read-write"/>
    	-->
    		
        <id name="id" type="java.lang.Integer">
            <column name="ID" />
            <generator class="native" />
        </id>
    
        <property name="name" type="java.lang.String">
            <column name="NAME" />
        </property>
        
        <property name="salary" type="float">
            <column name="SALARY" />
        </property>
        
        <property name="email" type="java.lang.String">
            <column name="EMAIL" />
        </property>
        
        <many-to-one name="dept" class="Department">
            <column name="DEPT_ID" />
        </many-to-one>
        
    </class>
    
</hibernate-mapping>

測試HQL查詢程式碼如下:

	@Test
	public void testHQL(){
		
		//1. 建立 Query 物件
		//基於位置的引數. 
		String hql = "FROM Employee e WHERE e.salary > ? AND e.email LIKE ? AND e.dept = ? "
				+ "ORDER BY e.salary";
		Query query = session.createQuery(hql);
		
		//2. 繫結引數--位置索引繫結方式
		//Query 物件呼叫 setXxx 方法支援方法鏈的程式設計風格.
		Department dept = new Department();
		dept.setId(1); 
		query.setFloat(0, 3000)
		     .setParameter(1, "%A%",StringType.INSTANCE)
		     .setEntity(2, dept);
		
		//3. 執行查詢
		List<Employee> emps = query.list();
		System.out.println(emps.size());  
	}

測試結果如下:

Hibernate: 
    select
        employee0_.ID as ID1_1_,
        employee0_.NAME as NAME2_1_,
        employee0_.SALARY as SALARY3_1_,
        employee0_.EMAIL as EMAIL4_1_,
        employee0_.DEPT_ID as DEPT_ID5_1_ 
    from
        GG_EMPLOYEE employee0_ 
    where
        employee0_.SALARY>? 
        and (
            employee0_.EMAIL like ?
        ) 
        and employee0_.DEPT_ID=? 
    order by
        employee0_.SALARY
1

使用命名引數進行引數繫結:

	@Test
	public void testHQLNamedParameter(){
		
		//1. 建立 Query 物件
		//基於命名引數. 
		String hql = "FROM Employee e WHERE e.salary > :sal AND e.email LIKE :email";
		Query query = session.createQuery(hql);
		
		//2. 繫結引數
		query.setFloat("sal", 7000)
		     .setString("email", "%A%");
		
		//3. 執行查詢
		List<Employee> emps = query.list();
		System.out.println(emps.size());  
	}

⑤ 分頁查詢

setFirstResult(int firstResult): 設定從哪一個物件開始檢索, 引數 firstResult 表示這個物件在查詢結果中的索引位置, 索引位置的起始值為 0。預設情況下, Query 從查詢結果中的第一個物件開始檢索。

setMaxResults(int maxResults): 設定一次最多檢索出的物件的數目。在預設情況下, Query 和 Criteria 介面檢索出查詢結果中所有的物件。

測試程式碼如下:

	@Test
	public void testPageQuery(){
		String hql = "FROM Employee";
		Query query = session.createQuery(hql);
		
		int pageNo = 1;
		int pageSize = 5;
		
		List<Employee> emps = query.setFirstResult((pageNo - 1) * pageSize)
								     .setMaxResults(pageSize)
								     .list();
		System.out.println(emps);

測試結果如下:

Hibernate: 
    select
        employee0_.ID as ID1_1_,
        employee0_.NAME as NAME2_1_,
        employee0_.SALARY as SALARY3_1_,
        employee0_.EMAIL as EMAIL4_1_,
        employee0_.DEPT_ID as DEPT_ID5_1_ 
    from
        GG_EMPLOYEE employee0_ limit ?
[Employee [id=1], Employee [id=2], Employee [id=3], Employee [id=4], Employee [id=5]]

⑥ 在對映檔案中定義命名查詢語句

Hibernate 允許在對映檔案中定義字串形式的查詢語句。

<query> 元素用於定義一個 HQL 查詢語句, 它和 <class>元素並列。

在Employee.hbm.xml中新增query節點如下:

 <query name="salaryEmps">
	<![CDATA[FROM Employee e WHERE e.salary > :minSal AND e.salary < :maxSal]]>
 </query>

在程式中通過 Session 的 getNamedQuery() 方法獲取查詢語句對應的 Query 物件:

	@Test
	public void testNamedQuery(){
		Query query = session.getNamedQuery("salaryEmps");
		
		List<Employee> emps = query.setFloat("minSal", 5000)
				                   .setFloat("maxSal", 10000)
				                   .list();
		
		System.out.println(emps.size()); 
	}

測試結果如下:

Hibernate: 
    select
        employee0_.ID as ID1_1_,
        employee0_.NAME as NAME2_1_,
        employee0_.SALARY as SALARY3_1_,
        employee0_.EMAIL as EMAIL4_1_,
        employee0_.DEPT_ID as DEPT_ID5_1_ 
    from
        GG_EMPLOYEE employee0_ 
    where
        employee0_.SALARY>? 
        and employee0_.SALARY<?
7


⑦ 投影查詢

投影查詢,查詢結果僅包含實體的部分屬性, 通過 SELECT 關鍵字實現。

Query 的 list() 方法返回的集合中包含的是陣列型別的元素, 每個物件陣列代表查詢結果的一條記錄。

測試程式碼一如下:

	@Test
	public void testFieldQuery(){
		String hql = "SELECT e.email, e.salary, e.dept FROM Employee e WHERE e.dept = :dept";
		Query query = session.createQuery(hql);
		
		Department dept = new Department();
		dept.setId(1);
		List<Object[]> result = query.setEntity("dept", dept).list();
		
		for(Object [] objs: result){
			System.out.println(Arrays.asList(objs));
		}
	}

測試一結果如下:

Hibernate: 
    select
        employee0_.EMAIL as col_0_0_,
        employee0_.SALARY as col_1_0_,
        employee0_.DEPT_ID as col_2_0_,
        department1_.ID as ID1_0_,
        department1_.NAME as NAME2_0_ 
    from
        GG_EMPLOYEE employee0_ 
    inner join
        GG_DEPARTMENT department1_ 
            on employee0_.DEPT_ID=department1_.ID 
    where
        employee0_.DEPT_ID=?
[[email protected], 5000.0, Department [id=1]]
[[email protected], 6000.0, Department [id=1]]

可以在持久化類中定義一個物件的構造器來包裝投影查詢返回的記錄, 使程式程式碼能完全運用面向物件的語義來訪問查詢結果集。

測試程式碼二如下:

	@Test
	public void testFieldQuery2(){
		//實體類Employee需要有該構造器
		String hql = "SELECT new Employee(e.email, e.salary, e.dept) "
				+ "FROM Employee e "
				+ "WHERE e.dept = :dept";
		Query query = session.createQuery(hql);
		
		Department dept = new Department();
		dept.setId(1);
		List<Employee> result = query.setEntity("dept", dept).list();
		
		for(Employee emp: result){
			System.out.println(emp.getId() + ", " + emp.getEmail() 
					+ ", " + emp.getSalary() + ", " + emp.getDept());
		}
	}

測試二結果如下:

Hibernate: 
    select
        employee0_.EMAIL as col_0_0_,
        employee0_.SALARY as col_1_0_,
        employee0_.DEPT_ID as col_2_0_ 
    from
        GG_EMPLOYEE employee0_ 
    inner join //內連線
        GG_DEPARTMENT department1_ 
            on employee0_.DEPT_ID=department1_.ID 
    where
        employee0_.DEPT_ID=?
Hibernate: 
    select
        department0_.ID as ID1_0_0_,
        department0_.NAME as NAME2_0_0_ 
    from
        GG_DEPARTMENT department0_ 
    where
        department0_.ID=?
null, [email protected], 5000.0, Department [id=1]
null, [email protected], 6000.0, Department [id=1]

另外,可以通過 DISTINCT 關鍵字來保證查詢結果不會返回重複元素。


⑧ 報表查詢

報表查詢用於對資料分組和統計, 與 SQL 一樣, HQL 利用 GROUP BY 關鍵字對資料分組, 用 HAVING 關鍵字對分組資料設定約束條件,過濾分組資料。

在 HQL 查詢語句中可以呼叫以下聚集函式:

count()
min()
max()
sum()
avg()

測試程式碼如下:

@Test
	public void testGroupBy(){
		String hql = "SELECT min(e.salary), max(e.salary) "
				+ "FROM Employee e "
				+ "GROUP BY e.dept "
				+ "HAVING min(salary) > :minSal";
		
		Query query = session.createQuery(hql)
				             .setFloat("minSal", 5000);
		
		List<Object []> result = query.list();
		for(Object [] objs: result){
			System.out.println(Arrays.asList(objs));
		}
	}

測試結果如下:

Hibernate: 
    select
        min(employee0_.SALARY) as col_0_0_,
        max(employee0_.SALARY) as col_1_0_ 
    from
        GG_EMPLOYEE employee0_ 
    group by
        employee0_.DEPT_ID 
    having
        min(employee0_.SALARY)>?
[7000.0, 8000.0]
[9000.0, 10000.0]
[7000.0, 8000.0]

⑨ 左外連線和迫切左外連線

左外連線:

  • LEFT JOIN 關鍵字表示左外連線查詢
  • list() 方法返回的集合中存放的是物件陣列型別(list<Object[]>)
  • 根據配置檔案來決定 Employee 集合的檢索策略
  • 如果希望 list() 方法返回的集合中僅包含 Department 物件, 可以在HQL 查詢語句中使用 SELECT 關鍵字

使用select關鍵字測試程式碼如下:

	@Test
	public void testLeftJoinFetch(){
		String hql = "SELECT DISTINCT d FROM Department d LEFT JOIN  d.emps";
		Query query = session.createQuery(hql);
		
		List<Department> depts = query.list();
		depts = new ArrayList<>(new LinkedHashSet(depts));
		System.out.println(depts.size()); 
		
		for(Department dept: depts){
			System.out.println(dept.getName() + "-" + dept.getEmps().size());
		}
	}

測試結果如下:

Hibernate: 
    select
        distinct department0_.ID as ID1_0_,
        department0_.NAME as NAME2_0_ 
    from
        GG_DEPARTMENT department0_ 
    left outer join
        GG_EMPLOYEE emps1_ 
            on department0_.ID=emps1_.DEPT_ID
14
Hibernate: 
    select
        emps0_.DEPT_ID as DEPT_ID5_1_0_,
        emps0_.ID as ID1_1_0_,
        emps0_.ID as ID1_1_1_,
        emps0_.NAME as NAME2_1_1_,
        emps0_.SALARY as SALARY3_1_1_,
        emps0_.EMAIL as EMAIL4_1_1_,
        emps0_.DEPT_ID as DEPT_ID5_1_1_ 
    from
        GG_EMPLOYEE emps0_ 
    where
        emps0_.DEPT_ID=?
AA-2 //使用的時候再發送查詢語句,SQL語句數目比較多
Hibernate: 
    select
        emps0_.DEPT_ID as DEPT_ID5_1_0_,
        emps0_.ID as ID1_1_0_,
        emps0_.ID as ID1_1_1_,
        emps0_.NAME as NAME2_1_1_,
        emps0_.SALARY as SALARY3_1_1_,
        emps0_.EMAIL as EMAIL4_1_1_,
        emps0_.DEPT_ID as DEPT_ID5_1_1_ 
    from
        GG_EMPLOYEE emps0_ 
    where
        emps0_.DEPT_ID=?
BB-2
//...

不適用select關鍵字測試如下:

	@Test
	public void testLeftJoin(){
		String hql = " FROM Department d LEFT JOIN  d.emps";
		Query query = session.createQuery(hql);
		

		List<Object []> result = query.list(); 
		result = new ArrayList<>(new LinkedHashSet<>(result));
		System.out.println(result); 
		
		for(Object [] objs: result){
			System.out.println(Arrays.asList(objs)+objs[1].toString());
		}
	}

測試結果如下:

Hibernate: 
    select
        department0_.ID as ID1_0_0_,
        emps1_.ID as ID1_1_1_,
        department0_.NAME as NAME2_0_0_,
        emps1_.NAME as NAME2_1_1_,
        emps1_.SALARY as SALARY3_1_1_,
        emps1_.EMAIL as EMAIL4_1_1_,
        emps1_.DEPT_ID as DEPT_ID5_1_1_ 
    from
        GG_DEPARTMENT department0_ 
    left outer join
        GG_EMPLOYEE emps1_ 
            on department0_.ID=emps1_.DEPT_ID
[[Ljava.lang.Object;@1763992e, [Ljava.lang.Object;@5c92166b, [Ljava.lang.Object;@659925f4, [Ljava.lang.Object;@4cd1c1dc, [Ljava.lang.Object;@47f08b81, [Ljava.lang.Object;@b9dfc5a, [Ljava.lang.Object;@2787de58, [Ljava.lang.Object;@659a2455, [Ljava.lang.Object;@267517e4, [Ljava.lang.Object;@426e505c, [Ljava.lang.Object;@5b022357, [Ljava.lang.Object;@6f8e0cee, [Ljava.lang.Object;@614aeccc, [Ljava.lang.Object;@5116ac09, [Ljava.lang.Object;@1bc425e7, [Ljava.lang.Object;@4b2a30d, [Ljava.lang.Object;@322803db, [Ljava.lang.Object;@56ba8773, [Ljava.lang.Object;@6ceb7b5e]
[Department [id=1], Employee [id=1]]Employee [id=1]
[Department [id=1], Employee [id=2]]Employee [id=2]
[Department [id=2], Employee [id=3]]Employee [id=3]
[Department [id=2], Employee [id=4]]Employee [id=4]
[Department [id=3], Employee [id=5]]Employee [id=5]
[Department [id=3], Employee [id=6]]Employee [id=6]
[Department [id=4], Employee [id=7]]Employee [id=7]
[Department [id=4], Employee [id=8]]Employee [id=8]
[Department [id=5], Employee [id=9]]Employee [id=9]
[Department [id=5], Employee [id=10]]Employee [id=10]

會將Department和Employee一併查詢出來,但是返回的是list<Object[]>型別。


迫切左外連線:

  • LEFT JOIN FETCH 關鍵字表示迫切左外連線檢索策略
  • list() 方法返回的集合中存放實體物件的引用(list<Departmet>), 每個 Department 物件關聯的 Employee 集合都被初始化, 存放所有關聯的 Employee 的實體物件.
  • 查詢結果中可能會包含重複元素, 可以通過一個 HashSet 來過濾重複元素

使用select關鍵字測試程式碼如下:

	@Test
	public void testLeftJoinFetch(){
		String hql = "SELECT DISTINCT d FROM Department d LEFT JOIN FETCH d.emps";
		Query query = session.createQuery(hql);
		
		List<Department> depts = query.list();
		depts = new ArrayList<>(new LinkedHashSet(depts));
		System.out.println(depts.size()); 
		
		for(Department dept: depts){
			System.out.println(dept.getName() + "-" + dept.getEmps().size());
		}
	}

測試結果如下:

Hibernate: 
    select
        distinct department0_.ID as ID1_0_0_,
        emps1_.ID as ID1_1_1_,
        department0_.NAME as NAME2_0_0_,
        emps1_.NAME as NAME2_1_1_,
        emps1_.SALARY as SALARY3_1_1_,
        emps1_.EMAIL as EMAIL4_1_1_,
        emps1_.DEPT_ID as DEPT_ID5_1_1_,
        emps1_.DEPT_ID as DEPT_ID5_1_0__,
        emps1_.ID as ID1_1_0__ 
    from
        GG_DEPARTMENT department0_ 
    left outer join //使用左外連線一併查出相關聯的實體物件
        GG_EMPLOYEE emps1_ 
            on department0_.ID=emps1_.DEPT_ID
14
AA-2
BB-2
CC-2
DD-2
EE-2
FF-0
GG-0
HH-0
II-0
JJ-0
KK-0
LL-0
MM-0
NN-0

不使用select關鍵字測試如下:

	@Test
	public void testLeftJoin(){
		String hql = " FROM Department d LEFT JOIN fetch d.emps";
		Query query = session.createQuery(hql);
		
		List<Department> depts = query.list();
		System.out.println(depts.size());
		
		for(Department dept: depts){
			System.out.println(dept.getName() + ", " + dept.getEmps().size()); 
		}
		

測試結果如下:

Hibernate: 
    select
        department0_.ID as ID1_0_0_,
        emps1_.ID as ID1_1_1_,
        department0_.NAME as NAME2_0_0_,
        emps1_.NAME as NAME2_1_1_,
        emps1_.SALARY as SALARY3_1_1_,
        emps1_.EMAIL as EMAIL4_1_1_,
        emps1_.DEPT_ID as DEPT_ID5_1_1_,
        emps1_.DEPT_ID as DEPT_ID5_1_0__,
        emps1_.ID as ID1_1_0__ 
    from
        GG_DEPARTMENT department0_ 
    left outer join
        GG_EMPLOYEE emps1_ 
            on department0_.ID=emps1_.DEPT_ID
19
AA, 2
AA, 2
BB, 2
BB, 2
CC, 2
CC, 2
DD, 2
DD, 2
EE, 2
EE, 2
FF, 0
GG, 0
HH, 0
II, 0
JJ, 0
KK, 0
LL, 0
MM, 0
NN, 0

會將Department和Employee一併查詢出來,但是返回的是list<Department>型別。


⑩ HQL (迫切)內連線

內連線:

  • INNER JOIN 關鍵字表示內連線, 也可以省略 INNER 關鍵字
  • list() 方法的集合中存放的每個元素對應查詢結果的一條記錄, 每個元素都是物件陣列型別
  • 如果希望 list() 方法的返回的集合僅包含 Department 物件, 可以在 HQL 查詢語句中使用 SELECT 關鍵字

測試程式碼如下:

	@Test
	public void testInnerJoin(){
		String hql = "FROM Department d INNER JOIN  d.emps";
//		String hql = "FROM Department d INNER JOIN FETCH d.emps";
		Query query = session.createQuery(hql);
		
		List<Object []> result = query.list(); 
		result = new ArrayList<>(new LinkedHashSet<>(result));
		System.out.println(result); 
		
		for(Object [] objs: result){
			System.out.println(Arrays.asList(objs));
		}
	}

測試結果如下:

Hibernate: 
    select
        department0_.ID as ID1_0_0_,
        emps1_.ID as ID1_1_1_,
        department0_.NAME as NAME2_0_0_,
        emps1_.NAME as NAME2_1_1_,
        emps1_.SALARY as SALARY3_1_1_,
        emps1_.EMAIL as EMAIL4_1_1_,
        emps1_.DEPT_ID as DEPT_ID5_1_1_ 
    from
        GG_DEPARTMENT department0_ 
    inner join
        GG_EMPLOYEE emps1_ 
            on department0_.ID=emps1_.DEPT_ID
[[Ljava.lang.Object;@57fae983, [Ljava.lang.Object;@4a29f290, [Ljava.lang.Object;@4bee18dc, [Ljava.lang.Object;@44f3fe83, [Ljava.lang.Object;@44c5a16f, [Ljava.lang.Object;@417d6615, [Ljava.lang.Object;@7a6ebe1e, [Ljava.lang.Object;@21325036, [Ljava.lang.Object;@489543a6, [Ljava.lang.Object;@6272c96f]
[Department [id=1], Employee [id=1]]
[Department [id=1], Employee [id=2]]
[Department [id=2], Employee [id=3]]
[Department [id=2], Employee [id=4]]
[Department [id=3], Employee [id=5]]
[Department [id=3], Employee [id=6]]
[Department [id=4], Employee [id=7]]
[Department [id=4], Employee [id=8]]
[Department [id=5], Employee [id=9]]
[Department [id=5], Employee [id=10]]


迫切內連線:

  • INNER JOIN FETCH 關鍵字表示迫切內連線, 也可以省略 INNER 關鍵字
  • list() 方法返回的集合中存放 Department 物件的引用, 每個 Department 物件的 Employee 集合都被初始化, 存放所有關聯的 Employee 物件

測試程式碼如下:

	@Test
	public void testInnerJoin(){
		String hql = "FROM Department d INNER JOIN FETCH d.emps";
		Query query = session.createQuery(hql);
		
		List<Department> depts = query.list();
		System.out.println(depts.size());
		
		for(Department dept: depts){
			System.out.println(dept.getName() + ", " + dept.getEmps().size()); 
		}
	}

測試結果如下:

Hibernate: 
    select
        department0_.ID as ID1_0_0_,
        emps1_.ID as ID1_1_1_,
        department0_.NAME as NAME2_0_0_,
        emps1_.NAME as NAME2_1_1_,
        emps1_.SALARY as SALARY3_1_1_,
        emps1_.EMAIL as EMAIL4_1_1_,
        emps1_.DEPT_ID as DEPT_ID5_1_1_,
        emps1_.DEPT_ID as DEPT_ID5_1_0__,
        emps1_.ID as ID1_1_0__ 
    from
        GG_DEPARTMENT department0_ 
    inner join
        GG_EMPLOYEE emps1_ 
            on department0_.ID=emps1_.DEPT_ID
10
AA, 2
AA, 2
BB, 2
BB, 2
CC, 2
CC, 2
DD, 2
DD, 2
EE, 2
EE, 2

對比可以發現,inner join和inner join fetch傳送的SQL查詢一樣,差別在返回的接收物件型別。前者為list<Object[]>,後者為list<Department>

另外,這裡均使用的為“From”語句,並未使用select關鍵字。如果使用了select關鍵字,查出來的結果為list<Department>,但是SQL語句也會有變化。

測試一程式碼如下:

	@Test
	public void testInnerJoin(){
		String hql = "select d FROM Department d INNER JOIN  d.emps";
		Query query = session.createQuery(hql);
		
		List<Department> depts = query.list();
		System.out.println(depts.size());
		
		for(Department dept: depts){
			System.out.println(dept.getName() + ", " + dept.getEmps().size()); 
		}
	}

測試結果如下:

Hibernate: 
    select
        department0_.ID as ID1_0_,
        department0_.NAME as NAME2_0_ 
    from
        GG_DEPARTMENT department0_ 
    inner join
        GG_EMPLOYEE emps1_ 
            on department0_.ID=emps1_.DEPT_ID
10
Hibernate:  // 使用的時候再次傳送SQL語句進行查詢
    select
        emps0_.DEPT_ID as DEPT_ID5_1_0_,
        emps0_.ID as ID1_1_0_,
        emps0_.ID as ID1_1_1_,
        emps0_.NAME as NAME2_1_1_,
        emps0_.SALARY as SALARY3_1_1_,
        emps0_.EMAIL as EMAIL4_1_1_,
        emps0_.DEPT_ID as DEPT_ID5_1_1_ 
    from
        GG_EMPLOYEE emps0_ 
    where
        emps0_.DEPT_ID=?
AA, 2
AA, 2
//...

而inner join fetch則無論是否使用select關鍵字,都會將關聯的實體一併查詢出來。

測試二程式碼如下:

	@Test
	public void testInnerJoin(){
		String hql = "select d FROM Department d INNER JOIN fetch  d.emps";
		Query query = session.createQuery(hql);

		List<Department> depts = query.list();
		System.out.println(depts.size());
		
		for(Department dept: depts){
			System.out.println(dept.getName() + ", " + dept.getEmps().size()); 
		}
	}

測試二結果如下:

Hibernate: 
    select
        department0_.ID as ID1_0_0_,
        emps1_.ID as ID1_1_1_,
        department0_.NAME as NAME2_0_0_,
        emps1_.NAME as NAME2_1_1_,
        emps1_.SALARY as SALARY3_1_1_,
        emps1_.EMAIL as EMAIL4_1_1_,
        emps1_.DEPT_ID as DEPT_ID5_1_1_,
        emps1_.DEPT_ID as DEPT_ID5_1_0__,
        emps1_.ID as ID1_1_0__ 
    from
        GG_DEPARTMENT department0_ 
    inner join
        GG_EMPLOYEE emps1_ 
            on department0_.ID=emps1_.DEPT_ID
10
AA, 2
AA, 2
BB, 2
BB, 2
CC, 2
CC, 2
DD, 2
DD, 2
EE, 2
EE, 2

(11)關聯級別執行時的檢索策略

如果在 HQL 中沒有顯式指定檢索策略, 將使用對映檔案配置的檢索策略。

HQL 會忽略對映檔案中設定的迫切左外連線檢索策略, 如果希望 HQL 採用迫切左外連線策略, 就必須在 HQL 查詢語句中顯式的指定它。

若在 HQL 程式碼中顯式指定了檢索策略, 就會覆蓋對映檔案中配置的檢索策略。

具體參考博文:Hibernate檢索策略詳解