Hibernate 5.3(七)

Hibernate 查詢

Hibernate提供異常強大的查詢體系,使用Hibernate 有多種查詢方式可以選擇:既可以使用Hibermate的HQL查詢,也可以使用條件查詢,甚至可以使用原生的SQL查詢語句。不僅如此,Hibernate還提供了一種數據過濾功能,這些都用於篩選目標數據。

HQL 查詢

HQL 簡介

HQL是Hibernate Query Language 的縮寫,HQL的語法很像SQL的語法,但HQL是一種面向對象的查詢語言。SQL的操作對象是數據表、列等數據庫對象,而HQL的操作對象是類、實例、屬性等。

HQL是完全面向對象的查詢語言,因此可以支持繼承、多態等特性。

HQL語句本身是不區分大小寫的。也就是說,HQL語句的關鍵字、函數都是不區分大小寫的。但HQL語句中所使用的包名、類名、實例名、屬性名都區分大小寫。

HQL 查詢步驟

  1. HQL查詢依賴於Query類,每個Query實例對應一個查詢對象。
  2. 獲取Hibernate Session對象。
  3. 編寫HQL語句。
  4. 以HQL語句作爲參數,調用Session的createQuery方法創建查詢對象。
  5. 如果HQL語句包含參數,則調用Query的setXxx方法爲參數賦值。
  6. 調用Query對象的list等方法返回杳詢結果列表(持久化實體集)。
HQL 牛刀小試

在這我給出映射文件,持久化比較簡單,就不給出了,自己根據映射文件,去創建相應的持久化類。

Person.hbm.xml:

<hibernate-mapping package="com.example.test.bean">
	<class name="Person" table="PERSON" dynamic-insert="true"
		dynamic-update="false" >
		<id name="id" type="integer" column="person_id">
			  <generator class="identity"></generator>
		</id>
		<property name="name" type="string" column="person_name"></property>
		<property name="gender" type="string" column="person_gender"></property>
		<set name="events" table="person_event"><!-- 多對多的關係 連接表 -->
		   <key column="person_id"></key><!-- 外鍵列 -->
		   <many-to-many class="Event" column="event_id"></many-to-many><!-- 外鍵列 -->
		</set>
	</class>
</hibernate-mapping>

<hibernate-mapping package="com.example.test.bean">
<class name="Event" table="event_info">
     <id name="event_id" column="event_id">
     <generator class="identity">
     </generator>
     </id>
     <property name="event_date" type="date" column="event_date"></property>
     <property name="event_title" type="string" column="event_title"></property>
     <set name="persons" table="person_event">
        <key column="event_id"></key><!-- 外鍵列   -->
        <many-to-many class="Person" column="person_id"></many-to-many><!-- 外鍵列 -->
     </set>
