文章目錄
Hibernate 查詢
Hibernate提供異常強大的查詢體系,使用Hibernate 有多種查詢方式可以選擇:既可以使用Hibermate的HQL查詢,也可以使用條件查詢,甚至可以使用原生的SQL查詢語句。不僅如此,Hibernate還提供了一種數據過濾功能,這些都用於篩選目標數據。
HQL 查詢
HQL 簡介
HQL是Hibernate Query Language 的縮寫,HQL的語法很像SQL的語法,但HQL是一種面向對象的查詢語言。SQL的操作對象是數據表、列等數據庫對象,而HQL的操作對象是類、實例、屬性等。
HQL是完全面向對象的查詢語言,因此可以支持繼承、多態等特性。
HQL語句本身是不區分大小寫的。也就是說,HQL語句的關鍵字、函數都是不區分大小寫的。但HQL語句中所使用的包名、類名、實例名、屬性名都區分大小寫。
HQL 查詢步驟
- HQL查詢依賴於Query類,每個Query實例對應一個查詢對象。
- 獲取Hibernate Session對象。
- 編寫HQL語句。
- 以HQL語句作爲參數,調用Session的createQuery方法創建查詢對象。
- 如果HQL語句包含參數,則調用Query的setXxx方法爲參數賦值。
- 調用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) 形式:
- 隱式(implicit)
隱式連接形式不使用join關鍵字,使用英文點號(.) 來隱式連接關聯實體,而Hibernate底層將自動進行關聯查詢。
select p from Person as p where p.events.event_title =:event_title1//這裏實際上查看持久化類events 是一個集合,實際上,就是通過它知道映射關係,底層會自動關聯,集合裏面都是Event 對象,拿到event_title 屬性的。
- 顯式(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 子句可以包含很多的表達式進行查詢,但是需要注意的是,有些需要底層數據庫的支持,稍微注意一下。
-
數學運算符: +、一、*、/等。基於字段是數值類型的操作。
-
二進制比較運算符: =、>=、<=、<>(不相等)、!=(不相等)、like等。(這裏有一個要注意,就是日期類型也是可以用>比較的)
-
邏輯運算符: and、 or、 not等。
-
字符串連接符:如value1 Il value2,或使用字符串連接函數concat(value1,value2)。裏面的兩個值是字段名,將字段值拼接起來。
-
查詢時間: 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()
-
HQL語句支持使用英文問號(?)作爲參數佔位符,這與JDBC的參數佔位符一致;命名參數佔位符號,方法是在參數名前加英文冒號(😃 ,例如:start date, :x1 等。
-
可在where子句中使用SQL常量,例如"foo’、69、'1970-01-01 10:00:01.0’等 。還可以在HQL語句中使用Java中的public static final類型的常量,例如Color.RED。
-
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 中的字符串是需要單引號括起來。
-
下面的聲明表明: HQL 轉換SQL語句時,將使用字符1和0來取代關鍵字true和false, 然後將可以在表達式中使用布爾表達式。
<property I name"hibernate. query. substitutions">true 1,! false 0</property>
from Cat cat where cat.alive =0;
- 對於有序集合(普通屬性會報錯),還可使用minindex 與maxindex函數代表最小與最大的索引序數。同理,可以使用minelement與maxelement 函數代表集合中最小與最大的元素(這個一定是數值類型,纔可以使用).
- size關鍵字用於返回一個集合的大小。
cat where cat.kittens.size > 0;
cat where size (cat.kittens) > 0;
- 在where子句中,有序集合(數組、List集合、Map對象)的元素可以通過[ ]運算符訪問。如果是List,[數字];如果是Map,[key]。
- 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();