程序员

JPA框架下实现分页及排序查询

2020-07-13  本文已影响0人  梅西爱骑车

JpaRepository<T,ID>,两个变量分别是实体类名字和实体类的主键的类型。
因为JpaRepository<T,ID>继承PagingAndSortingRepository<T, ID>, CrudRepository<T, ID>,所以封装的就有增删查改功能和分页功能。
另外一篇类似文章,稍显复杂:https://www.jianshu.com/p/14cd90f32d4d

一、简单分页查询

JPA已经实现的分页接口,适用于简单的分页查询。代码实现:

1.1 POM文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.erbadagang.data.jpa</groupId>
    <artifactId>jpa</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>jpa</name>
    <description>JPA project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.3.0.RELEASE</spring-boot.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <!--MySQL数据库连接-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

1.2 application.properties

spring.application.name=jpa
# 应用服务 WEB 访问端口
server.port=8080
####数据库连接池###
spring.datasource.url=jdbc:mysql://101.133.227.13:3306/orders_1?useSSL=false&useUnicode=true&characterEncoding=UTF-8
spring.datasource.username=guo
spring.datasource.password=205010guo
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

1.3 UserRepository

写个接口UserRepository继承JpaRepository,持久层配置。

package com.erbadagang.data.jpa.repository;

import com.erbadagang.data.jpa.entity.UserEntity;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;

/**
 * (User)表数据库访问层
 *
 * @author 郭秀志 jbcode@126.com
 * @since 2020-07-13 11:13:01
 */
public interface UserRepository extends JpaRepository<UserEntity, Long> {
    
}

JpaRepository内部已经有好多接口,看到已经继承了分页排序的PagingAndSortingRepository

public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
    List<T> findAll();

    List<T> findAll(Sort var1);

    List<T> findAllById(Iterable<ID> var1);

    <S extends T> List<S> saveAll(Iterable<S> var1);

    void flush();

    <S extends T> S saveAndFlush(S var1);

    void deleteInBatch(Iterable<T> var1);

    void deleteAllInBatch();

    T getOne(ID var1);

    <S extends T> List<S> findAll(Example<S> var1);

    <S extends T> List<S> findAll(Example<S> var1, Sort var2);
}

简单分页查询正是使用PagingAndSortingRepository中的如下方法Page<T> findAll(Pageable var1)

@NoRepositoryBean
public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID> {
    Iterable<T> findAll(Sort var1);

    Page<T> findAll(Pageable var1);
}

1.4 测试代码

package com.erbadagang.data.jpa;

import com.erbadagang.data.jpa.entity.UserEntity;
import com.erbadagang.data.jpa.repository.UserRepository;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;

/**
 * @description JPA测试用例。
 * @ClassName: JpaApplicationTests
 * @author: 郭秀志 jbcode@126.com
 * @date: 2020/7/13 16:29
 * @Copyright:
 */
@SpringBootTest
@Slf4j
class JpaApplicationTests {

    @Autowired
    private UserRepository userRepository;

    @Test
    void contextLoads() {
    }

    @Test
    void testFindAll() {
        //Jpa 分页查询
        Sort sort = Sort.by(Sort.Direction.DESC, "id"); //通过id进行倒序,id 是Bean 中的变量,不是数据库中的字段(*)
        int page = 0;
        Pageable pageable = PageRequest.of(page, 5, sort);  // page 从 0 开始 ,5是指每个page的条数,这个意思是id排序分页查询,每次查询2个数据

        Page<UserEntity> userPage = userRepository.findAll(pageable);
        userPage.forEach(System.out::println);
        log.info("总记录数:" + userPage.getTotalElements());
    }
}
数据库的11条数据: 全部数据

运行测试代码,控制台输出的第一页数据:

UserEntity(id=13, name=弗鲁姆, age=22, email=null, createTime=null, updateTime=null, operator=null, deleteFlag=null, version=null)
UserEntity(id=12, name=天空车队, age=22, email=null, createTime=null, updateTime=null, operator=null, deleteFlag=null, version=null)
UserEntity(id=9, name=梅花, age=8, email=look@erbadagang.cn, createTime=2020-07-12 01:03:51.0, updateTime=2020-07-12 01:03:51.0, operator=梅西爱骑车, deleteFlag=0, version=2)
UserEntity(id=8, name=闪电, age=18, email=specialized@erbadagang.cn, createTime=2020-07-12 00:11:09.0, updateTime=2020-07-12 00:11:09.0, operator=梅西爱骑车, deleteFlag=1, version=null)
UserEntity(id=7, name=trek, age=28, email=trek@erbadagang.cn, createTime=2020-07-11 14:57:43.0, updateTime=2020-07-11 23:11:53.0, operator=梅西爱骑车, deleteFlag=null, version=null)
2020-07-13 16:47:33.192  INFO 8060 --- [main] c.e.data.jpa.JpaApplicationTests         : 总记录数:11

可以看到,自带的接口findAll(pageable pageable)只有一个分页参数,但是没有带查询参数。

二、有查询参数分页

如果我们需要查询某个条件下的分页,对于分页操作,需要使用到 Pageable 参数,需要作为方法的最后一个参数

