Hibernate5中HQL檢索方式

HQL(Hibernate Query Language)是面向對象的查詢語言,它和SQL查詢語言有些相似。在Hibernate提供的各種檢索方式中,HQL是使用最廣的一種檢索方式。它具有以下功能:

  • 在查詢語句中設定各種查詢條件。
  • 支持投影查詢,即僅檢索出對象的部分屬性。
  • 支持分頁查詢。
  • 支持分組查詢,允許使用group by和having關鍵字。
  • 提供內置聚集函數,如sum()、min()和max()。
  • 能夠調用用戶定義的SQL函數。
  • 支持子查詢,即嵌套查詢。
  • 支持動態綁定參數。

Session 類的 Qurey 接口支持HQL檢索方式,它提供了以上列出的各種查詢功能。

注:Qurey 接口支持方法鏈編程風格,它的set方法都返回自身實例,而不是返回void類型。方法鏈編程風格能使程序代碼更加簡潔。

一、HQL 查詢的步驟

    寫 HQL 語句注意: 表名與列名 對應 持久化類的類名和字段名,嚴格區分大小寫HQL不支持 * 號,一般使用別名 ,

	@SuppressWarnings("unchecked")
	@Test
	public void test() {
		//1. 寫HQL  
		String hql = "from Activity";  //等價於: String hql = "select a from Activity a";
		//2. 獲取Query<>
		Query<Activity> query = session.createQuery(hql);
		//3. 給動態綁定參數賦值
		//4. 獲取list(查詢)  增刪改 使用executeUpdate()
		List<Activity> list = query.list();
		
		for (Activity activity : list) {
			System.out.println(activity.getAname() +"--"+ activity.getMember().size() );
		}
	}

二. HQL 動態綁定參數查詢

    Activity 與 Member  多對多

1). 使用 ? 做佔位符: Hibernate setParameter() 佔位符參數從 0 開始

	@SuppressWarnings("unchecked")
	@Test
	public void test() {
		//1. 寫HQL
		String hql = "select a from Activity a where a.id<? and a.aname like ?"; 
		//2. 獲取Query<>
		Query<Activity> query = session.createQuery(hql);
		//3. 給動態綁定參數賦值  Hibernate 從0開始
		query.setParameter(0, 5);
		query.setParameter(1, "建%");
		//4. 獲取list
		List<Activity> list = query.list();
		
		for (Activity activity : list) {
			System.out.println(activity.getAname() +"--"+ activity.getMember().size() );
		}
	}

--------

建軍節--2
Hibernate: 
    select
        member0_.activity_id as activity1_1_0_,
        member0_.member_id as member_i2_1_0_,
        member1_.id as id1_2_1_,
        member1_.m_name as m_name2_2_1_ 
    from
        t_activity_member member0_ 
    inner join
        t_member member1_ 
            on member0_.member_id=member1_.id 
    where
        member0_.activity_id=?
建黨節--1

2). 使用 :自定義名 做佔位符: Hibernate setParameter() 佔位符參數 用字符串表示, 推薦使用 可讀性強

	@SuppressWarnings("unchecked")
	@Test
	public void test() {
		//1. 寫HQL
		String hql = "select a from Activity a where a.id<:aid and a.aname like :an"; 
		//2. 獲取Query<>
		Query<Activity> query = session.createQuery(hql);
		//3. 給動態綁定參數賦值  
		query.setParameter("aid", 5);
		query.setParameter("an", "建%");
		//4. 獲取list
		List<Activity> list = query.list();
		
		for (Activity activity : list) {
			System.out.println(activity.getAname() +"--"+ activity.getMember().size() );
		}
	}

 

3. where 篩選參數可以是基本數據類型外,還可以是一個對象(必須有id,否則報錯)

    Teacher 與 Student  一對多

	@SuppressWarnings("unchecked")
	@Test
	public void test() {
				
		//1. 寫HQL
		String hql = "select s from Student s where s.teacher = ?"; 
		//2. 獲取Query<>
		Query<Student> query = session.createQuery(hql);
		//3. 給動態綁定對象參數賦值  
		Teacher teacher = new Teacher();
		teacher.setId(3);
		
		query.setParameter(0, teacher);
		//4. 獲取list
		List<Student> list = query.list();
		
		for (Student Student : list) {
			System.out.println(Student.getSname() +"--"+ Student.getTeacher().getTname());
		}

}


--------

學生3--老師3
學生4--老師3

 

三、HQL 分頁查詢: mysql 和 orcale 都適合。 

分頁查詢主要靠兩個方法來實現:

setFirstResult(index) : 從index這個索引值開始查詢數據庫的記錄,  index 從 0 開始

