结合SpringDataJPA中的PagingAndSortingRepository和 JpaSpecificationExecutor接口实现大数据量的数据分页查询
2017-09-23 10:54
2016 查看
其中PagingAndSortingRepository用于分页和排序功能的实现,JpaSpecificationExecutor用于条件查询(主要是针对一张表中的数据进行查询)
其中第二个findAll(Pageable var1)中封装的功能特别强大,参数是Pageable类型,这个var1参数中包含了pageNumber和PageSize两个参数,当然,还可以添加Sort参数,在分页时进行排序。像这样:
这个page对象中存储了所有分页的信息.
(下面的代码是从网上复制粘贴的,仅用于展示其操作)
这里外加一个衍生查询的定义,需要的时候可以用到:
PagingAndSortingRepository接口中只有findAll方法,想要实现分页我就就要集成这个接口,当然我们在使用PagingAndSortingRepository提供的findAll方法的同时,还可以使用SpringDataJPA中Repository的衍生查询,(也就是自定义根据属性名称或者是添加@Query查询的方法,比如:findByAge) ,在这些衍生查询方法的参数中只要把pageable参数放置在最后一个,就可以实现同时分页的功能。像是这样(下面是一段手写的kotlin代码):
要使用这种方式,就需要继承我上面说过的接口:JpaSpecificationExecutor,这个接口有以下方法:
这个接口通过Specification来定义查询的条件,(也就是说,在这里,在使用Specification查询条件的同时,使用上面第三个方法,将Pageable作为参数传入,就能实现查询分页。)在实现Specification这个接口的同时,重写其中的toPredict方法,自定义查询的条件。用kotlin语言实现的方法如下:
上面是一个简单的equal的JPQL语句的构建,当然这里可以构建更多的查询方式,是这样来链接的,形成or或者and语句.
这里的 root[“name”]中的root是当前查询的表名称,name为root实体中的一个属性名。
CriteriaBuilder接口中提供了很多方法:
Equal:val predict:Predicate = criteriaBuilder.equal(userName, name)
Like: val predict:Predicate = criteriaBuilder.like(userName, “%${name}%”)
lessThan:val predict:Predicate = criteriaBuilder.lessThan(createDate, createAt)
等等。其中第一个参数为Path类型,第二个参数是查询条件,如果这个参数值符合查询条件,则会被筛选出来。
**—————————————————这里是分割线
下面实现 根据user 的name、age、createDate进行筛选查询分页功能的实现,其中,name进行模糊查询,age查询符合某个年龄段的User进行相等查询,createDate为符合某个用户创建时间段的查询。这三种要求进行and操作。
看明白了没?
不过针对上面这种多条件的查询,常涉及到的功能是多条件模糊查询的集合。而有些条件用户可能不想进行筛选,这时候,就涉及到上面的条件p1/p2/p3中可能有一个为空,或者两个为空。然后criteriaBuilder.and()或者criteriaBuilder.or()中的参数是不能为空的,这就遇到一个比较头疼的问题,用kotlin来解决的话,用机械式的笨拙方法解决,写出来的代码实在是太丑了,也就是吧这些条件添加到一个集合中,去空之后,再进行forEach到criteriaBuilder中。下面记录一下比较好的解决方法:
拓展:关于list的varags的操作
参考:kotlin中文官网
spring data jpa 利用JpaSpecificationExecutor做复杂查询
Spring Boot系列(五):spring data jpa的使用
SpringBoot第二讲利用Spring Data JPA实现数据库的访问(二)_分页和JpaSpecificationExecutor接口介绍
一、下面先针对这两个接口进行详细的介绍,第二部分为实际应用的实例,方便实践和测试:
第一部分:PagingAndSortingRepository
1.PagingAndSortingRepository接口提供了一下接口:
Iterable<T> findAll(Sort var1); //得到经过排序的所有数据 Page<T> findAll(Pageable var1); //经过分页之后的某一页的数据
其中第二个findAll(Pageable var1)中封装的功能特别强大,参数是Pageable类型,这个var1参数中包含了pageNumber和PageSize两个参数,当然,还可以添加Sort参数,在分页时进行排序。像这样:
Pageable page = PageRequest(pageNumber,pageSize,sort) Pageable page = PageRequest(pageNumber,pageSize)
这个page对象中存储了所有分页的信息.
2.Pageable
Pageable 是一个接口,他的实现类是PageRequest。这个PageRequest有三个实现构造方法:(下面的代码是从网上复制粘贴的,仅用于展示其操作)
//这个构造出来的分页对象不具备排序功能 public PageRequest(int page, int size) { this(page, size, (Sort)null); } //Direction和properties用来做排序操作 public PageRequest(int page, int size, Direction direction, String... properties) { this(page, size, new Sort(direction, properties)); } //自定义一个排序的操作 public PageRequest(int page, int size, Sort sort) { super(page, size); this.sort = sort; }
这里外加一个衍生查询的定义,需要的时候可以用到:
PagingAndSortingRepository接口中只有findAll方法,想要实现分页我就就要集成这个接口,当然我们在使用PagingAndSortingRepository提供的findAll方法的同时,还可以使用SpringDataJPA中Repository的衍生查询,(也就是自定义根据属性名称或者是添加@Query查询的方法,比如:findByAge) ,在这些衍生查询方法的参数中只要把pageable参数放置在最后一个,就可以实现同时分页的功能。像是这样(下面是一段手写的kotlin代码):
fun findByAge:Iterable<T>(pageNumber:int,pageSize:int,age:Int){ Val page = PageRequest(pageNumber,pageSize) Val list = findByAge(age,page) Return list }
第二部分:
SpringDataJPA在提供分页的同时,也提供了类似于hibernate的Criteria的查询方式:要使用这种方式,就需要继承我上面说过的接口:JpaSpecificationExecutor,这个接口有以下方法:
Optional<T> findOne(@Nullable Specification<T> var1); List<T> findAll(@Nullable Specification<T> var1); Page<T> findAll(@Nullable Specification<T> var1, Pageable var2); List<T> findAll(@Nullable Specification<T> var1, Sort var2); long count(@Nullable Specification<T> var1);
这个接口通过Specification来定义查询的条件,(也就是说,在这里,在使用Specification查询条件的同时,使用上面第三个方法,将Pageable作为参数传入,就能实现查询分页。)在实现Specification这个接口的同时,重写其中的toPredict方法,自定义查询的条件。用kotlin语言实现的方法如下:
class mySpec : Specification<User> { override fun toPredicate(root: Root<User>, criteriaQuery: CriteriaQuery<*>, criteriaBuilder: CriteriaBuilder): Predicate { val userName: Path<String> = root["name"] return criteriaBuilder.equal(userName, name) } }
上面是一个简单的equal的JPQL语句的构建,当然这里可以构建更多的查询方式,是这样来链接的,形成or或者and语句.
这里的 root[“name”]中的root是当前查询的表名称,name为root实体中的一个属性名。
CriteriaBuilder接口中提供了很多方法:
Equal:val predict:Predicate = criteriaBuilder.equal(userName, name)
Like: val predict:Predicate = criteriaBuilder.like(userName, “%${name}%”)
lessThan:val predict:Predicate = criteriaBuilder.lessThan(createDate, createAt)
等等。其中第一个参数为Path类型,第二个参数是查询条件,如果这个参数值符合查询条件,则会被筛选出来。
二、实践代码实例
这里关于这两个接口的详细介绍就完了,下面是我实际中写的代码实例:为了不暴露代码还要体现出使用的基本逻辑,暂时改为用User类来代替。首先:Dao层的接口继承
interface PagingRepository : PagingAndSortingRepository<User, Int>, JpaSpecificationExecutor<User>
service层和controller层
然后,我这里是没有写serviceImpl的,因为这边代码量少,也不属于单独一个模块,所以直接将实现方法写在了controller层中。(哈哈,这里说代码量少…确实是少,因为我们初期如果不适用、SpringDataJPA中的接口,想要实现分页模糊查询,可能要写很多复杂的sql语句以及代码的实现,模糊查询写一些sql语句去实现,分页新建一个Page类,在每次请求使用这个工具类,并且可能还需要表与表之间的级联查询。好在我是在同一张表中查数据。 那么问题来了,怎么样实现大数据量中的符合条件的分页查询功能呢?这个就会很难链接到一起同时实现,我目前能想到的就是先吧所有的都查出来,再分页发送给前端,这样做只是减少了前端的鸭梨,后台还是同样的工作。 但是使用了这两个接口就不一样啦,短短几行代码就搞定。)**—————————————————这里是分割线
下面实现 根据user 的name、age、createDate进行筛选查询分页功能的实现,其中,name进行模糊查询,age查询符合某个年龄段的User进行相等查询,createDate为符合某个用户创建时间段的查询。这三种要求进行and操作。
fun loadFilesByGroupTag(@RequestParam pageNumber: Int, @RequestParam pageSize: Int,@PathVariable userName: String, @RequestParam userAge: Int,@RequestParam createDate: Int): Page<Any> { class mySpec : Specification<File> { override fun toPredicate(root: Root<File>, criteriaQuery: CriteriaQuery<*>, criteriaBuilder: CriteriaBuilder): Predicate { val userName: Path<String> = root["name"] val userAge: Path<Int> = root["Age"] val userCreateDate: Path<Date> = root["createDate"] //这里可以将上面的uName的定义直接写在下面的like语句中,但是这里equal语句是不能写的,会报错,除非单独写出来,通过Path<T>来定义他的类型。可以多加尝试。 val p1 = criteriaBuilder.like(userName,"%uName%") //uName为用户模糊筛选的值 val p2 = criteriaBuilder.equal(userAge,uAge) val p3 = criteriaBuilder.lessThan(usercreateDate,"%createDate%") //小于... return criteriaBuilder.and(p1,p2,p3) //如果是或语句,这里用or就ok } } val pageable = PageRequest(pageNumber, pageSize) val page = pagingRepository.findAll(mySpec(), pageable) println("当前第几页:" + (page.number + 1)) println("当前页总共有几条数据:" + page.numberOfElements) println("总共多少条符合条件的数据:" + page.totalElements) println("总共多少页:" + page.totalPages) return page }
看明白了没?
不过针对上面这种多条件的查询,常涉及到的功能是多条件模糊查询的集合。而有些条件用户可能不想进行筛选,这时候,就涉及到上面的条件p1/p2/p3中可能有一个为空,或者两个为空。然后criteriaBuilder.and()或者criteriaBuilder.or()中的参数是不能为空的,这就遇到一个比较头疼的问题,用kotlin来解决的话,用机械式的笨拙方法解决,写出来的代码实在是太丑了,也就是吧这些条件添加到一个集合中,去空之后,再进行forEach到criteriaBuilder中。下面记录一下比较好的解决方法:
fun loadFilesByGroupTag(@RequestParam pageNumber: Int, @RequestParam pageSize: Int,@PathVariable userName: String, @RequestParam userAge: Int,@RequestParam createDate: Int): Page<Any> { class mySpec : Specification<File> { override fun toPredicate(root: Root<File>, criteriaQuery: CriteriaQuery<*>, criteriaBuilder: CriteriaBuilder): Predicate { val userName: Path<String> = root["name"] val userAge: Path<Int> = root["Age"] val userCreateDate: Path<Date> = root["createDate"] val p1 = criteriaBuilder.like(userName,"%uName%") val p2 = criteriaBuilder.equal(userAge,uAge) val p3 = criteriaBuilder.lessThan(usercreateDate,"%createDate%") val list = mutableListOf<Predicate>() with(criteriaBuilder) { uName?.let { list.add(like(userName, "%$uName%")) } uAge?.let { list.add(like(userAge, "%$uAge")) } createDate?.let { list.add(equal(userCreateDate, createDate)) } } return and(*list.toTypedArray()) //这里将list先装换为array,然后再展开,涉及到list的varags } } val pageable = PageRequest(pageNumber, pageSize) val page = pagingRepository.findAll(mySpec(), pageable) return page }
拓展:关于list的varags的操作
//Use spread operator, see example from M2 release notes472. Unfortunately, it is not described in main Koltin docs. fun printAll(vararg a : String) { for (item in a) println(item) } fun main(args: Array<String>) { printAll("one", "two") printAll(*args) }
三、运算符集合
运算符: /** * 运算符 */ public enum Operator { /** 等于 */ eq(" = "), /** 不等于 */ ne(" != "), /** 大于 */ gt(" > "), /** 小于 */ lt(" < "), /** 大于等于 */ ge(" >= "), /** 小于等于 */ le(" <= "), /** 类似 */ like(" like "), /** 包含 */ in(" in "), /** 为Null */ isNull(" is NULL "), /** 不为Null */ isNotNull(" is not NULL "); Operator(String operator) { this.operator = operator; } private String operator; public String getOperator() { return operator; } public void setOperator(String operator) { this.operator = operator; } }
参考:kotlin中文官网
spring data jpa 利用JpaSpecificationExecutor做复杂查询
Spring Boot系列(五):spring data jpa的使用
SpringBoot第二讲利用Spring Data JPA实现数据库的访问(二)_分页和JpaSpecificationExecutor接口介绍
相关文章推荐
- Spring boot data JPA 自定义JPQL语句,以及PagingAndSortingRepository接口实现分页查询
- 【SpringData】轻松愉快之玩转SpringData( 第5章 CrudRepository PagingAndSortingRespsitory JpaRepository接口)
- spring data 接口之 PagingAndSortingRepository
- Spring Data JPA(2)--利用PagingAndSortingRespository实现分页和排序
- SpringBoot第二讲利用Spring Data JPA实现数据库的访问(二)_分页和JpaSpecificationExecutor接口介绍
- SpringData 学习(7)—— 学习 Repository 接口的第 2 个子接口 PagingAndSortingRepository
- SpringData_PagingAndSortingRepository接口
- SpringBoot第二讲 利用Spring Data JPA实现数据库的访问(二)_分页和JpaSpecificationExecutor接口介绍
- 【SpringData】轻松愉快之玩转SpringData( 第5章 JpaSpecificationExecutor接口 第6章 课程总结 )
- spring data 接口之 JpaRepository,JpaSpecificationExecutor
- Spring Data JPA(3)--JpaRepository和JpaSpecificationExecutor的结合使用
- spring data 接口之 JpaRepository,JpaSpecificationExecutor
- spring data 接口之 JpaRepository,JpaSpecificationExecutor
- spring data 接口之 JpaRepository,JpaSpecificationExecutor
- spring data jpa中继承JpaSpecificationExecutor接口的接口不会被IOC容器所管理
- pagingandsortingrepository接口实现分页查询
- 在Spring Boot中使用Spring-data-jpa实现分页查询
- Spring技术内幕之Spring Data JPA-自定义Repository实现
- Spring data jpa 实现简单动态查询的通用Specification方法
- 利用AOP为Spring Data Jpa的接口Repository添加全局自定义过滤