JPQL(Java Persistence Query Language,Java 持久化查询语言)和 SQL 之间有很多相似之处,它们之间主要的区别在于前者处理 JPA 实体类,而后者则直接涉及关系数据。在 JPQL 中,可以使用SELECT
、UPDATE
和DELETE
语法来定义查询。
1. 查询
语法:SELECT ... FROM ... [WHERE ...] [GROUP BY ... [HAVING ...]] [ORDER BY ...]
FROM 子句
通过声明一个或多个标识符变量来定义查询的范围。在SELECT
和WHERE
子句中可以引用这些变量。
WHERE 子句
用于限制查询到的对象或值的条件表达式。
GROUP BY 子句
根据一组属性对查询结果进行分组。
HAVING 子句
配合GROUP BY
子句使用,以根据条件表达式进一步限制查询结果。
ORDER BY 子句
对查询结果进行排序。
# 部门实体
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @Entity(name = "Department") public class Department { @Id @GeneratedValue private Long id; private String name; @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) @JoinColumn(name = "department_id") private Set<Employee> employees; }
|
# 雇员实体
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| @Entity(name = "Employee") public class Employee { @Id @GeneratedValue private Long id; private String name; @Enumerated(EnumType.STRING) private Sex sex; private Integer age; private Boolean married; private Double salary; private Date hireDate; @ManyToOne(fetch = FetchType.LAZY) private Department department; }
|
# 性别枚举
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package org.fanlychie.enums; public enum Sex { MALE("男"), FEMALE("女"), ; private final String displayText; private Sex(String displayText) { this.displayText = displayText; } @Override public String toString() { return displayText; } }
|
1.1 基础查询
语法:SELECT 标识符变量 FROM 实体名称 [AS] 标识符变量
示例:查询所有的雇员信息
1 2
| @Query("SELECT E FROM Employee E") List<Employee> selectAll();
|
1.2 查询参数
JPQL 支持两种查询参数,它们分别是命名参数和位置参数。
1.2.1 命名参数
语法::自定义的参数名称
示例:按性别和薪资范围查找雇员信息
1 2
| @Query("SELECT E FROM Employee E WHERE E.sex = :sex AND E.salary > :salary") List<Employee> selectByNamedParams(@Param("sex") Sex sex, @Param("salary") Double salary);
|
在方法的参数列表中,需要使用@Param
注解标注每个参数的名称,使之与查询语句参数名称匹配。
1.2.2 位置参数
语法:?位置编号的数值
示例:按姓名和性别查找雇员信息
1 2
| @Query("SELECT E FROM Employee E WHERE E.sex = ?1 AND E.salary > ?2") List<Employee> selectByPositionalParams(Sex sex, Double salary);
|
在方法的参数列表中,参数的顺序需要与查询语句中参数标注的编号依次对应起来。
1.3 关联查询
通过使用关键字[LEFT|INNER] JOIN
联接关系属性查询。
1.3.1 单值关联查询
语法:SELECT 标识符变量 FROM 实体名称 [AS] 标识符变量 JOIN 实体名称.单值关联字段 [AS] 标识符变量2 ...
示例:按部门名称查找该部门所有的雇员信息
1 2
| @Query("SELECT E FROM Employee E JOIN E.department D WHERE D.name = ?1") List<Employee> selectByDeptName(String deptName);
|
1.3.2 多值关联查询
语法1:SELECT 标识符变量 FROM 实体名称 [AS] 标识符变量 JOIN 实体名称.多值关联字段 [AS] 标识符变量2 ...
示例:查询薪资大于10000的所有雇员所属的部门信息
1 2
| @Query("SELECT D FROM Department D JOIN D.employees E WHERE E.salary > 10000") List<Department> selectByMultRelatedField();
|
语法2:SELECT 标识符变量 FROM 实体名称 [AS] 标识符变量, IN(实体名称.多值关联字段) [AS] 标识符变量2 ...
1 2
| @Query("SELECT D FROM Department D, IN(D.employees) E WHERE E.salary > 10000") List<Department> selectByMultRelatedCollection();
|
1.4 去重查询
语法:SELECT DISTINCT 标识符变量 FROM 实体名称 [AS] 标识符变量 ...
示例:查询薪资大于10000的所有雇员所属的部门信息,并消除查询结果中的重复的部门
1 2
| @Query("SELECT DISTINCT D FROM Department D JOIN D.employees E WHERE E.salary > 10000") List<Department> selectByMultRelatedFieldDistinct();
|
1.5 字面值
JPQL 支持的字面值有以下的4种,它们分别是:字符串、数字、布尔、枚举。
1.5.1 字符串
语法:'字符串'
示例:查询给定名字的雇员信息
1 2
| @Query("SELECT E FROM Employee E WHERE E.name = '张三'") Employee selectByLiteralString();
|
如果字符串中含有单引号,则用两个单引号来表示。如:Li'Si
-> Li''Si
1 2
| @Query("SELECT E FROM Employee E WHERE E.name = 'Li''Si'") Employee selectByLiteralStringWithQuote();
|
1.5.2 数字
整数类型:如24
、+24
、-24
、24L
,支持 Java Long 范围的数值。
浮点类型:如24.
、24.6
、+24.6
、-24.6
、24.6F
、24.6D
,支持 Java Double 范围的数值。
示例:查询薪资大于10000的所有雇员
1 2
| @Query("SELECT E FROM Employee E WHERE E.salary > 10000.0") List<Employee> selectByLiteralNumber();
|
1.5.3 布尔
布尔类型的可选值为:TRUE
或FALSE
,它们不区分大小写。
示例:查找已婚的所有雇员
1 2
| @Query("SELECT E FROM Employee E WHERE E.married = TRUE") List<Employee> selectByLiteralBool();
|
1.5.4 枚举
枚举类名必须指定为完全限定类名。
示例:查询所有女性的雇员
1 2
| @Query("SELECT E FROM Employee E WHERE E.sex = org.fanlychie.enums.Sex.FEMALE") List<Employee> selectByLiteralEnum();
|
1.6 模糊查询
表达式 |
匹配 |
不匹配 |
E.name LIKE ‘张%’ |
张三 |
小张伟 |
E.name LIKE ‘张_’ |
张三 |
张三丰 |
E.name LIKE ‘张\_%’ |
张_三 |
张三 |
示例:查询张性的所有雇员
1 2
| @Query("SELECT E FROM Employee E WHERE E.name LIKE '张%'") List<Employee> selectByLikeLiteralString();
|
1.7 空集合查询
通过使用关键字IS [NOT] EMPTY
来查找关联的属性集合的值为空的记录。
示例:查找尚无雇员的所有部门
1 2
| @Query("SELECT D FROM Department D WHERE D.employees IS EMPTY") List<Department> selectByEmpty();
|
1.8 构造器
查询结果的类型如果不是持久化的实体类,必须使用该类的完全限定名。
语法:SELECT NEW 类的完全限定名(参数1, 参数2, ...) ...
示例:查询所有的雇员信息
1 2
| @Query("SELECT NEW org.fanlychie.model.SimpleEmployee(E.name, E.sex) FROM Employee E") List<SimpleEmployee> selectSimpleEmployees();
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| package org.fanlychie.model; public class SimpleEmployee { private String name; private Sex sex; public SimpleEmployee(String name, Sex sex) { this.name = name; this.sex = sex; } }
|
2. 更新
示例:更新某个雇员的婚姻状态和薪资信息
1 2 3 4
| @Modifying @Transactional @Query("UPDATE Employee SET married = ?2, salary = ?3 WHERE id = ?1") int update(Long id, Boolean married, Double salary);
|
@Query
无法进行 DML(Data Manipulation Language 数据操控语言,主要语句有 INSERT、DELETE、UPDATE)操作,如需更新数据库表的数据需要标注@Modifying
注解,并且需要使用支持事务的@Transactional
注解。
3. 删除
示例:删除没有雇员的部门信息
1 2 3 4
| @Modifying @Transactional @Query("DELETE FROM Department D WHERE D.employees IS EMPTY") int delete();
|
示例项目开发环境:Java-8、Maven-3、IntelliJ IDEA-2017、Spring Boot-1.5.2.RELEASE
完整示例项目链接:spring-boot-jpql-sample
参考文档文献链接:http://docs.oracle.com/javaee/7/tutorial/persistence-querylanguage.htm、http://docs.oracle.com/html/E13946_04/ejb3_langref.html