文档章节

在Spring Boot中使用Spring-data-jpa实现分页查询

老虎是个蛋蛋
 老虎是个蛋蛋
发布于 2017/01/07 18:52
字数 1868
阅读 9864
收藏 34

上一篇我们简单介绍了在spring Boot中使用Spring-data-jpa操作数据库,本篇着重讲一下如何使用jpa进行分页。

    在我们平时的工作中,查询列表在我们的系统中基本随处可见,那么我们如何使用jpa进行多条件查询以及查询列表分页呢?下面我将介绍两种多条件查询方式。

1、引入起步依赖   

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

2、对thymeleaf和jpa进行配置

    打开application.yml,添加以下参数,以下配置在之前的文章中介绍过,此处不做过多说明

spring:
  thymeleaf:
    cache: true
    check-template-location: true
    content-type: text/html
    enabled: true
    encoding: utf-8
    mode: HTML5
    prefix: classpath:/templates/
    suffix: .html
    excluded-view-names:
    template-resolver-order:
  datasource:
      driver-class-name: com.mysql.jdbc.Driver
      url: jdbc:mysql://localhost:3306/restful?useUnicode=true&characterEncoding=UTF-8&useSSL=false
      username: root
      password: root
      initialize: true
  init-db: true
  jpa:
      database: mysql
      show-sql: true
      hibernate:
        ddl-auto: update
        naming:
          strategy: org.hibernate.cfg.ImprovedNamingStrategy

3、编写实体Bean

@Entity
@Table(name="book")
public class Book {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", updatable = false)
    private Long id;

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

    @Column(nullable = false,name = "isbn")
    private String isbn;

    @Column(nullable = false,name = "author")
    private String author;

    public Book (String name,String isbn,String author){
        this.name = name;
        this.isbn = isbn;
        this.author = author;
    }
    public Book(){

    }
    //此处省去get、set方法
}
public class BookQuery {
    private String name;
    private String isbn;
    private String author;
    //此处省去get、set方法
}

4、编写Repository接口

@Repository("bookRepository")
public interface BookRepository extends JpaRepository<Book,Long> 
        ,JpaSpecificationExecutor<Book> {
}

    此处继承了两个接口,后续会介绍为何会继承这两个接口

5、抽象service层

    首先抽象出接口

public interface BookQueryService {
    Page<Book> findBookNoCriteria(Integer page,Integer size);
    Page<Book> findBookCriteria(Integer page,Integer size,BookQuery bookQuery);
}

    实现接口

@Service(value="bookQueryService")
public class BookQueryServiceImpl implements BookQueryService {
    @Resource
    BookRepository bookRepository;
    @Override
    public Page<Book> findBookNoCriteria(Integer page,Integer size) {
        Pageable pageable = new PageRequest(page, size, Sort.Direction.ASC, "id");
        return bookRepository.findAll(pageable);
    }

    @Override
    public Page<Book> findBookCriteria(Integer page, Integer size, final BookQuery bookQuery) {
        Pageable pageable = new PageRequest(page, size, Sort.Direction.ASC, "id");
        Page<Book> bookPage = bookRepository.findAll(new Specification<Book>(){
            @Override
            public Predicate toPredicate(Root<Book> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
                List<Predicate> list = new ArrayList<Predicate>();
                if(null!=bookQuery.getName()&&!"".equals(bookQuery.getName())){
                    list.add(criteriaBuilder.equal(root.get("name").as(String.class), bookQuery.getName()));
                }
                if(null!=bookQuery.getIsbn()&&!"".equals(bookQuery.getIsbn())){
                    list.add(criteriaBuilder.equal(root.get("isbn").as(String.class), bookQuery.getIsbn()));
                }
                if(null!=bookQuery.getAuthor()&&!"".equals(bookQuery.getAuthor())){
                    list.add(criteriaBuilder.equal(root.get("author").as(String.class), bookQuery.getAuthor()));
                }
                Predicate[] p = new Predicate[list.size()];
                return criteriaBuilder.and(list.toArray(p));
            }
        },pageable);
        return bookPage;
    }
}

    此处我定义了两个接口,findBookNoCriteria是不带查询条件的,findBookCriteria是带查询条件的。在此处介绍一下上面提到的自定义Repository继承的两个接口,如果你的查询列表是没有查询条件,只是列表展示和分页,只需继承JpaRepository接口即可,但是如果你的查询列表是带有多个查询条件的话则需要继承JpaSpecificationExecutor接口,这个接口里面定义的多条件查询的方法。当然不管继承哪个接口,当你做分页查询时,都是需要调用findAll方法的,这个方法是jap定义好的分页查询方法。

    findBookCriteria方法也可以使用以下方法实现,大家可以自行选择

 @Override
    public Page<Book> findBookCriteria(Integer page, Integer size, final BookQuery bookQuery) {
        Pageable pageable = new PageRequest(page, size, Sort.Direction.ASC, "id");
        Page<Book> bookPage = bookRepository.findAll(new Specification<Book>(){
            @Override
            public Predicate toPredicate(Root<Book> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
                Predicate p1 = criteriaBuilder.equal(root.get("name").as(String.class), bookQuery.getName());
                Predicate p2 = criteriaBuilder.equal(root.get("isbn").as(String.class), bookQuery.getIsbn());
                Predicate p3 = criteriaBuilder.equal(root.get("author").as(String.class), bookQuery.getAuthor());
                query.where(criteriaBuilder.and(p1,p2,p3));
                return query.getRestriction();
            }
        },pageable);
        return bookPage;
    }

