5、 JPQL
JPQL(JavaPersistence Query Language),是基于实体的查询,所查询的内容都是实体或实体属性。
5.1、查询实体
SELECT c FROM Customer c
JPQL语句与SQL基本类似,但它是基于实体的查询。在初次使用时,需要注意以下几个问题。
1) 实体名和属性区分大小写;
2) JPQL中的保留关键字不区分大小写;
3) 标识实体的别名,也不区分大小写;
4) 实体的别名可以使用AS关键字来标识,AS关键字也可以省略;
5.2、实体属性
查询时如果使用了实体的别名,在SELECT查询时便可以引用该实体的属性(而不是表的字段名),如下:
SELECT c.name FROM Customer c
如果查询的实体属性是另一个实体(一对一关系),也可以通过属性后“.”来获得实体的属性,如下
SELECT c.address FROM Customer c
SELECT c.address.street FROM Customer c(多级导航)
5.3、关联查询
JPA提供了两种显示关联实体的方式,分别为内连接(INNERJOIN)和左外连接(LEFTOUTER JOIN)。
首先回顾下传统SQL中的内连接、外连接、左连接、右连接方式。假设有表tb_customer和表tb_order分别对应实体Customer和Order,表中数据如下
tb_customer数据
id |
name |
|
address_id |
1 |
zhangsan |
[email protected] |
6 |
2 |
lisi |
[email protected] |
7 |
3 |
wangwu |
[email protected] |
8 |
tb_order数据
id |
num |
customer_id |
create_date |
5 |
10002 |
1 |
|
6 |
10003 |
1 |
|
7 |
10004 |
5 |
|
8 |
10005 |
1 |
|
9 |
10006 |
3 |
1) 内连接
内连接查询也叫自然连接查询,它是两个表关联的最小集合。也就是说,左表中的数据和右表中的数据都存在时才会关联,并且执行内连接查询时不会出现null值。如执行下面的SQL语句
SELECT * from tb_customer c INNER JOIN tb_order o ONc.id=o.customer_id
查询的结果如下
c.id |
c.name |
o.id |
o.num |
o.customer_id |
1 |
zhangsan |
5 |
10002 |
1 |
1 |
zhangsan |
6 |
10003 |
1 |
1 |
zhangsan |
8 |
10005 |
1 |
3 |
wangwu |
9 |
10006 |
3 |
左表为tb_customer,右表为tb_oreder。左表中id=2的记录,在右表中不存在。右表中id=7的记录,在左表中也不存在,所以在使用内连接查询时不存在着两项数据。
2) 左连接
左连接是以左表为基础,如果右表的数据有关联则显示,如果右表中没有数据则显示为null。也就是说左连接查询的全部是坐标的数据,而右表的数据不一定是全部的,如下
SELECT * from tb_customer c LEFT JOIN tb_order o ONc.id=o.customer_id
c.id |
c.name |
o.id |
o.num |
o.customer_id |
1 |
zhangsan |
5 |
10002 |
1 |
1 |
zhangsan |
6 |
10003 |
1 |
1 |
zhangsan |
8 |
10005 |
1 |
2 |
lisi |
null |
null |
null |
3 |
wangwu |
9 |
10006 |
3 |
3) 右连接查询
右连接查询与左连接查询相反,它是以右表为查询基础。如果左表数据有关联则显示,没有则显示null。如下
SELECT * from tb_customer c RIGHT JOIN tb_order o ONc.id=o.customer_id
c.id |
c.name |
o.id |
o.num |
o.customer_id |
1 |
zhangsan |
5 |
10002 |
1 |
1 |
zhangsan |
6 |
10003 |
1 |
null |
null |
7 |
10004 |
5 |
1 |
zhangsan |
8 |
10005 |
1 |
3 |
wangwu |
9 |
10006 |
3 |
4) 外连接查询
外连接查询是相对于内连接查询来说的,总的来说左连接查询和右连接查询都属于外连接查询,只不过是方向不同而已。
表连接查询的关系如下图所示:
与传统的SQL语句相比,JPA只提供了两种方式的表连接:内连接和左连接。
l 内连接
内连接是最常用的连接方式,连接后关联数据不会出现null值,语法如下:
[INNER] JOIN
SELECT c,o FROM Customer c JOIN c.orders o
l 左连接
左连接也可以叫做左外连接,它是以左表为基础,关联右表。连接后右表中的数据可能为null值,如下
LEFT[OUTER] JOIN
SELECT c,o FROMCustomer c LEFT JOIN c.orders o
此时,查询结果是所有的客户,即使客户没有订单,也是可以查询到的。
l 抓取连接
实体属性的加载有两种方式,分为即时加载和懒加载。同样,对于实体关联的查询,也可以设置查询时的加载方式,这就是抓取连接,如下
[LEFT|INNER] JOIN FETCH
简单地说在JOIN关键字后加上FETCH关键字,则表示查询为抓取查询。事实上抓取连接主要针对实体属性为懒加载方式的,使用内连接并没有加载关联的实体,如下
SELECT c FROM Customer c JOIN c.orders o
此时在客户端调用getOrders方法将抛出异常。但若将关联查询设置为抓取方式,如下
SELECT c FROM Customer c JOIN FETCH
则查询结果的customer对性已加载所关联的orders属性。
l 唯一性
当进行连接查询时,通常会产生一些重复数据,若要去掉重复的数据,在关联查询时可以使用DISTINCT关键字,如下
SELECT DISTINCT c FROM Customer c JOIN c.orders o
5.4、操作符
WHERE条件表达式可以包含比较操作符和逻辑操作符,如下
l 比较操作符
=、>、>=、<、<=、<>、[NOT]BETWEEN、[NOT]LIKE、
[NOT] BETWEEN、[NOT] IN、IS [NOT] NULL、IS [NOT]EMPTY、[NOT]MEMBER OF
l 逻辑操作符
NOT、AND、OR
5.5、BETWEEN
BETWEEN 操作符是查询值在某一个指定范围内的缩写,语法如下:
[NOT] BETWEEN 范围值一 AND 范围值二
SELECT c FROM Customer WHRER c.asset BETWEEN 1000.0 and2000.0
5.6、IN
IN操作符可以查询在指定的多个值,语法如下:
[NOT] IN (值{,值}*|子查询)
5.7、LIKE
LIKE 操作符用来查询匹配指定的字符串,匹配字符串的关键字符有以下两种:
1) 下画线“_”:表示匹配某一个字符;
2) 百分号:表示匹配零个或多个字符;
如果要查询的字符串存在“_”或“%”,则需要在匹配的字符串前转移字符“\”。
5.8、NULL
NULL操作符用来判断属性是否是null值,如下
判断属性是null,使用“ISNULL”关键字,如下
SELECT c FROM Customer c WHERE c.address IS NULL
判断属性不为null,使用“IS NOTNULL”关键字,如下
SELECT c FROM Customer c WHERE c.address IS NULL
NULL操作符也可以用在参数查询中,用于判断输入的参数是否为null值,如下
SELECT c FROM Customer c WHERE :zip IS NOT NULLL ANDc.address.zip=:zip
5.9、EMPTY
EMPTY操作符用于判断实体的集合类属性是否为空。它与NULL操作符不太相同,主要是针对属性是集合类的判断。
判断集合类属性为空值,使用IS EMPTY,如下
SELECT c FROM Customer c WHERE c.orders IS EMPTY
注意:当使用内连接查询时,由于此时关联后的表中没有null值,所以即使查询到空值,查询结果中也不会出现,所以在使用内连接查询时注意不要使用IS
MPTY来判断空值,如下
SELECT c FROM Customer c JOIN c.orders o WHERE c.orders ISEMPTY
判断集合类属性不是空值,使用IS NOT EMPTY,如下
SELECT c FROM Customer c WHERE c.orders IS NOT EMPTY
5.10、MEMBER OF
MEMBER OF操作符用于判断一个实体是否包含在集合类对象中(MEMBEROF 前可以添加NOT),如下
//查询订单号为1的客户实体
SELECT c FROM Customer c WHERE :order MEMBER OF c.orders
Query query=entityManager.createQuery(jpql);
Order o=entityManager.find(Order.class,1);
query.setParameter(“order”,o);
5.11、函数表达式
JPQL中也定义了一些常用的函数,这些函数可以针对字符型、数值型和日期型数值使用。
字符串函数
字符串函数通常可在查询时使用,例如查询顾客姓名的长度大于10的JPQL语句如下:
SELECT c FROM Customer c WHERE LENGTH(c.name)>10
JPQL 中提供的字符串函数主要有:
3) CONCAT(str1,str2):返回连接两个字符串的值
4) SUBSTRING(str,start,len):返回字符串的一段,start为字符串的开始索引位(第一个字符位置为1),len为截取的长度;
5) TRIM(str):去掉字符串的首尾的空格;
6) LENGTH(str):返回字符串的长度;
数值函数
数值函数通常也可以在查询时使用,如下:
SELECT c FROM Customer c WHERE ABS(c.asset)>20
JPQL 中提供的主要数值函数有:
l ABS(num):返回数的绝对值;
l SQRT(num):返回数的平方;
l MOD(int,int):取模;
l SIZE:返回集合类的总数;
日期函数
JPA提供了三种获取系统当前时间的格式的方法:
l CURRENT_DATE
l CURRENT_TIME
l CURRENT_TIMESTAMP
5.12、子查询
当一个查询条件依赖于另一个查询结果时,就需要使用子查询(嵌套查询),如下
SELECT c FROM Customer c WHERE c.age > (SELECT AVG(c.age)FROM Customer c)
EXISTS表达式
EXISTS表达式用于判断子查询的结果,如果子查询的结果有一个或多个,则返回true;如果子查询没有返回任何结果,则返回false。语法如下:
[NOT] EXISTS (子查询表达式)
如查询系统当前日期之前的订单所属的客户,如下
SELECT c FROM Customer c WHERE EXISTS
(SELECT o FROM c.orders o WHERE o.createTime <CURRENT_DATE)
也可以在EXISTS关键字前加上“NOT”表示不存在,查询结果正好与EXISTS相反。
ALL和ANY表达式
当子查询的返回结果有多个,顶层查询的条件使用=,<,<=,>,>=,<>这些比较来满足条件,就需要使用ALL、ANY和SOME(ANY、SOME与NOT ALL等价,只要存在一个即可)表达式,基本语句入如下:
{ALL|ANY|SOME}(子查询)
SELECT o FROM Order o WHERE 50 < ANY (SELECT l.quantityFROM o.lineItems l) //查询订单明细中存在50个以上的订单
5.13、分组
分组查询是JPQL中很重要的查询,它可以按照指定的属性将数据分组,通常在统计数据时使用。
分组查询通常使用“GROUPBY”表达式和“HAVING”表达式,分组查询的基本语法如下:
GROUP BY <分组子句>(HAVING<having 子句>)
“GROUPBY”关键字后指明分组的属性,“HAVING”则可以对分组后的数据进行过滤,作用相当于WHERE子句,只能用在分组查询中,如下
SELECT c FROM Customer c GROUP BY c.asset HAVING AVG(c.asset)>1000
5.14、排序
ORDER BY子句可以对查询结果进行排序,语法如下:
ORDER BY 排序属性[ASC |DESC]{, 排序属性[ASC |DESC]}*
其中“ORDERBY”关键字后指定排序的属性,多个属性排序用分号分割,如下:
SELECT c FROM Customer c ORDER BY c.id ASC,c.name DESC
注意:在动态组装JPQL指定多个排序域时,不能使用HashMap,而要使用LinkedHashMap。
版权声明:本文为博主原创文章,未经博主允许不得转载。