学习笔记:SpringData JPA条件查询、排序、分页查询

前言

在刚开始学习的时候,在dao的定义的接口须要继承JpaRepository<T, ID>接口JpaSpecificationExecutor< T >接口,可是一直以来我用到的都只是JpaRepository,用于自动生成相关SQL语句简化代码。而JpaSpecificationExecutor给个人感受就无关紧要了,直到最近才发现它的用处,在此记录一波。由于是学习笔记的关系,因此里面都只是截取关键的代码进行记录。java

正文

要想使用Spring Data JPA,须要在pom.xml中添加如下依赖:web

<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.1.7.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
</parent>

<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
<dependencies>

如前言所说,带条件的分页查询方法是被定义在JpaSpecificationExecutor接口中的,因此这里须要继承这个接口。spring

/** * @author Veggie * @date 2019/8/14 - 14:11 */
@Repository
public interface MessageRepository extends JpaRepository<Message, Long>, 
		JpaSpecificationExecutor<Message> {
}

1. 条件查询

自定义查询条件的步骤:app

  1. 实现Specification< T >接口(提供泛型;查询的对象类型)
  2. 实现toPredicate方法(构造查询条件)
  3. 须要借助方法参数中的两个参数:
    root:获取须要查询的对象属性
    CriteriaBuilder:构造查询条件,内部封装了不少查询条件(模糊匹配,精准匹配)
//自定义查询条件
Specification<Message> spec = new Specification<Message>() {
            @Override
            public Predicate toPredicate(Root<Message> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
                //根据属性名获取查询对象的属性
                Path<Message> path = root.get("receiverName");
                //至关于 where receiverName = "Veggie", CriteriaBuilder接口中还有不少查询条件,建议看源码
                Predicate equal = criteriaBuilder.equal(path, "Veggie");
                return equal;
            }
        }
//进行条件查询,findAll()方法中的参数即为条件
List<Message> result = MessageRepository.findAll(spec);

当查询条件用到gt, ge, lt, le, like(分别表示>, >=, <, <=,模糊查询)时,须要代表查询对象属性的类别,以下所示:ide

//查询用户名以"V"开头的用户
Predicate like = criteriaBuilder.like(path.as(String.class), "V%");

2. 排序

排序用到的是一个Sort类,它是查询的排序选项(看源码,有介绍)
它初始化是用到的参数第一个参数:
Sort.Direction.DESC表示降序
Sort.Direction.ASC表示升序
svg

随后的参数就是要排序的属性列表,能够有多个参数,也能够直接用List传,可是至少传入一个属性。spring-boot

Sort sort = new Sort(Sort.Direction.DESC, "id");
//sort做为findAll()方法中的参数,查询获得获得的结果是通过排序的
List<Message> result = MessageRepository.findAll(sort);

3.分页查询

分页须要设置分页参数类Pageable,初始化主要是有两个参数:第一个是查询的页码(下标从0开始),第二个是每页查询的条数,好比说结果有55条,若是每页查询10条,结果就会被分红6页。也能够添加
注意:原来用到的new PageRequest()已通过时,如今用PageRequest.of()来实现。学习

Page接口是封装为Spring Data Jpa 内部的page bean,它的经常使用方法以下:ui

//获取总页数
int getTotalPages();
//获取总记录数 
long getTotalElements();
//获取列表数据
List<T> getContent();

分页查询的代码以下:spa

//设置分页参数
Pageable pageable = PageRequest.of(05);
//分页查询
Page<Message> page = MessageRepository.findAll(pageable);

4. 完整的方法代码

完整的代码是以上三个知识点的集合,是带条件的分页查询,查询获得的结果按id号降序排序。

@RequestMapping(path = "/page")
    public List<Message> queryPage(@RequestBody Map<String, Object> params) {
        /** * 自定义查询条件 * 1. 实现Specification接口(提供泛型;查询的对象类型) * 2. 实现toPredicate方法(构造查询条件) * 3. 须要借助方法参数中的两个参数( * root:获取须要查询的对象属性 * CriteriaBuilder:构造查询条件,内部封装了不少查询条件(模糊匹配,精准匹配) */
        Specification<Message> spec = new Specification<Message>() {
            @Override
            public Predicate toPredicate(Root<Message> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
                Path<Message> path = root.get("receiverName");
                Predicate equal = criteriaBuilder.equal(path, "Veggie");
                return equal;
            }
        };

        Integer pageNo = Integer.valueOf((String) params.get("pageNo"));
        Integer pageSize = Integer.valueOf((String)params.get("pageSize"));
        /** * 添加排序Sort * Sort.Direction.DESC表示降序 * Sort.Direction.ASC表示升序 * properties是指实体类的属性名(不是字段名) */
        Sort sort = new Sort(Sort.Direction.ASC, "id");
        /** * 分页参数Pageable * 参数1:查询的页码 * 参数2:每页查询的条数 * 参数3:查询结果的排序规则(可选 */
        Pageable pageable = PageRequest.of(pageNo, pageSize, sort); //原来的new PageRequest()已通过时
        /** * 分页查询 * 参数1:查询条件Specification * 参数2:分页参数Pageable */
        Page<Message> page = MessageRepository.findAll(spec, pageable);
        return page.getContent();
    }

感悟

既然都写博客了,顺带记录最近学习以及和大佬交谈的感悟:

  1. 理解概念:在学习的时候,不能仅仅想着敲代码,要多关注一些相关概念的理解,就好比说JPA、 Hibernate和SpringData JPA各自的基本概念,他们之间有什么关系之类的。
  2. 多看源码:我具体也说不上来,就本能的感受这个很重要。有时候一看源码,能够理解以前想了好久都没有想明白的问题。其次,看某个类或接口的源码时,还能够了解到这个类能够提供什么方法(通常都会有英文注释的)。
  3. 思惟导图:就像看书必定要想看目录,学习新的内容应该先作个思惟导图。它以让咱们对同样东西的整体有比较直观、系统的认识,可让学习的思路变得清晰,能够避免浪费一些没必要要 的时间。
  4. 多作笔记:连dalao都记不住以前学过的东西,况且我等凡夫俗子。作笔记不只能够备忘,避免踩一样的坑,还能够加深对该部分知识的理解。

参考资料

  1. https://www.bilibili.com/video/av53910024/?p=65