6、编写Controller

    针对有查询条件和无查询条件,我们分别编写一个Controller,默认每页显示5条,如下

@Controller
@RequestMapping(value = "/queryBook")
public class BookController {
    @Autowired
    BookQueryService bookQueryService;

    @RequestMapping("/findBookNoQuery")
    public String findBookNoQuery(ModelMap modelMap,@RequestParam(value = "page", defaultValue = "0") Integer page,
                        @RequestParam(value = "size", defaultValue = "5") Integer size){
        Page<Book> datas = bookQueryService.findBookNoCriteria(page, size);
        modelMap.addAttribute("datas", datas);
        return "index1";
    }

    @RequestMapping(value = "/findBookQuery",method = {RequestMethod.GET,RequestMethod.POST})
    public String findBookQuery(ModelMap modelMap, @RequestParam(value = "page", defaultValue = "0") Integer page,
                                @RequestParam(value = "size", defaultValue = "5") Integer size, BookQuery bookQuery){
        Page<Book> datas = bookQueryService.findBookCriteria(page, size,bookQuery);
        modelMap.addAttribute("datas", datas);
        return "index2";
    }
}

7、编写页面

    首先我们编写一个通用的分页页面,新建一个叫page.html的页面

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
      layout:decorator="page">
<body>
<div th:fragment="pager">
    <div class="text-right" th:with="baseUrl=${#httpServletRequest.getRequestURL().toString()},pars=${#httpServletRequest.getQueryString() eq null ? '' : new String(#httpServletRequest.getQueryString().getBytes('iso8859-1'), 'UTF-8')}">
        <ul style="margin:0px;" class="pagination" th:with="newPar=${new java.lang.String(pars eq null ? '' : pars).replace('page='+(datas.number), '')},
                                                curTmpUrl=${baseUrl+'?'+newPar},
                                                curUrl=${curTmpUrl.endsWith('&amp;') ? curTmpUrl.substring(0, curTmpUrl.length()-1):curTmpUrl}" >
            <!--<li th:text="${pars}"></li>-->

            <li><a href="#" th:href="@{${curUrl}(page=0)}">首页</a></li>
            <li th:if="${datas.hasPrevious()}"><a href="#" th:href="@{${curUrl}(page=${datas.number-1})}">上一页</a></li>

            <!--总页数小于等于10-->
            <div th:if="${(datas.totalPages le 10) and (datas.totalPages gt 0)}" th:remove="tag">
                <div th:each="pg : ${#numbers.sequence(0, datas.totalPages - 1)}" th:remove="tag">
                        <span th:if="${pg eq datas.getNumber()}" th:remove="tag">
                            <li class="active"><span class="current_page line_height" th:text="${pg+1}">${pageNumber}</span></li>
                        </span>
                    <span th:unless="${pg eq datas.getNumber()}" th:remove="tag">
                            <li><a href="#" th:href="@{${curUrl}(page=${pg})}" th:text="${pg+1}"></a></li>
                        </span>
                </div>
            </div>

            <!-- 总数数大于10时 -->
            <div th:if="${datas.totalPages gt 10}" th:remove="tag">
                <li th:if="${datas.number-2 ge 0}"><a href="#" th:href="@{${curUrl}(page=${datas.number}-2)}" th:text="${datas.number-1}"></a></li>
                <li th:if="${datas.number-1 ge 0}"><a href="#" th:href="@{${curUrl}(page=${datas.number}-1)}" th:text="${datas.number}"></a></li>
                <li class="active"><span class="current_page line_height" th:text="${datas.number+1}"></span></li>
                <li th:if="${datas.number+1 lt datas.totalPages}"><a href="#" th:href="@{${curUrl}(page=${datas.number}+1)}" th:text="${datas.number+2}"></a></li>
                <li th:if="${datas.number+2 lt datas.totalPages}"><a href="#" th:href="@{${curUrl}(page=${datas.number}+2)}" th:text="${datas.number+3}"></a></li>
            </div>


            <li th:if="${datas.hasNext()}"><a href="#" th:href="@{${curUrl}(page=${datas.number+1})}">下一页</a></li>
            <!--<li><a href="#" th:href="@{${curUrl}(page=${datas.totalPages-1})}">尾页</a></li>-->
            <li><a href="#" th:href="${datas.totalPages le 0 ? curUrl+'page=0':curUrl+'&amp;page='+(datas.totalPages-1)}">尾页</a></li>
            <li><span th:utext="'共'+${datas.totalPages}+'页 / '+${datas.totalElements}+' 条'"></span></li>
        </ul>
    </div>