setMaxResults(數量) : 表示從起始的索引對應的記錄開始, 最多查詢出的記錄數目

	@SuppressWarnings("unchecked")
	@Test
	public void test() {
		int pageNo = 4;
		int pageSize = 5;
		
		String hql = "from Student where id>?";	//可以加條件
		Query<Student> query = session.createQuery(hql);
		query.setParameter(0, 5);
		//使用下 鏈式語法
		query.setFirstResult((pageNo-1)*pageSize).setMaxResults(pageSize);
		
		List<Student> list = query.list();
		//System.out.println(Arrays.toString(list.toArray()));
		for (Student Student : list) {
			System.out.println(Student.getSname());
		}
	}

 

四、只查詢出表中的部分字段

只查詢出表中的部分字段, 在 Hibernate 中叫做投影查詢:

1. 方法一:多字段(大於1)查詢

	@Test
	public void test() {
		String hql = "select s.sname,s.teacher from Student s";
                //返回結果爲 Object[]的List集合
		List<Object[]> list = session.createQuery(hql).list();
		for (Object[] objects : list) {
			System.out.println(Arrays.toString(objects));
		}
	}

--------
[學生1, cn.jq.hibernate5.model.Teacher@5b6e8f77]
[學生2, cn.jq.hibernate5.model.Teacher@5b6e8f77]
[學生3, cn.jq.hibernate5.model.Teacher@6b7d1df8]
[學生4, cn.jq.hibernate5.model.Teacher@6b7d1df8]
[學生5, cn.jq.hibernate5.model.Teacher@3044e9c7]
[學生6, cn.jq.hibernate5.model.Teacher@41d7b27f]
。。。

     方法一投影查詢返回結果是一個 List<Object[]> 類型,這是因爲查詢的字段是多個(大於1),所以是 Object[] 數組,如果我們業務層很多方法都是以對象來傳遞的,Object 類型要強制類型轉換,不好!

2.方法二: 對象查詢

	@Test
	public void test() {
		String hql = "select new Student(s.sname) from Student s";
		// 返回結果是 對用POJO類的List集合, 注意:必須要有對應參數的構造方法
		List<Student> list = session.createQuery(hql).list();
		for (Student student : list) {
			System.out.println(student);
		}
	}

----其他值爲空或默認基本類型值--
Student [id=0, sname=學生1, teacher=null]
Student [id=0, sname=學生2, teacher=null]
Student [id=0, sname=學生3, teacher=null]
Student [id=0, sname=學生4, teacher=null]
。。。

方法二返回結果是一個 List<POJO類> 對象集合類型,(被泛型限定的POJO類),這種方式解決了傳遞 Object[] 方式,只傳遞對象了,且也實現了,只查詢部分屬性。

注意: POJO 類映射對象中必須要有對應參數查詢語句的構造方法

 

五. HQL語句中使用聚合函數

重點內容

函數名 說明
count() 統計選擇對象的記錄條數
sum() 求和
max() 求最大值
min() 求最小值
avg() 計算選擇屬性的平均值
	@SuppressWarnings("unchecked")
	@Test
	public void test() {
		String hql = "select sum(s.id),avg(s.id),max(s.id),min(s.id),count(s.id),s.teacher from Student s group by s.teacher.id having  s.teacher.id < ?";
		//返回結果爲 Object[]的List集合
		List<Object[]> list = session.createQuery(hql).setParameter(0, 6).list();
		for (Object[] object : list) {
			System.out.println(Arrays.toString(object));
		}
	}

--------
[3, 1.5, 2, 1, 2, Teacher [id=1, tname=老師1]]
[7, 3.5, 4, 3, 2, Teacher [id=3, tname=老師3]]
[18, 6.0, 7, 5, 3, Teacher [id=4, tname=老師4]]

結論: HQL 語言和 SQL 語言基本上是相通的,只是 將字段,表對應成了屬性和類名字 即可。

六. HQL連接查詢

HQL提供的連接方式:

迫切連接是指在指定連接方式時不僅指定了連接查詢方式,而且顯式地指定了關聯級別的查詢策略。

Hibernate 使用 fetch 關鍵字實現,fetch 關鍵字表明“左邊”對象用於與“右邊”對象關聯的屬性會立即被初始化

平時項目中都建議使用 fetch !

 

多對一關聯,無迫切連接和迫切連接比較(其他連接類同):

sql 中 表名 inner join 表名

hqlPOJO類名 inner join 關聯 屬性類

	@SuppressWarnings("unchecked")
	@Test
	public void test() {
		String hql = "from Student s inner join s.teacher t where s.id<? and t.id<?";
		//返回結果爲 Object[]的List集合
		List<Object[]> list = session.createQuery(hql).setParameter(0, 6).setParameter(1, 5).list();
		for (Object[] object : list) {
			System.out.println(Arrays.toString(object));
		}
	}


	@SuppressWarnings("unchecked")
	@Test
	public void test() {
		String hql = "from Student s inner join fetch s.teacher t where s.id<? and t.id<?";
		//返回結果爲 POJO類的List集合
		List<Student> list = session.createQuery(hql).setParameter(0, 6).setParameter(1, 5).list();
		for (Student student : list) {
			System.out.println(student);
		}
	}