</class>
</hibernate-mapping>
		List list = ss
				.createQuery(
						"select p from   Person as p join p.events where event_title =:event_title1")
				.setString("event_title1", "問問我").list();
		java.util.Iterator<Person> it = list.iterator();
		while (it.hasNext()) {

			System.out.println(it.next().getName());
		}
		java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat(
				"yyyy-MM-dd");
		Date d = null;
		Date d1 = null;
		try {
			d = sdf.parse("2018-09-16");
			d1 = sdf.parse("2018-09-30");

		} catch (ParseException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		List list = ss
				.createQuery(
						"select distinct p from Person as p join p.events event where event.event_date between :date1 and :date2 ")
				.setDate("date1", d).setDate("date2", d1).list();
		java.util.Iterator<Person> it = list.iterator();
		while (it.hasNext()) {

			System.out.println(it.next().getName());
		}

		List list = ss
				.createQuery(
						"select distinct p.id,p.name,p.gender from Person p join p.events")
				.list();
		java.util.Iterator<Object> it = list.iterator();
		while (it.hasNext()) {
			Object[] obj = (Object[]) it.next();
			System.out.println(java.util.Arrays.toString(obj));
		}

這裏只是帶你體驗一把HQL 語句的使用,數據庫的數據,自己隨便準備,下面纔是真正的講解,裏面有不懂的,繼續向下看即可。

HQL 查詢的where 子句

from是最簡單的HQL語句,也是最基本的HQL語句。from關鍵字後緊跟持久化類的類名。

select distinct p.id,p.name,p.gender from Person p join p.events

大部分時候,推薦爲持久化類的每個實例起別名。既然是實例的別名,命名也符合Java的命名規則:

第一個單詞的首字母小寫,後面每個單詞的首字母大寫。

關聯和連接

當程序需要從多個數據表中取得數據時,SQL語句將會考慮使用多表連接查詢。Hibemate使用關聯映射來處理底層數據表之間的連接,一旦我們提供了正確的關聯映射後,當程序通過Hiberate進行持久化訪問時,將可利用Hibernate的關聯來進行連接。

HQL支持兩種關聯連接(join) 形式:

  1. 隱式(implicit)

隱式連接形式不使用join關鍵字,使用英文點號(.) 來隱式連接關聯實體,而Hibernate底層將自動進行關聯查詢。

select p from Person as p  where p.events.event_title =:event_title1//這裏實際上查看持久化類events 是一個集合,實際上,就是通過它知道映射關係,底層會自動關聯,集合裏面都是Event 對象,拿到event_title  屬性的。
  1. 顯式(explicit)
select p from Person as p join p.events where event_title =:event_title1

使用顯式連接時可以爲相關聯的實體,甚至是關聯集合中的全部元素指定一個 別名。

select p from Person as p join p.events as event where event.event_title =:event_title1

使用顯式連接時,還可通過HQL的with關鍵字來提供額外的連接條件。

select p from Person as p join p.events  with p.id >event.event_id  where event_title =:event_title1 

數據庫具體到底使用哪種連接方法,則取決於HQL語句的顯式連接使用了哪種連接方式。

select p from Person as p   where p.events.event_title =:event_title1

如果events是普通組件屬性,或單個的關聯實體,則Hibernate會自動生成隱式內連接,上面 HQL語句依然有效。
如果events是一個集合(包括1-N、N- N關聯),那麼系統將會出現QueryException異常,異常提示信息爲: ilegal attempt to dereference collection。必須顯式喲紀念館join 關鍵字。

隱式連接和顯式連接查詢後返回的結果不同。

當HQL語句中省略select關鍵字時,使用隱式連接查詢返回的結果是多個被查詢實體組成的集合。

from Person as p  join p.events event//顯式查詢

當使用顯式連接查詢的HQL語句中省略select關鍵字時,返回的結果也是集合,但集合元素是被查詢持久化對象、所有被關聯的持久化對象所組成的數組。上面的HQL語句,查詢到的結果集的每條記錄既包含了Person實體的全部屬性,也包含了Event 實體的全部屬性。屬於Person的屬性創建Person對象,屬於Event的屬性創建Event對象,每一條記錄裏面都包含一個person對象和Event對象,每一條記錄對應一個數組,最後將每一個記錄對應成一個集合的元素。

在這裏插入圖片描述

如果你只想得到某一個實體得到的集合,並且不重複。

select distinct p from Person p join p.events

這樣得到的實體,就只是Person 實體,並且通過distinct 關鍵字來保證關聯查詢的數據,不重複。

HQL 結果返回

List list = ss.createQuery("select distinct p from Person p join p.events").list();
		   tt.commit();
		   ss.close();
		   sf.close();
		   java.util.Iterator<Person> it = list.iterator();
		  while (it.hasNext()) {
			Person p = (Person) it.next();
			System.out.println(p.getName());
		}

我們之前用hiberante 查詢,一旦查詢的session 關閉,就無法在查詢結果,但是使用的HQL 查詢,查詢之後返回的Query 是把結果放在集合對象中,所以,你關閉session對象,還可以通過該對象去訪問,至於該對象回收,那就是Java 的垃圾回收器的問題。雖然上述,我們不習慣在使用關閉session,但是在這你要知道,即使關閉,你也可以使用。

HQL 查詢fetch

我們之前hibernate 查詢默認都是懶加載,就是關聯的對象和集合屬性屬性無法直接加載,之前我們可以在配置文件中設置hibernate lazy 進行設置,我們同樣可以在HQL 語句中進行設置。

List list = ss.createQuery("select distinct p from Person p join fetch p.events").list();
		java.util.Iterator<Person> it = list.iterator();
		while (it.hasNext()) {
			Person p = (Person) it.next();
			System.out.println(p.getName());
			Set<Event> set = p.getEvents();
			for(Event e:set){
				System.out.println(e.getEvent_title());
				//Exceptopn:failed to lazily initialize a collection of role
			}
		}

上述如果沒有訪問,沒有fetch,則會報出異常。

使用fetch關鍵字時有如下幾個注意點:

  • fetch不應該與setMaxResults()或setFirstResult()(這兩個屬性是用來分頁顯示查詢的)共用。因爲這些操作是基於結果集的,無法預先知道精確的行數。fetch 是將查詢的實體關聯的對象,都查詢出來。

fetch不能與獨立的with條件一起使用。因爲fetch 查詢出來的是關聯實體所有結果,with 是在結果的篩選條件,兩者衝突。

如果在映射文件映射屬性時通過指定lazy= "true"啓用了延遲加載(這種延遲加載是通過字節碼增強來實現的),然後程序裏又希望預加載那些原本應延遲加載的屬性,則可以通過fetch all properties來強制Hibermate立即抓取這些屬性。

HQL 查詢select 子句

select可以選擇持久化類的直接屬性,還可以選擇組件屬性包含的屬性以及持久化實例(就是該持久化類的所有屬性),當然select選擇的必須是屬於from後面持久化類。

在通常情況下,使用select 子句查詢的結果是集合,而集合元素就是select 後的實例、屬性等組成的數組。

即使select後的列表項選出某個持久化類的全部屬性,這些屬性依然是屬性,Hibernate不會將這些屬性封裝成對象。只有在select後的列表裏給出持久化類的別名(其實就是實例名), Hibernate纔會將該項封裝成一個持久化實體。

select p from Person p 

這時需要注意雖然查詢該實例的所有屬性,但是無法訪問集合元素,如果強制訪問,會報failed to lazily initialize a collection of role,也就是上面說的懶加載。

select支持將選擇出的屬性存入一個List對象
  List list = ss.createQuery("select  new list(p.name ,p.id) from Person p ").list();
		   tt.commit();
		   ss.close();
		   sf.close();
		java.util.Iterator<Object> it = list.iterator();
		while (it.hasNext()) {
			List list1 = (List) it.next();//正常的查詢結果是封裝在list,如果裏面有多個屬性,那麼
			//是封裝成數組,存放在list中;而這裏對於多個屬性,則是又封裝在list中,存放在外面的list中。
			System.out.println("姓名"+list1.get(0));
			System.out.println("id"+list1.get(1));
		}
select支持將選擇出的屬性存入一個Map對象
List list = ss.createQuery("select new map(p.name as personname) from Person p  ").list();
		   //這裏給select 後面查詢的設置別名,一般該別名和map 使用,作爲map 對象的鍵,而實際查詢
		   //查詢出來的值是作爲map的value。
		   tt.commit();
		   ss.close();
		   sf.close();
		java.util.Iterator<Map<String,String>> it = list.iterator();
		while(it.hasNext()){
			Map p = (Map) it.next();//這裏每次產生都是一個新的map 對象,所以用一個key,沒有問題
			System.out.println(p.get("personname"));
		}
select支持將選擇出的屬性存入一個對象
public class QueryEntity {
	private Integer id;
	private String name;
	
	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 QueryEntity(Integer id, String name) {
		super();
		this.id = id;
		this.name = name;
	}
}

這個需要提供對應類型的構造函數,必須一致。

 //這裏必須指定對象包路徑,不然後找不到實體類
		   List list = ss.createQuery("select new com.example.test.bean.QueryEntity(p.id,p.name) from Person p  ").list();
		   tt.commit();
		   ss.close();
		   sf.close();
		   java.util.Iterator<QueryEntity> it = list.iterator();
		   while (it.hasNext()) {
			QueryEntity qe = (QueryEntity) it.next();
			System.out.println("姓名"+qe.getName());
			System.out.println("id"+qe.getId());
		}

HQL 聚集函數

  • avg: 計算屬性平均值。
  • count: 統計選擇對象的數量。
  • max:統計某個屬性值的最大值。
  • min: 統計某個屬性值的最小值。
  • sum: 計算某個屬性值的總和。
List list = ss.createQuery("select count(*) from Person p").list();//返回查詢的結果總條數
		   tt.commit();
		   ss.close();
		   sf.close();
		   java.util.Iterator<QueryEntity> it = list.iterator();
		   while (it.hasNext()) {
			System.out.println(it.next());
		}

多態查詢

HQL支持在from子句中指定任何Java 類或接口,查詢會返回繼承了該類的所有持久化子類的實例。前提,你需要在映射文件進行正確的繼承映射。

where 查詢子句

Person 定義一個組件屬性

private Name name1; 
List list = ss.createQuery("select p.name1.first from Person p").list();//雖然組件屬性在數據庫表中,是在person表中,但是不可以直接p.first 
//必須直接從Person 的屬性訪問複合屬性,在訪問複合屬性的屬性。
		   tt.commit();
		   ss.close();
		   sf.close();
		   java.util.Iterator<String> it = list.iterator();
		   while (it.hasNext()) {
			System.out.println(it.next());
		   }

我們可以通過點號來隱式查詢多張數據庫的表,前提不是集合屬性,只要該類持有關聯類的對象

(select phone_name from Phone p where p.person.name=:name1 ").setParameter("name1","laoqiang").list()

在該HQL中,Phone 類持有Person 對象,可以直接關聯查詢,如果是多個表中都存在這樣的關係,會關聯多個表查詢。而之前我們說的隱式關聯,都是通過集合去關聯查詢,其實都一樣,只是出發點不同而已。

HQL 中= 可以用來用給屬性賦值,同時還可以比較實例。這裏有人,肯定會想一張表就是一個實例,每次查詢的都是一張表,怎麼可能是多個實例,那問題簡單,我們就給它多張表看看(注意這裏的多張,不是關聯的啦)

 from Phone p,Person p1//注意這種方式,不是查詢多個表。

我們執行該HQL,發現數據庫執行的語句是一條cross join ,是笛卡爾積,將兩個相乘起來。

在where 子句中,id可以表示一個對象的標識符,也就是主鍵,比較方便。(這裏也可以使用對象的主鍵屬性,兩者選擇其一)

(" select p.number from Phone p where p.id =:id1").setParameter("id1",2).list()

如果你是通過id 去引用主鍵,那麼如何引用組件主鍵呢?

一定不要忘記組件類作爲主鍵,該類需要實現序列化接口,同時必須重寫屬性的hashcode和equals。

               <composite-id class="PersonId" name="personId"><!--用來定義組件主鍵 ,class 用來指定用PersonId 類生成組件,name 是屬性值 -->
		<key-property name="a" column="a" type="integer">
		</key-property>
		<key-property name="b" column="b" type="integer">
		</key-property>
		</composite-id>
(" select p.name from Person p where p.id.a =:a1").setParameter("a1",1).list();

在映射表具有繼承關係的時候,我們一般在where 子句中使用class 去判斷某個實例。class 的值就是discriminator-value 配置name(就是實例的類名)。

<hibernate-mapping package="com.example.test.bean">
	<class name="Person1" table="PERSON" dynamic-insert="true">
           <id name="id" column="id" type="integer">
             <generator class="identity"></generator>
           </id>	
           <discriminator column="type" type="string"></discriminator>
           <property name="name" type="string" column="name"></property>
          <!--  指定繼承關係中鑑別值 -->
           <subclass discriminator-value="man" name="Man">
           <property name="manonly" type="string" column="manonly"></property>
           </subclass>
           <subclass discriminator-value="woman" name="Woman">
           <property name="womanonly" type="string" column="womanonly"></property>
           </subclass>
	</class>
</hibernate-mapping>
 select p.name from Person1 p where p.class = Man

在where 子句中一個實例(表)查詢,有多個條件用and 連接。

當where 子句中的運算符只支持基本類型或者字符串時,where 子句中的屬性表達式必須以基本類型或者字符串結尾,不要使用組件類型屬性結尾。

表達式

演示的一些例子,是以字段,在HQL中使用,需要屬性名。

hql 的where 子句可以包含很多的表達式進行查詢,但是需要注意的是,有些需要底層數據庫的支持,稍微注意一下。

  1. 數學運算符: +、一、*、/等。基於字段是數值類型的操作。

  2. 二進制比較運算符: =、>=、<=、<>(不相等)、!=(不相等)、like等。(這裏有一個要注意,就是日期類型也是可以用>比較的)

  3. 邏輯運算符: and、 or、 not等。

  4. 字符串連接符:如value1 Il value2,或使用字符串連接函數concat(value1,value2)。裏面的兩個值是字段名,將字段值拼接起來。

  5. 查詢時間: current_ date(). current_ time(). current_ timestamp(). second(). minute().hour(). day(). month(). year()
    在這裏插入圖片描述
    6.字段值的操作函數: substring() trim() lower() upper() length() locate() abs() sqrt() bit length() coalesce()f nullif()
    在這裏插入圖片描述

  6. HQL語句支持使用英文問號(?)作爲參數佔位符,這與JDBC的參數佔位符一致;命名參數佔位符號,方法是在參數名前加英文冒號(😃 ,例如:start date, :x1 等。

  7. 可在where子句中使用SQL常量,例如"foo’、69、'1970-01-01 10:00:01.0’等 。還可以在HQL語句中使用Java中的public static final類型的常量,例如Color.RED。

  8. in. not in. [not]between …and(這個後面是字段值,查詢是會根據數據庫表的排列順序,返回兩個字段值其中的數據) . is null., is not null. member of and not memberof (這個在sql 有多態查詢,判斷一個表實例是否是另外一個實例 Man extends Person man member of person ),注意在hql 中的字符串是需要單引號括起來。

  9. 下面的聲明表明: HQL 轉換SQL語句時,將使用字符1和0來取代關鍵字true和false, 然後將可以在表達式中使用布爾表達式。

<property I name"hibernate. query. substitutions">true 1,! false 0</property>
from Cat cat where cat.alive =0;
  1. 對於有序集合(普通屬性會報錯),還可使用minindex 與maxindex函數代表最小與最大的索引序數。同理,可以使用minelement與maxelement 函數代表集合中最小與最大的元素(這個一定是數值類型,纔可以使用).
  2. size關鍵字用於返回一個集合的大小。
cat where cat.kittens.size > 0;
cat where size (cat.kittens) > 0;
  1. 在where子句中,有序集合(數組、List集合、Map對象)的元素可以通過[ ]運算符訪問。如果是List,[數字];如果是Map,[key]。
  2. elements 可以返回指定集合中所有元素,indices 可以返回指定集合的所有索引。
("select elements(p.phones) from Person p where p.name=:name1").setParameter("name1","laoqiang1")//查詢名字爲laoqiang1 的phone 信息,執行的時候,會發現底層數據庫執行的笛卡爾積運算,查詢出來phone表滿足的主鍵id,在根據id 去查詢phone信息。
select
        phones1_.phone_id as col_0_0_ 
    from
        PERSON person0_ cross 
    join
        PHONE phones1_ 
    where
        person0_.person_id=phones1_.person_id 
        and person0_.person_name=?
"select p1.name from Phone p,Person p1 where p in elements(p1.phones) and p.id=3"//查詢Phone 中id=3 的Person 的名字,通過啥關聯的就是通過 p  in elements(p1.phones)來確定了 p 對應的Person ,從來產生聯繫,就可以查詢到Person了 

order by

在HQL 可以和SQL 對結果集進行排序。asc 升序 desc 降序,如果不指定,那麼默認是升序。

select p1.name from Person p1 **order by** p1.name desc

group by

在HQL 可以和SQL 對結果集進行分組。

select p1.name from Person p1 group by  p1.name 

注意分組的屬性一定要出現在select 後面。

having

是對分組的結果進行篩選。

select p.color from Person p group by p. color having p.color in ('yellow','red')

having子句用於對分組進行過濾,因此having子句只能在有group by子句時纔可以使用, 沒有group by子句,不能使用having子句。

子查詢

HQL 的子查詢只能出現在where或者select 中。

命名查詢

HQL查詢還支持將查詢所用的HQL語句放入配置文件中,而不是代碼中。

在Hibernate映射文件的<hibermate -mappng…元素中使用<query…>子元素來定義命名查詢,使用<query…>元素只需指定個name屬性,指定該命名查詢的名字。

<query name="query1">
	select p from Person p
</query>
		 java.util.List list = ss.getNamedQuery("query1").list();

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