2.1 自定义带过滤条件的JPA接口

package com.erbadagang.data.jpa.repository;

import com.erbadagang.data.jpa.entity.UserEntity;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;

/**
 * (User)表数据库访问层
 *
 * @author 郭秀志 jbcode@126.com
 * @since 2020-07-13 11:13:01
 */
public interface UserRepository extends JpaRepository<UserEntity, Long> {
    //查询某年龄的所有用户数据并分页
    Page<UserEntity> findByAgeLessThan(Integer age, Pageable pageable);
}

JPA写的方法名称通过关键字来定义功能,但是findByAgeLessThan中并没有带分页关键字,是根据返回的类型自动判断是否分页,我们这里返回类型 Page<UserEntity>
如果返回类型为Page,则返回的数据是带分页参数的集合,如果返回类型是list,则返回的数据是list集合。

2.2 测试

测试用例:

    @Test
    void testFindByAgeLessThanPage() {
        //Jpa 分页查询
        Sort sort = Sort.by(Sort.Direction.DESC, "id"); //通过id进行倒序,id 是Bean 中的变量,不是数据库中的字段(*)
        int page = 0;
        Pageable pageable = PageRequest.of(page, 2, sort);  // page 从 0 开始 ,2是指每个page的大小,这个意思是id排序分页查询,每次查询2个数据

        Page<UserEntity> userPage = userRepository.findByAgeLessThan(20, pageable);
        userPage.getContent().forEach(System.out::println);
        log.info("总记录数:" + userPage.getTotalElements());
        log.info("总页数:" + userPage.getTotalPages());
    }

运行测试代码,控制台输出的第一页数据:

UserEntity(id=9, name=梅花, age=8, email=look@erbadagang.cn, createTime=2020-07-12 01:03:51.0, updateTime=2020-07-12 01:03:51.0, operator=梅西爱骑车, deleteFlag=0, version=2)
UserEntity(id=8, name=闪电, age=18, email=specialized@erbadagang.cn, createTime=2020-07-12 00:11:09.0, updateTime=2020-07-12 00:11:09.0, operator=梅西爱骑车, deleteFlag=1, version=null)
2020-07-13 17:03:06.020  INFO 21144 --- [           main] c.e.data.jpa.JpaApplicationTests         : 总记录数:4
2020-07-13 17:03:06.020  INFO 21144 --- [           main] c.e.data.jpa.JpaApplicationTests         : 总页数:2

结论:JPA是根据返回的类型自动判断是否分页,如果返回类型为Page,则返回的数据是带分页参数的集合

2.3 稍复杂的条件

UserRepository新增一个多条件组合的查询并分页方法。

    //查询<某年龄,并且姓名包括某字符串的所有用户数据并分页
    Page<UserEntity> findByAgeLessThanAndNameContains(Integer age, String name, Pageable pageable);

测试代码


    @Test
    void testfindByAgeLessThanAndNameContains() {
        //Jpa 分页查询
        Sort sort = Sort.by(Sort.Direction.DESC, "id"); //通过id进行倒序,id 是Bean 中的变量,不是数据库中的字段(*)
        int page = 0;
        Pageable pageable = PageRequest.of(page, 2, sort);  // page 从 0 开始 ,2是指每个page的大小,这个意思是id排序分页查询,每次查询2个数据

        Page<UserEntity> userPage = userRepository.findByAgeLessThanAndNameContains(20, "Guo", pageable);
        userPage.getContent().forEach(System.out::println);
        log.info("总记录数:" + userPage.getTotalElements());
        log.info("总页数:" + userPage.getTotalPages());
    }

运行测试代码,控制台输出的第一页数据:

UserEntity(id=1, name=Guo , age=18, email=trek@erbadagang.com, createTime=null, updateTime=null, operator=null, deleteFlag=null, version=null)
2020-07-13 17:13:51.212  INFO 484 --- [           main] c.e.data.jpa.JpaApplicationTests         : 总记录数:1
2020-07-13 17:13:51.212  INFO 484 --- [           main] c.e.data.jpa.JpaApplicationTests         : 总页数:1

2.4 复杂SQL分页

如果SQL逻辑比较复杂,可以通过自己写@Query注解,而不是通过简单的方法名来实现。如:

  /**
   * 一个参数name,匹配两个查询条件字段(c.firstName=:name or c.lastName=:name)
   * @param name2
   * @Param pageable 分页参数
   * @return
   * 这里Param的值和=:后面的参数匹配,但不需要和方法名对应的参数值对应
   * 这里增加了@QueryHints注解,是给查询添加一些额外的提示, 比如:当前的name值为HINT_COMMENT是在查询的时候带上一些备注信息
   */
  @QueryHints(value = { @QueryHint(name = HINT_COMMENT, value = "a query for pageable")})
  @Query("select c from Customer c where c.firstName=:name or c.lastName=:name")
  Page<Customer> findByName(@Param("name") String name2,Pageable pageable);

底线


本文源代码使用 Apache License 2.0开源许可协议,可从Gitee代码地址通过git clone命令下载到本地或者通过浏览器方式查看源代码。

上一篇下一篇

猜你喜欢

热点阅读