JPA动态条件查询的具体实现(附带实例)
虽然 Spring Data JPA 通过非常简单的 AND 或者 OR 等关键字就能实现很多数据查询功能,但如果查询参数很多、条件很复杂,这种自定义简单查询的方法名就会变得特别长,并且还不能解决动态多条件查询的问题。
在介绍 JpaSpecificationExecutor 之前,我们需要了解 Criteria 中的几个概念:
Spring Data JPA 为我们提供了 JpaSpecificationExecutor 接口来实现动态条件查询功能,该接口提供了如下方法:
在 JpaSpecificationExecutor 的接口参数中,Pageable 与 Sort 应该是比较简单的,分别是分页参数和排序参数,重点是 Specification 参数。JpaSpecificationExecutor 接口通过 Specification 来定义各种复查的查询条件,我们再查看一下 Specification 源码,来看看 Specification 接口的定义:
使用 Specification 的核心是通过 CriteriaBuilder 创建查询条件,之后返回一个 Predicate 对象。这个对象中就有了相应的查询需求。示例代码如下:
最后,单击 Run Test 或在方法上右击,选择 Run 'testPageQuery',运行单元测试方法,结果如下图所示。

图 1 动态条件查询的运行结果
结果表明创建的单元测试运行成功,并查询出了姓名包含 w、年龄大于 30 的 User 数据,说明使用 JPA 实现了动态条件查询功能。
JpaSpecificationExecutor介绍
JpaSpecificationExecutor 是 JPA 2.0 提供的 Criteria API 的使用封装,可以用于动态生成查询条件来满足业务中的各种复杂数据查询场景。在介绍 JpaSpecificationExecutor 之前,我们需要了解 Criteria 中的几个概念:
- Predicate类:简单或复杂的谓词类型,用来拼接条件;
- Criteria:以元模型的概念为基础,元模型是为具体持久化单元的受管实体定义的,这些实体可以是实体类、嵌入类或者映射的父类;
- Root<T> root:代表可以查询和操作的实体对象的根,通过 get("属性名") 来获取对应的值。
- CriteriaQuery<?> query:代表一个 specific 的顶层查询对象,它包含着查询的各个部分,比如 select、from、where、group by、order by等;
- CriteriaBuilder cb:构建 CriteriaQuery 的构建器对象,其实就相当于条件或者条件组合,并以 Predicate 的形式返回。
Spring Data JPA 为我们提供了 JpaSpecificationExecutor 接口来实现动态条件查询功能,该接口提供了如下方法:
public interface JpaSpecificationExecutor<T> { // 根据Specification 条件查询单个对象,需要注意的是,如果条件能查出来多个就会报错 T findOne(@Nullable Specification<T> spec); // 根据Specification条件查询List结果 List<T> findAll(@Nullable Specification<T> spec); // 根据Specification条件分页查询 Page<T> findAll(@Nullable Specification<T> spec, Pageable pageable); // 根据Specification条件提供带排序的查询结果 List<T> findAll(@Nullable Specification<T> spec, Sort sort); // 根据Specification条件查询数量 long count(@Nullable Specification<T> spec); }从源码可以看到,JpaSpecificationExecutor 接口提供了 findOne()、findAll()、count() 等方法,其功能主要是通过构建 Specification 对象实现数据查询的功能。
在 JpaSpecificationExecutor 的接口参数中,Pageable 与 Sort 应该是比较简单的,分别是分页参数和排序参数,重点是 Specification 参数。JpaSpecificationExecutor 接口通过 Specification 来定义各种复查的查询条件,我们再查看一下 Specification 源码,来看看 Specification 接口的定义:
public interface Specification<T> { Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb); }可以看到 Specification 定义了 toPredicate() 方法,返回的是动态查询的数据结构,可以通过 toPredicate() 方法实现动态条件查询功能。
实现动态条件查询
首先,修改 UserRepository,继承 JpaSpecificationExecutor 接口。示例代码如下:public interface UserRepository extends JpaSpecificationExecutor<User>, JpaRepository<User, Long> { … }然后,创建单元测试,定义 Specification 测试方法。
使用 Specification 的核心是通过 CriteriaBuilder 创建查询条件,之后返回一个 Predicate 对象。这个对象中就有了相应的查询需求。示例代码如下:
@Test public void testSpecificaiton() { List<User> users = userRepository.findAll(new Specification<User>() { @Override public Predicate toPredicate(Root<User> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) { Predicate p1 = criteriaBuilder.like(root.get("name"), "%w%"); Predicate p2 = criteriaBuilder.greaterThan(root.get("age"),30); // 将两个查询条件联合起来之后返回Predicate对象 return criteriaBuilder.and(p1,p2); } }); for (User u : users){ System.out.println("name:"+u.getName()+",age:"+u.getAge()); } }上面的示例是根据不同条件来动态查询 User 数据,根据这个思路我们可以不断扩展,以完成更复杂的动态 SQL 查询。
最后,单击 Run Test 或在方法上右击,选择 Run 'testPageQuery',运行单元测试方法,结果如下图所示。

图 1 动态条件查询的运行结果
结果表明创建的单元测试运行成功,并查询出了姓名包含 w、年龄大于 30 的 User 数据,说明使用 JPA 实现了动态条件查询功能。