</div>
</body>
</html>

    针对无查询条件的接口,创建一个名为index1.html的页面并引入之前写好的分页页面,如下

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="UTF-8"/>
    <title>Title</title>
    <script type="text/javascript" th:src="@{/jquery-1.12.3.min.js}"></script>
    <script type="text/javascript" th:src="@{/bootstrap/js/bootstrap.min.js}"></script>
    <link type="text/css" rel="stylesheet" th:href="@{/bootstrap/css/bootstrap-theme.min.css}"/>
    <link type="text/css" rel="stylesheet" th:href="@{/bootstrap/css/bootstrap.css}"/>
</head>
<body>
    <table class="table table-hover">
        <thead>
        <tr>
            <th>ID</th>
            <th>name</th>
            <th>isbn</th>
            <th>author</th>
        </tr>
        </thead>
        <tbody>
        <tr th:each="obj : ${datas}">
            <td th:text="${obj.id}">${obj.id}</td>
            <td th:text="${obj.name}">${obj.name}</td>
            <td th:text="${obj.isbn}">${obj.isbn}</td>
            <td th:text="${obj.name}">${obj.author}</td>
        </tr>
        </tbody>
    </table>
        <div th:include="page :: pager" th:remove="tag"></div>
</body>
</html>

     针对有查询条件的接口,创建一个名为index2.html的页面并引入之前写好的分页页面,如下

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="UTF-8"/>
    <title>Title</title>
    <script type="text/javascript" th:src="@{/jquery-1.12.3.min.js}"></script>
    <script type="text/javascript" th:src="@{/bootstrap/js/bootstrap.min.js}"></script>
    <link type="text/css" rel="stylesheet" th:href="@{/bootstrap/css/bootstrap-theme.min.css}"/>
    <link type="text/css" rel="stylesheet" th:href="@{/bootstrap/css/bootstrap.css}"/>
</head>
<body>
<form th:action="@{/queryBook/findBookQuery}" th:object="${bookQuery}" th:method="get">
    <div class="form-group">
        <label class="col-sm-2 control-label" >name</label>
        <div class="col-sm-4">
            <input type="text" class="form-control" id="name" placeholder="请输入名称" th:field="*{name}"/>
        </div>
        <label class="col-sm-2 control-label">isbn</label>
        <div class="col-sm-4">
            <input type="text" class="form-control" id="isbn" placeholder="请输ISBN" th:field="*{isbn}"/>
        </div>
    </div>
    <div class="form-group">
        <label class="col-sm-2 control-label" >author</label>
        <div class="col-sm-4">
            <input type="text" class="form-control" id="author" placeholder="请输author" th:field="*{author}"/>
        </div>
        <div class="col-sm-4">
            <button class="btn btn-default" type="submit" placeholder="查询">查询</button>
        </div>
    </div>
</form>
    <table class="table table-hover">
        <thead>
        <tr>
            <th>ID</th>
            <th>name</th>
            <th>isbn</th>
            <th>author</th>
        </tr>
        </thead>
        <tbody>
        <tr th:each="obj : ${datas}">
            <td th:text="${obj.id}">${obj.id}</td>
            <td th:text="${obj.name}">${obj.name}</td>
            <td th:text="${obj.isbn}">${obj.isbn}</td>
            <td th:text="${obj.name}">${obj.author}</td>
        </tr>
        </tbody>
    </table>
        <div th:include="page :: pager" th:remove="tag"></div>
</body>
</html>

    ok!代码都已经完成,我们将项目启动起来,看一下效果。大家可以往数据库中批量插入一些数据,访问http://localhost:8080/queryBook/findBookNoQuery,显示如下页面

