(轉)JPA JPQL
JPQL就是一種查詢語言,具有與
SQL
相
類似的特徵,
JPQL
是完全面向對象的,具備繼承、多態和關聯等特性,和hibernate HQL很相似。
查詢語句的參數
JPQL
語句支持兩種方式的參數定義方式
:
命名參數和位置參數
。
。在同一個查
詢語句中只允許使用一種參數定義方式。
命令參數的格式爲:“
: +
參數
名”
例:
Query query = em.createQuery("select p from Person p where
p.personid=:Id
");
query.setParameter("Id",new Integer(1));
位置參數的格式爲“
?+
位置編號”
例:
Query query = em.createQuery("select p from Person p where
p.personid=?1
");
query.setParameter(1,new Integer(1));
如果你需要傳遞
java.util.Date
或
java.util.Calendar
參數進一個參數查詢
,你需要使用一個特殊的
setParameter()
方法
,相關的
setParameter
方法定義如下:
public interface
Query
{
//
命名參數查詢時使用,參數類型爲
java.util.Date
Query setParameter(String name, java.util.Date value,
TemporalType temporalType);
//
命名參數查詢時使用,參數類型爲
java.util.Calendar
Query setParameter(String name, Calendar value, TemporalType
temporalType);
//
位置參數查詢時使用,參數類型爲
java.util.Date
Query setParameter(int position, Date value, TemporalType
temporalType);
//
位置參數查詢時使用,參數類型爲
java.util.Calendar
Query setParameter(int position, Calendar value, TemporalType
temporalType);
}
因爲一個
Date
或
Calendar
對象能夠描述一個真實的日期、時間或時間戳
.
所以我們需要告訴
Query
對象怎麼使用這些參數,我們把
javax.persistence.TemporalType
作爲參數傳遞進
setParameter
方法,告訴查詢接口在轉換
java.util.Date
或
java.util.Calendar
參數到本地
SQL
時使用什麼數據庫類型
。
下面通過實例來學習JPQL語句,例子的entity
Bean
有
Person, Order, OrderItem
,
他們之間
的關係是:一個
Person
有多個
Order,
一個
Order
有多個
OrderItem
。
JPQL語句的大小寫敏感性:除了
Java
類和屬性名稱外,查詢都是大小寫不敏感的
。所以,
SeLeCT
和
sELEct
以及
SELECT
相同的,但是
com.foshanshop.ejb3.bean.Person
和
com.foshanshop.ejb3.bean.PERSon
是不同的,
person.name
和
person.NAME
也是不同的。
命名查詢
可以在實體
bean
上通過
@NamedQuery
or
@NamedQueries
預先定義一個或多個查詢語句,減少每次因書寫錯誤而引起的
BUG
。通常把經常使用的查詢語句定義成命名查詢
。
定義單個命名查詢:
@NamedQuery
(name=
"getPerson"
, query=
"FROM Person WHERE
personid=?1"
)
@Entity
public class
Person
implements
Serializable{
如果要定義多個命名查詢,應在
@javax.persistence.NamedQueries裏定義
@NamedQuery
:
@NamedQueries
({
@NamedQuery
(name=
"getPerson"
, query=
"FROM Person WHERE
personid=?1"
),
@NamedQuery
(name=
"getPersonList"
, query=
"FROM Person WHERE
age>?1"
)
})
@Entity
public class
Person
implements
Serializable{
當命名查詢定義好了之後,我們就可以通過名稱執行其查詢。代碼如下:
Query query =
em
.
createNamedQuery("getPerson")
;
query.setParameter(1,
1);
排序
(order by)
"ASC"
和
"DESC"
分別爲升序和降序,
JPQL
中默認爲
asc
升序
例:
//
先按年齡降序排序,然後按出生日期升序排序
Query query =
em.createQuery("select p from Person p order
by p.age desc, p.birthday asc
");
查詢部分屬性
通常來說,都是針對
Entity
類的查詢,返回的也是被查詢的
Entity
類的實體。J
P
QL
也允許我們
直接查詢返回我們需要的屬性,而不是返回整個
Entity
。在一些
Entity
中屬性特別多的情況,這樣的查詢可以提高性能
例:
//
只
查詢我們感興趣的屬性
(
列
)
Query
query=em.createQuery("select p.personid, p.name from Person p order by
p.personid desc ");
//
集合中的元素不再是
Person,
而是一個
Object[]
對象數組
List result =
query.getResultList();
if (result!=null){
Iterator iterator = result.iterator();
while( iterator.hasNext() ){
Object[] row = ( Object[]) iterator.next();
int personid = Integer.parseInt(row[0].toString());
String PersonName = row[1].toString();
。。。。
}
}
查詢中使用構造器
(Constructor)
JPQL
支持將查詢的屬性結果直接作爲一個
java class
的構造器參數,併產生實體作爲結果返回
。例如上面的例子只獲取
person entity bean的name and
personid屬性,我們不希望返回的集合的元素是object[],而希望用一個類來包裝它。就要用到使用構造器
。
例:
public class
SimplePerson {
private Integer
personid;
private
String
name
;
。。。。
public
SimplePerson() {
}
public
SimplePerson(Integer personid,
String name) {
this
.
name
= name;
this
. personid = personid;
}
}
查詢代碼爲:
//
我們把需要的兩個屬性作爲
SimplePerson
的構造器參數,並使用
new
函數。
Query query =
em.createQuery("
select new com.foshanshop.ejb3.bean.SimplePerson(p. personid,
p.name) from Person p order by p.personid desc
");
//
集合中的元素是
SimplePerson
對象
List result =
query.getResultList();
if (result!=null){
Iterator iterator = result.iterator();
while( iterator.hasNext() ){
SimplePerson simpleperson = (SimplePerson) iterator.next();
。。。。
}
}
聚合查詢
(Aggregation)
JPQL
支持的聚合函數
包括:
1. AVG()
2. SUM()
3. COUNT()
,返回類型爲
Long
,注意
count(*)
語法在
hibernate
中可用,但在
toplink
其它產品中並不可用
4. MAX()
5. MIN()
例:
//
獲取最大年齡
Query query =
em.createQuery("select
max(p.age)
from
Person p");
Object result =
query.getSingleResult();
String maxAge =
result.toString();
//
獲取平均年齡
query =
em.createQuery("select
avg(p.age)
from
Person p");
//
獲取最小年齡
query =
em.createQuery("select
min(p.age)
from
Person p");
//
獲取總人數
query =
em.createQuery("select
count(p)
from
Person p");
//
獲取年齡總和
query =
em.createQuery("select
sum(p.age)
from
Person p");
如果聚合函數不是
select...from
的唯一一個返回列,需要使用
"GROUP BY"
語句
。
"GROUP
BY"
應該包含
select
語句中除了聚合函數外的所有屬性。
例:
//
返回男女生各自的總人數
Query query =
em.createQuery("
select
p.sex, count(p) from Person p group by p.sex
");
//
集合中的元素不再是
Person,
而是一個
Object[]
對象數組
List result =
query.getResultList();
如果還需要加上查詢條件,需要使用
"HAVING"
條件語句而不是
"WHERE"
語句
例:
//
返回人數超過
1
人的性別
Query query =
em.createQuery("select p.sex, count(p) from Person p group by p.sex
having count(*)
>?1");
//
設置查詢中的參數
query.setParameter(1,
new Long(1));
//
集合中的元素不再是
Person,
而是一個
Object[]
對象數組
List result =
query.getResultList();
關聯
(join)
JPQL
仍然支持和
SQL
中類似的關聯語法:
left out join/left
join
inner join
left join fetch/inner
join fetch
left out join/left
join
等,
都是允許符合條件的右邊表達式中的
Entiies
爲空(
需要顯式使用
left
join/left outer join
的情況會比較少。
)
例:
//
獲取
26
歲人的訂單
,
不管
Order
中是否有
OrderItem
select o from Order o left join o.orderItems
where o.ower.age=26
order by o.orderid
inner join
要求右邊的表達式必須返回
Entities
。
例:
//
獲取
26
歲人的訂單
,Order
中必須要有
OrderItem
select o from Order o inner join o.orderItems
where o.ower.age=26
order by o.orderid
!!重要知識點
:
在默認的查詢中,
Entity
中的集合屬性默認不會被關聯,集合屬性默認是延遲加載
( lazy-load )
。那麼,
left fetch/left out fetch/inner join fetch
提供了一種靈活的查詢加載方式來提高查詢的性能。
例:
private String
QueryInnerJoinLazyLoad(){
//
默認不關聯集合屬性變量
(orderItems)
對應的表
Query query =
em.createQuery("select o from Order o
inner join
o.orderItems where
o.ower.age=26 order by o.orderid");
List result =
query.getResultList();
if (result!=null
&& result.size()>0){
//
這時獲得
Order
實體中
orderItems(
集合屬性變量
)
爲空
Order order = (Order) result.get(0);
//
當需要時,
EJB3 Runtime
纔會執行一條
SQL
語句來加載屬於當前
Order
的
//OrderItems
Set<OrderItem> list
=
order.getOrderItems
();
Iterator<OrderItem> iterator = list.iterator();
if (iterator.hasNext()){
OrderItem orderItem
=iterator.next();
System.out.println ("
訂購產品名:
"+ orderItem.getProductname());
}
}
上面代碼在執行
"select o from Order o inner join
o.orderItems where o.ower.age=26 order by o.orderid"
時編譯成的
SQL
如下(
他不包含集合屬性變量
(orderItems)
對應表的字段
)
:
select order0_.orderid as
orderid6_, order0_.amount as amount6_, order0_.person_id as
person4_6_, order0_.createdate as
createdate6_ from Orders order0_ inner join OrderItems
orderitems1_ on
order0_.orderid=orderitems1_.order_id, Person person2_ where
order0_.person_id=person2_.personid
and person2_.age=26 order by order0_.orderid
上面代碼當執行到
Set<OrderItem> list =
order.getOrderItems();
時纔會執行一條
SQL
語句來加載
屬於當前
Order
的
OrderItems
,編譯成的
SQL
如下
:
select orderitems0_.order_id as
order4_1_, orderitems0_.id as id1_, orderitems0_.id as id7_0_,
orderitems0_.order_id as
order4_7_0_, orderitems0_.productname as productn2_7_0_,
orderitems0_.price as price7_0_
from OrderItems orderitems0_ where orderitems0_.order_id=?
order by orderitems0_.id ASC
這樣的查詢性能上有不足的地方
。爲了查詢
N
個
Order
,我們需要一條
SQL
語句獲得所有的
Order
的
原始對象屬性,但需要另外
N
條語句獲得每個
Order
的
orderItems
集合屬性。爲了避免
N+1
的性能問題,我們可以利用
join fetch
一次過用一條
SQL
語句把
Order
的所有信息查詢出來
例子
//
獲取
26
歲人的訂單
,Order
中必須要有
OrderItem
Query query =
em.createQuery("select o from Order o
inner join fetch
o.orderItems where
o.ower.age=26 order
by o.orderid");
上面這句HPQL編譯成以下的
SQL
:
select order0_.orderid as
orderid18_0_, orderitems1_.id as id19_1_, order0_.amount as
amount18_0_,order0_.person_id as
person4_18_0_, order0_.createdate as createdate18_0_,
orderitems1_.order_id
as order4_19_1_, orderitems1_.productname
as productn2_19_1_,
orderitems1_.price
as price19_1_, orderitems1_.order_id
as order4_0__, orderitems1_.id
as
id0__
from Orders order0_ inner join
OrderItems orderitems1_ on
order0_.orderid=orderitems1_.order_id,
Person person2_ where
order0_.person_id=person2_.personid
and person2_.age=26 order by order0_.orderid,
orderitems1_.id ASC
上面由於使用了
fetch,
這個查詢只會產生一條
SQL
語
句,比原來需要
N+1
條
SQL
語句在性能上有了極大的提升
排除相同的記錄
DISTINCT
使用關聯查詢,我們很經常得到重複的對象,
如下面語句:
"
select o from Order o
inner join fetch o.orderItems order by o.orderid
"
當有
N
個
orderItem
時就會產生
N
個
Order,
而有些
Order
對象往往是相同的,這時我們需要使用
DISTINCT
關鍵字來排除掉相同的對象
。
例:
select
DISTINCT
o from Order o inner
join fetch o.orderItems order by o.orderid
比較
Entity
在查詢中使用參數查詢時
,
參數類型除了
String,
原始數據類型
(
int, double
等
)
和它們的
對象類型
( Integer, Double
等
),
也可以是
Entity
的實例
。
例:
//
查詢某人的所有訂單
Query query =
em.createQuery("select o from Order o where
o.ower =?1
order by o.orderid");
Person person = new
Person();
person.setPersonid(new
Integer(1));
//
設置查詢中的參數
query.setParameter(1,person);
批量更新
(Batch Update)
HPQL支持批量更新
例:
//
把所有訂單的金額加
10
Query query =
em.createQuery("
update
Order as o set o.amount=o.amount+10
");
//update
的記錄數
int result = query.
executeUpdate
();
批量刪除
(Batch Remove)
例:
//
把金額小於100的訂單刪除,先刪除訂單子項,
再刪除訂單
Query query =
em
.createQuery(
"delete from OrderItem
item where item.order in(from Order as o where o.amount<100)"
);
query.
executeUpdate();
query =
em
.createQuery(
"delete from Order as o
where o.amount<100"
);
query.executeUpdate();
//delete
的記錄數
使用操作符
NOT
//
查詢除了指定人之外的所有訂單
Query query =
em.createQuery("select o from Order o where
not(o.ower =?1)
order by o.orderid");
Person person = new
Person();
person.setPersonid(new
Integer(2));
//
設置查詢中的參數
query.setParameter(1,person);
使用操作符
BETWEEN
select o from Order as o where o.amount between 300 and 1000
使用操作符
IN
//
查找年齡爲
26,21
的
Person
select p from Person as p where
p.age in(26,21)
使用操作符
LIKE
//
查找以字符串
"li"
開頭的
Person
select p from Person as p where
p.name like 'li%'
使用操作符
IS NULL
//
查詢含有購買者的所有
Order
select o from Order as o where
o.ower is [not] null
使用操作符
IS EMPTY
IS EMPTY
是針對集合屬性
(Collection)
的操作符
。可以和
NOT
一起使用
。
注:低版權的
Mysql
不
支持
IS EMPTY
//
查詢含有訂單項的所有
Order
select o from Order as o where
o.orderItems is [not] empty
使用操作符
EXISTS
[NOT]EXISTS
需要和子查詢配合使用
。注:低版權的
Mysql
不支持
EXISTS
//
如果存在訂單號爲
1
的訂單,就獲取所有
OrderItem
select oi from OrderItem as oi
where exists
(select o
from Order o where o.orderid=1)
//
如果不存在訂單號爲
10
的訂單,就獲取
id
爲
1
的
OrderItem
select oi from OrderItem as oi
where oi.id=1 and not exists
(select o from Order o where o.orderid=10)
字符串函數
JPQL
定義了內置函數方便使用。這些函數的使用方法和
SQL
中相應的函數方法類似。包括:
1. CONCAT
字符串拼接
2. SUBSTRING
字符串截取
3. TRIM
去掉空格
4. LOWER
轉換成小寫
5. UPPER
裝換成大寫
6. LENGTH
字符串長度
7. LOCATE
字符串定位
例:
//
查詢所有人員,並在姓名後面加上字符串
"_foshan"
select p.personid, concat(p.name, '_foshan')
from Person as p
//
查詢所有人員
,
只取姓名的前三個字符
select p.personid, substring
(p.name,1,3) from Person as p
計算函數
HPQL
定義的計算函數包括:
ABS
絕對值
SQRT
平方根
MOD
取餘數
SIZE
取集合的數量
例:
//
查詢所有
Order
的訂單號及其訂單項的數量
select o.orderid, size
(o.orderItems) from Order as o group by
o.orderid
//
查詢所有
Order
的訂單號及其總金額
/10
的餘數
select o.orderid, mod
(o.amount, 10) from Order as o
子查詢
子查詢可以用於
WHERE
和
HAVING
條件語句中
例:
//
查詢年齡爲
26
歲的購買者的所有
Order
select o from Order as
o where o.ower in(select p from Person as p where p.age =26)
結果集分頁
有些時候當執行一個查詢會返回成千上萬條記錄,事實上我們只需要顯示一部分數據。這
時我們需要對結果集進行分頁
,
QueryAPI
有兩個接口方法可以解決這個問題:
setMaxResults( )
和
setFirstResult( )
。
setMaxResults
方法設置獲取多少條記錄
setFirstResult
方法設置從結果集中的那個索引開始獲取
(假如返回的記錄有
3
條,容器會自動爲記錄編上索引,索引從
0
開始,
依次爲
0
,
1
,
2
)
例:
public
List getPersonList(
int
max,
int
whichpage) {
try
{
int
index = (whichpage-1) * max;
Query query =
em
.createQuery(
"from Person p order by
personid asc"
);
List list = query.
setMaxResults(max)
.
setFirstResult(index)
.
getResultList();
em
.clear();
//
分離內存中受EntityManager管理的
實體bean,讓VM進行垃圾回收
return
list;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.