首页 > 编程笔记 > Java笔记 阅读:25

JPA动态条件查询的具体实现(附带实例)

虽然 Spring Data JPA 通过非常简单的 AND 或者 OR 等关键字就能实现很多数据查询功能,但如果查询参数很多、条件很复杂,这种自定义简单查询的方法名就会变得特别长,并且还不能解决动态多条件查询的问题。

JpaSpecificationExecutor介绍

JpaSpecificationExecutor 是 JPA 2.0 提供的 Criteria API 的使用封装,可以用于动态生成查询条件来满足业务中的各种复杂数据查询场景。

在介绍 JpaSpecificationExecutor 之前,我们需要了解 Criteria 中的几个概念:
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 实现了动态条件查询功能。

相关文章