JPQL(Java Persistence Query Language,Java 持久化查询语言)和 SQL 之间有很多相似之处,它们之间主要的区别在于前者处理 JPA 实体类,而后者则直接涉及关系数据。在 JPQL 中,可以使用SELECTUPDATEDELETE语法来定义查询。

1. 查询

语法:SELECT ... FROM ... [WHERE ...] [GROUP BY ... [HAVING ...]] [ORDER BY ...]

FROM 子句
通过声明一个或多个标识符变量来定义查询的范围。在SELECTWHERE子句中可以引用这些变量。

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;
// getters and setters
}

# 雇员实体


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;
// getters and setters
}

# 性别枚举


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-2424L,支持 Java Long 范围的数值。

浮点类型:如24.24.6+24.6-24.624.6F24.6D,支持 Java Double 范围的数值。

示例:查询薪资大于10000的所有雇员

1
2
@Query("SELECT E FROM Employee E WHERE E.salary > 10000.0")
List<Employee> selectByLiteralNumber();

1.5.3 布尔

布尔类型的可选值为:TRUEFALSE,它们不区分大小写。

示例:查找已婚的所有雇员

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;
}
// getters and setters
}

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.htmhttp://docs.oracle.com/html/E13946_04/ejb3_langref.html