Spring data jpa 的使用与详解(二):复杂动态查询及分页,排序

上一篇介绍了spring data jpa在spring boot中的基本搭建和整合,以及较为简单的查询,这一篇来说spring data jpa中如何实现较为复杂的查询及分页,排序。

1 使用Specification实现复杂查询

1.1 什么是Specification

Specification是springDateJpa中的一个接口,他是用于当jpa的一些基本CRUD操作的扩展,可以把他理解成一个spring jpa的复杂查询接口。其次我们需要了解Criteria 查询,这是是一种类型安全和更面向对象的查询。而Spring Data JPA支持JPA2.0的Criteria查询,相应的接口是JpaSpecificationExecutor。

而JpaSpecificationExecutor这个接口基本是围绕着Specification接口来定义的, Specification接口中只定义了如下一个方法:

Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb); 
复制代码

Criteria查询基本概念

Criteria 查询是以元模型的概念为基础的,元模型是为具体持久化单元的受管实体定义的,这些实体可以是实体类,嵌入类或者映射的父类。

CriteriaQuery接口

代表一个specific的顶层查询对象,它包含着查询的各个部分,比如:select 、from、where、group by、order by等注意:CriteriaQuery对象只对实体类型或嵌入式类型的Criteria查询起作用。

Root:

代表Criteria查询的根对象,Criteria查询的查询根定义了实体类型,能为将来导航获得想要的结果,它与SQL查询中的FROM子句类似。
Root实例是类型化的,且定义了查询的FROM子句中能够出现的类型。root代表查询的实体类,query可以从中得到root对象,告诉jpa查询哪一个实体类,还可以添加查询条件,还可以结合EntityManager对象 得到最终查询的 TypedQuery对象。 CriteriaBuilder接口

用来构建CritiaQuery的构建器对象Predicate:一个简单或复杂的谓词类型,其实就相当于条件或者是条件组合。 可通过 EntityManager.getCriteriaBuilder 而得。

2 使用Specification进行复杂的动态查询

maven的依赖继续使用上一章的就可以,这里修改一下实体类和controller层。

请求实体类:

@Data
public class AccountRequest {

    //从第几页开始
    private Integer page;

    //每一页查询多少
    private Integer limit;

    private String id;

    private String name;

    private String pwd;

    private String email;

    private Integer[] types;

}
复制代码

实体类:

@Data
@Entity
@Table(name = "account")
@ToString
@EntityListeners(AuditingEntityListener.class)
public class Account {

    @Id
    @GenericGenerator(name = "idGenerator", strategy = "uuid")
    @GeneratedValue(generator = "idGenerator")
    private String id;

    @Column(name = "username", unique = true, nullable = false, length = 64)
    private String username;

    @Column(name = "password", nullable = false, length = 64)
    private String password;

    @Column(name = "email", length = 64)
    private String email;

    @Column(name = "type")
    private Short type;

    @CreatedDate
    @Column(name = "create_time", nullable = false)
    private LocalDateTime createTime;

}
复制代码

Repository层:

public interface AccountRepository extends JpaRepository<Account,String>, JpaSpecificationExecutor<Account> {}
复制代码

controller层(还是直接略过service层)

@Autowired
    private AccountRepository repository;


    @PostMapping("/get")
    public List<Account> get(@RequestBody AccountRequest request){
        Specification<Account> specification = new Specification<Account>() {

            @Override
            public Predicate toPredicate(Root<Account> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder builder) {
                //所有的断言 及条件
                List<Predicate> predicates = new ArrayList<>();
                //精确匹配id pwd
                if (request.getId() != null) {
                    predicates.add(builder.equal(root.get("id"), request.getId()));
                }
                if (request.getPwd() != null) {
                    predicates.add(builder.equal(root.get("password"), request.getPwd()));
                }
                //模糊搜索 name
                if (request.getName() != null && !request.getName().equals("")) {
                    predicates.add(builder.like(root.get("username"), "%" + request.getName() + "%"));
                }
                if (request.getEmail() != null && !request.getEmail().equals("")) {
                    predicates.add(builder.like(root.get("email"), "%" + request.getEmail() + "%"));
                }
                //in范围查询
                if (request.getTypes() != null) {
                    CriteriaBuilder.In<Object> types = builder.in(root.get("type"));
                    for (Integer type : request.getTypes()) {
                        types = types.value(type);
                    }
                    predicates.add(types);
                }
                return builder.and(predicates.toArray(new Predicate[predicates.size()]));
            }
        };
        List<Account> accounts = repository.findAll(specification);

        return accounts;
    }
复制代码

通过重写Specification的toPredicate的方法,这样一个复杂的动态sql查询就完成了,通过post请求直接就可以调用了。

3 分页及排序

@PostMapping("/page")
    public List<Account> getPage(@RequestBody AccountRequest request){

        Specification<Account> specification = new Specification<Account>() {
            @Override
            public Predicate toPredicate(Root<Account> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                List<Predicate> predicates = new ArrayList<>();
                //do anything
                return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
            }
        };
        //表示通过createTime进行 ASC排序
        PageRequest page = new PageRequest(request.getPage() - 1, request.getLimit(), Sort.Direction.ASC, "createTime");
        Page<Account> pageInfo = repository.findAll(specification, page);

        return pageInfo.getContent();
    }
复制代码

上面的代码是在经过复杂查询并进行分页与排序,通过 PageRequest
来构建分页排序的规则。传入起始页及每页的数量,还有排序的规则及以哪个属性排序。 jpa中是以第0页开始的,所以传参的时候需要注意!

当然,如果你不需要进行复杂的查询也可以对数据进行分页及排序查询。

修改repository,使其继承 PagingAndSortingRepository

@Repository
public interface AccountRepository extends JpaRepository<Account,String>, JpaSpecificationExecutor<Account> , PagingAndSortingRepository<Account,String> {
    Page<Account> findByAge(int age, Pageable pageable);
}
复制代码

使用时先创建 pageable
参数,然后传进去就可以了。

//显示第1页每页显示3条
PageRequest pr = new PageRequest(1,3);
//根据年龄进行查询
Page<Account> stus = accountPageRepository.findByAge(22,pr); 
复制代码

排序也是一样的,在repository中创建方法

List<Account> findByPwd(String pwd, Sort sort);
复制代码

调用的时候传入sort对象

//设置排序方式为username降序
List<Account> accs = accountPageRepository.findByAge("123456",new Sort(Sort.Direction.DESC,"username"));
//设置排序以username和type进行升序
acc = accountPageRepository.findByAge("123456",new Sort(Sort.Direction.ASC,"username","type"));
//设置排序方式以name升序,以address降序
Sort sort = new Sort(new Sort.Order(Sort.Direction.ASC,"name"),new Sort.Order(Sort.Direction.DESC,"type"));
accs = accountPageRepository.findByAge("123456",sort);
复制代码

原文 

https://juejin.im/post/5d64f70fe51d4561fa2ec09c

本站部分文章源于互联网,本着传播知识、有益学习和研究的目的进行的转载,为网友免费提供。如有著作权人或出版方提出异议,本站将立即删除。如果您对文章转载有任何疑问请告之我们,以便我们及时纠正。

PS:推荐一个微信公众号: askHarries 或者qq群:474807195,里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

转载请注明原文出处:Harries Blog™ » Spring data jpa 的使用与详解(二):复杂动态查询及分页,排序

赞 (0)
分享到:更多 ()

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址