--------
Student [id=1, sname=學生1, teacher=Teacher [id=1, tname=老師1]]
Student [id=2, sname=學生2, teacher=Teacher [id=1, tname=老師1]]
Student [id=3, sname=學生3, teacher=Teacher [id=3, tname=老師3]]
Student [id=4, sname=學生4, teacher=Teacher [id=3, tname=老師3]]
Student [id=5, sname=學生5, teacher=Teacher [id=4, tname=老師4]]

 

	
	@SuppressWarnings("unchecked")
	@Test
	public void test() {
		String hql = "select s from Student s inner join s.teacher t where s.id<? and t.id<?";
		//返回結果爲 POJO類的List集合, 多條sql語句
		List<Student> list = session.createQuery(hql).setParameter(0, 6).setParameter(1, 5).list();
		for (Student student : list) {
			System.out.println(student);
		}
	}


        @SuppressWarnings("unchecked")
	@Test
	public void test() {
		String hql = "select s from Student s inner join fetch s.teacher t where s.id<? and t.id<?";
		//返回結果爲 POJO類的List集合, 一條sql語句
		List<Student> list = session.createQuery(hql).setParameter(0, 6).setParameter(1, 5).list();
		for (Student student : list) {
			System.out.println(student);
		}
	}



--------

Hibernate: 
    select
        student0_.id as id1_3_0_,
        teacher1_.id as id1_4_1_,
        student0_.sname as sname2_3_0_,
        student0_.teacher_id as teacher_3_3_0_,
        teacher1_.tname as tname2_4_1_ 
    from
        t_student student0_ 
    inner join
        t_teacher teacher1_ 
            on student0_.teacher_id=teacher1_.id 
    where
        student0_.id<? 
        and teacher1_.id<?

使用結論:

      1. 使用 from.... 沒加 fetch 和加了,得到的結果類型是不一樣的,只發起一條 sql .

      2. 使用 select ...  指定查詢對象時, 加 fetch,  只發起一條 sql , 反之發起多條 sql , 結果類型是一樣。

根據發起的 sql 條數越來效率越高的原則,項目開發中, 推薦都加fetch

fetch關鍵字只對 inner join 和 left join 有效。right join 基本也不用.

 

七. HQL批量增刪改

       每個 Hibernate Session 中都維持了一個必選的數據緩存,這個內部緩存正常情況下是由 Hibernate 自動維護的,並且沒有容量限制。在批量插入與更新時,由於每次保存的實體都會保存在Session緩存中,當數據量大的時候,就可能出現OutOfMemoryException(內存溢出異常)。所以批量增加或更新操作中, 應該考慮到控制內部緩存的過度增長而出現OutOfMemeoryError 錯誤。

這裏有兩個方面進行控制:

一. 是在數據保存過程中週期性的對 Session 調用 flush 和 clear 方法,確保 Session 的容量不至於太大

二. 是設置 hibernate.jdbc.batch_size 參數來指定每次提交SQL的數量。

setParameter()     setParameterList()    executeUpdate()

1. 批量插入(不推薦):推薦使用 save()   

	@Test
	public void test() {
		List<Student> list = new ArrayList<Student>();
		for(int i=50; i < 85; i++) {
			Student student = new Student();
				student.setSname("學生" + i);
				list.add(student);
		}
		int row = 10;	//
		for(int i =0; i<list.size(); i++) {
			session.save(list.get(i));
			if(i % row == 0) {
				session.flush();
				session.clear();
			}
		}
	}

Session.flush():1.刷新所有數據;2.執行數據庫SQL完成持久化動作;必須在操作結束且在提交事務和關閉連接之前被調用
Session.clear(): 則是清除 session 中的緩存數據。這樣就達到控制session的一級緩存的大小

2. 批量修改

	@SuppressWarnings("unchecked")
	@Test
	public void test() {
		String hql = "update Student s  set s.sname = ? where s.id > ?";
		int n = session.createQuery(hql).setParameter(0, "李斯").setParameter(1, 15).executeUpdate();
		System.out.println(n);
	}

3. 批量刪除

	@SuppressWarnings("unchecked")
	@Test
	public void test() {
		Integer[] ids = {16,19,10}; //注意Object[]類型
		String hql = "delete from Student s where s.id in (:ids)";
		int n = session.createQuery(hql).setParameterList("ids", ids).executeUpdate();
		System.out.println(n);	}

-------
Hibernate: 
        delete 
    from
        t_student 
    where
        id in (
            ? , ? , ?
        )
3

批量操作總結:

1)優化 HIbernate API , 程序上採取分段插入或更新時 及時調用清除緩存方法。

2)通過  session.doWork()直接操作原生的 JDBC API 來做批量操作,性能更好。它具有以下優點:

     (1) 不會消耗大量內存。因爲它不再將數據庫中的大批量數據先加載到內存中,然後再逐個更新或修改。

     (2) 可以在一條SQL語句中更新或刪除大批量的數據。

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章