访问http://localhost:8080/queryBook/findBookQuery,显示页面如下,可以输入查询条件进行带条件的分页查询:

    ok!以上便是一个简单的jap分页查询功能的实现。

© 著作权归作者所有

老虎是个蛋蛋
粉丝 173
博文 31
码字总数 40085
作品 0
朝阳
高级程序员
私信 提问
加载中

评论(13)

老虎是个蛋蛋
老虎是个蛋蛋 博主

引用来自“lovepli”的评论

请问有源码吗?
不好意思,木有源码,源码基本上都在文章里面了
l
lovepli
请问有源码吗?
yuxxb
yuxxb

引用来自“老虎是个蛋蛋”的评论

引用来自“yuxxb”的评论

引用来自“老虎是个蛋蛋”的评论

引用来自“yuxxb”的评论

th:object="${bookQuery}"请问下怎样填写它?

回复@yuxxb : 没太明白你问的意思

引用来自“yuxxb”的评论

就是里面的bookQuery是什么意思?

引用来自“老虎是个蛋蛋”的评论

public String findBookQuery(ModelMap modelMap, @RequestParam(value = "page", defaultValue = "0") Integer page,@RequestParam(value = "size", defaultValue = "5") Integer size, BookQuery bookQuery) 这个方法的查询条件入参名称
是不是BookQuery后面的bookQuery?

回复@yuxxb : 是
作者,请问下我加了过滤器就报错:Request method 'GET' not supported
yuxxb
yuxxb

引用来自“老虎是个蛋蛋”的评论

引用来自“yuxxb”的评论

引用来自“老虎是个蛋蛋”的评论

引用来自“yuxxb”的评论

th:object="${bookQuery}"请问下怎样填写它?

回复@yuxxb : 没太明白你问的意思

引用来自“yuxxb”的评论

就是里面的bookQuery是什么意思?

引用来自“老虎是个蛋蛋”的评论

public String findBookQuery(ModelMap modelMap, @RequestParam(value = "page", defaultValue = "0") Integer page,@RequestParam(value = "size", defaultValue = "5") Integer size, BookQuery bookQuery) 这个方法的查询条件入参名称
是不是BookQuery后面的bookQuery?

回复@yuxxb : 是
作者,还是报同样的错
yuxxb
yuxxb

引用来自“老虎是个蛋蛋”的评论

引用来自“yuxxb”的评论

引用来自“老虎是个蛋蛋”的评论

引用来自“yuxxb”的评论

th:object="${bookQuery}"请问下怎样填写它?

回复@yuxxb : 没太明白你问的意思

引用来自“yuxxb”的评论

就是里面的bookQuery是什么意思?

引用来自“老虎是个蛋蛋”的评论

public String findBookQuery(ModelMap modelMap, @RequestParam(value = "page", defaultValue = "0") Integer page,@RequestParam(value = "size", defaultValue = "5") Integer size, BookQuery bookQuery) 这个方法的查询条件入参名称
是不是BookQuery后面的bookQuery?

回复@yuxxb : 是
我再试一试,看行不行。谢谢您
老虎是个蛋蛋
老虎是个蛋蛋 博主

引用来自“yuxxb”的评论

引用来自“老虎是个蛋蛋”的评论

引用来自“yuxxb”的评论

th:object="${bookQuery}"请问下怎样填写它?

回复@yuxxb : 没太明白你问的意思

引用来自“yuxxb”的评论

就是里面的bookQuery是什么意思?

引用来自“老虎是个蛋蛋”的评论

public String findBookQuery(ModelMap modelMap, @RequestParam(value = "page", defaultValue = "0") Integer page,@RequestParam(value = "size", defaultValue = "5") Integer size, BookQuery bookQuery) 这个方法的查询条件入参名称
是不是BookQuery后面的bookQuery?

回复@yuxxb : 是
yuxxb
yuxxb

引用来自“老虎是个蛋蛋”的评论

引用来自“yuxxb”的评论

th:object="${bookQuery}"请问下怎样填写它?

回复@yuxxb : 没太明白你问的意思

引用来自“yuxxb”的评论

就是里面的bookQuery是什么意思?

引用来自“老虎是个蛋蛋”的评论

public String findBookQuery(ModelMap modelMap, @RequestParam(value = "page", defaultValue = "0") Integer page,@RequestParam(value = "size", defaultValue = "5") Integer size, BookQuery bookQuery) 这个方法的查询条件入参名称
是不是BookQuery后面的bookQuery?
老虎是个蛋蛋
老虎是个蛋蛋 博主

引用来自“老虎是个蛋蛋”的评论

引用来自“yuxxb”的评论

