Spring Data JPA入门记录(二)动态查询

6 阅读1分钟

在日常开发中,我们可能会遇到不固定的查询需求,如果仅用方法名或者@Query注解的方式定义查询语句,会导致方法爆炸,代码难以维护。因此JPA提供了Specification ,允许开发者以动态、类型安全的方式构建复杂查询。

一、Specification相关接口介绍

首先,需要继承 JpaRepository<T, ID> 和 JpaSpecificationExecutor<T> 接口,JpaSpecificationExecutor接口为Specification动态查询提供了支持:


public interface AdminRepository extends JpaRepository<Admin, Long>, JpaSpecificationExecutor<Admin> {
}

JpaSpecificationExecutor接口中的源码可见,Specification可以作为查询参数传入JpaSpecificationExecutor接口的方法中:

public interface JpaSpecificationExecutor<T> {
    Optional<T> findOne(@Nullable Specification<T> spec);

    List<T> findAll(@Nullable Specification<T> spec);

    Page<T> findAll(@Nullable Specification<T> spec, Pageable pageable);

    List<T> findAll(@Nullable Specification<T> spec, Sort sort);

    long count(@Nullable Specification<T> spec);

    boolean exists(Specification<T> spec);
}

Specification接口内容如下:

public interface Specification<T> {  
    Predicate toPredicate(Root<T> root,  
        CriteriaQuery<?> query,  
        CriteriaBuilder cb);  
}
  • root:查询实体,root.get("name")等价于SQL语句中的user.name。需要注意的是,get("name")中的名称必须是实体类中的字段名,不是数据库列名
  • query:整个查询对象
  • cb:条件构造器,包含and,between,equal等方法,用于构建SQL查询语句,包含的其他方法可参考CriteriaBuilder的官方api文档

二、Specification常见的的使用方法

1、构造Specification

public static Specification<User> build(
        String name,
        Integer minAge,
        Integer maxAge) {

    return (root, query, cb) -> {
        //根据不同条件,确定是否添加查询语句
        List<Predicate> predicates = new ArrayList<>();
        if (name != null && !name.isEmpty()) {
            predicates.add(
                cb.like(root.get("name"), "%" + name + "%")
            );
        }
        if (minAge != null) {
            predicates.add(
                cb.greaterThanOrEqualTo(root.get("age"), minAge)
            );
        }
        if (maxAge != null) {
            predicates.add(
                cb.lessThanOrEqualTo(root.get("age"), maxAge)
            );
        }
        return cb.and(predicates.toArray(new Predicate[0]));
    };
}

2、调用查询方法:

Specification<User> spec = UserSpecification.build(
        "Tom",
        18,
        30
);

Pageable pageable = PageRequest.of(0, 10);
Page<User> page = userRepository.findAll(spec, pageable);

生成的SQL语句类似于:

select * from user 
where name like '%Tom%' 
and age >= 18 
and age <= 30
limit 0,10