th:object="${bookQuery}"请问下怎样填写它?

回复@yuxxb : 没太明白你问的意思

引用来自“yuxxb”的评论

就是里面的bookQuery是什么意思?
public String findBookQuery(ModelMap modelMap, @RequestParam(value = "page", defaultValue = "0") Integer page,@RequestParam(value = "size", defaultValue = "5") Integer size, BookQuery bookQuery) 这个方法的查询条件入参名称
yuxxb
yuxxb

引用来自“老虎是个蛋蛋”的评论

引用来自“yuxxb”的评论

th:object="${bookQuery}"请问下怎样填写它?

回复@yuxxb : 没太明白你问的意思
就是里面的bookQuery是什么意思?
yuxxb
yuxxb

引用来自“老虎是个蛋蛋”的评论

引用来自“yuxxb”的评论

th:object="${bookQuery}"请问下怎样填写它?

回复@yuxxb : 没太明白你问的意思
我启动项目,报错的语句是:Neither BindingResult nor plain target object for bean name 'sGMultiConditionalQuery' available as request attribute,不知道是什么意思。
spring-boot整合spring-data-jpa

一、创建spring-boot工程 新建spring-boot项目,选择项目类型为gradle project,勾选初始化依赖如下: 删除resources下application.properties,创建application.yml文件(.yml与.properties...

louieSun
2018/06/04
0
0
Spring Boot [组件学习-Spring Data JPA]

导读: 在上篇文章中对Spring MVC常用的一些注解做了简要的说明,在这篇文章中主要对Spring Data JPA 做一个简要的说明,并附有一个简单的例子,可以体会到Spring Data JPA 的强大之处。 Sp...

yangrd
2018/08/27
0
0
SpringBoot | 第三十章:Spring-data-jpa的集成和使用

前言 在前面的第九章:Mybatis-plus的集成和使用章节中,介绍了使用框架进行数据库的访问。今天,我们来简单学习下如何使用进行数据库的访问。由于本人未使用过,也是趁着写博文的机会查阅了...

oKong
2018/10/31
278
0
(入门帖)使用 Spring Data JPA 简化 JPA 开发

本文主要讲述 Spring Data JPA,但是为了不至于给 JPA 和 Spring 的初学者造成较大的学习曲线,我们首先从 JPA 开始,简单介绍一个 JPA 示例;接着重构该示例,并引入 Spring 框架,这两部分...

阿莱倪士
2014/01/09
668
0
Spring Data JPA实战视频教程

Spring Data JPA实战视频教程 视频大纲 JPA入门 Spring Data JPA入门 Repository的定义 查询方法的命名策略 JPQL查询,结果映射 Named Query,Named Native Query 排序,分页 JPA Criteria查...

刘宗泽
2018/06/26
0
0

没有更多内容

加载失败,请刷新页面

加载更多

【2019年8月版本】OCP 071认证考试最新版本的考试原题-第5题

choose the best answer The CUSTOMERS table has a CUST_LAST_NAME column of data type VARCHAR2. The table has two rows whose COST_LAST_MANE values are Anderson and Ausson. Which q......

oschina_5359
34分钟前
3
0
电脑怎样制作流程图?分享绘制流程图方法

流程图的绘制可以用很多方法来实现,小编经常使用电脑对流程图进行绘制,即简单又便利,相信很多朋友都因为不知道怎样绘制流程图而选择了放弃,今天这篇文章希望可以让大家重拾绘制流程图的信...

干货趣分享
36分钟前
3
0
Elasticsearch 7.x 之文档、索引和 REST API 【基础入门篇】

前几天写过一篇《Elasticsearch 7.x 最详细安装及配置》,今天继续最新版基础入门内容。这一篇简单总结了 Elasticsearch 7.x 之文档、索引和 REST API。 什么是文档 文档Unique ID 文档元数据...

泥瓦匠BYSocket
40分钟前
4
0
TL665x-EasyEVM开发板处理器、flash、RAM

TL665x-EasyEVM是广州创龙基于SOM-TL665x核心板研发的一款TI C66x多核定点/浮点高性能DSP开发板,采用核心板+底板方式,底板尺寸为200mm*106.65mm,采用4*50pin和1*80pin B2B工业级连接器,稳...

Tronlong创龙
44分钟前
3
0
DevExpress Report-XRTable绑定数据

将从跳转前的页面(A)中获取传入的数据(dtOrd、BatchID、ModelID),绑定到Report报表对应的控件 ,代码如下: this.xrtBatchID.Text = sBatchID; this.xrtModel.Text ...

_Somuns
45分钟前
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部