图书管理系统(2)图书列表接口

图书管理系统(2)图书列表接口

文章目录

观前提醒:

这个图书管理系统,非常的简陋,仅作为练习使用。不建议大家使用我介绍的 图书管理系统 ,去作为 课程设计。

如果你是第一次点击这篇博客的,需要你将我 图书管理系统 的博客列表中,看完这篇:
图书管理系统(1)项目准备,用户登录接口,添加图书接口

再过来看本篇博客

无Mybatis版本获取:

这个 图书管理系统 的实现,需要你从我的 gitee上,将 无Mybatis版本 的图书管理系统源代码下载下来。

gitee链接:https://gitee.com/mrbgvhbhjv/java-ee-course/tree/master/后端代码/springboot_bookManage_System

基于 Mybatis版本 的获取:

如果你想直接获取 基于 Mybatis 完全实现了增删查改功能的图书管理系统,就从我的 gitee 上面获取,下载源代码。

gitee链接:https://gitee.com/mrbgvhbhjv/java-ee-course/tree/master/后端代码/Book_System_20251107

目录结构:

以防大家会乱,或者我博客写的不详细,各种类的存放位置,我放个目录结构在这里:

个人建议:

这篇博客的代码,建议自己敲一下。

跟着我的步骤,使用 无Mybatis版本 的图书管理系统源代码 ,一步一步的将代码实现出来。

这里使用的数据库是 MySQL。

图形化工具:Navicat

虽然,截止到 2026年,cursor,TRAE等 AI工具,能够一键编写代码,甚至一个系统。

但是,如果你不会基础知识,只会让 ai 生成代码,不会看代码,不会修改代码,代码运行报错,你不会解决问题等等。其实,你都不算是一个 Java开发者。

如果我们将 AI工具,对我们的工作效率提升,有巨大的帮助,它一定是一个乘法结算的结果。

你的基础开发能力 × AI工具效率 == 工作效率。

如果你的基础开发能力不行,约等于 0,那么,无论 AI工具效率多高,99倍也好,0 * 99 == 0

所以,只有将我们自己的基础开发能力提升了,使用 AI工具提升效率,才是事半功倍的效果。

1. 图书列表接口后端代码

当前的图书列表,是模拟的数据,不是数据库的真实数据:

我们需要编写代码,使前端页面,显示的图书列表,是数据库中的数据。

1.1 翻页(分页)(原始操作,不是使用 Mybatis 插件的分页功能)

数据太多,我们在前端进行展示的时候,应该要翻页,一页 10 条数据。

为了有更多数据,我直接使用 sql语句,往图书表中,添加多条数据:

sql 复制代码
INSERT INTO `book_info` ( book_name, author, count, price, publish )
VALUES
( '图书2', '作者2', 29, 22.00, '出版社2' ),( '图书3', '作者2', 29, 22.00, '出版社
3' ),
( '图书4', '作者2', 29, 22.00, '出版社1' ),( '图书5', '作者2', 29, 22.00, '出版社
1' ),
( '图书6', '作者2', 29, 22.00, '出版社1' ),( '图书7', '作者2', 29, 22.00, '出版社
1' ),
( '图书8', '作者2', 29, 22.00, '出版社1' ),( '图书9', '作者2', 29, 22.00, '出版社
1' ),
( '图书10', '作者2', 29, 22.00, '出版社1'),( '图书11', '作者2', 29, 22.00, '出版
社1'),
( '图书12', '作者2', 29, 22.00, '出版社1'),( '图书13', '作者2', 29, 22.00, '出版
社1'),
( '图书14', '作者2', 29, 22.00, '出版社1'),( '图书15', '作者2', 29, 22.00, '出版
社1'),
( '图书16', '作者2', 29, 22.00, '出版社1'),( '图书17', '作者2', 29, 22.00, '出版
社1'),
( '图书18', '作者2', 29, 22.00, '出版社1'),( '图书19', '作者2', 29, 22.00, '出版
社1'),
( '图书20', '作者2', 29, 22.00, '出版社1'),( '图书21', '作者2', 29, 22.00, '出版
社1');

sql分页:limit

sql语句中,分页操作,使用的是 limit语句:

offset:表示分页的起始记录

从上面图片,我们可以分析,offset 和 limit 的取值规律:

offset:(page-1)* pageSize

limit:pageSize

所以,当记录是第 N 页的时候:

offset:(N-1)*pageSize

limit:pageSize

后端开发:

接口描述:返回第 N 页的图书列表信息

参数:页码(pageNum),每页的个数(pageSize)

返回:

  1. 第 pageNum 页的图书列表
  2. 总图书数(用于分页)

1.2 接口文档描述:

currentPage:表示当前页码

pageSize:表示每页的记录个数

1.3 Controller层:

根据接口文档,进行开发,但是,接口文档中,响应返回的内容,我们当前的任意类,是没有符合的属性,能够接收到的,所以,我们需要新创建一个类:ResponseResult:

ResponseResult类代码:

在 modul包下,创建 ResponseResult:

java 复制代码
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;

@AllArgsConstructor
@NoArgsConstructor
@Data
public class ResponseResult<T> {
    private Integer total;
    private List<T> records;
    private PageRequest pageRequest;
}

PageRequest类,如果报红,不用管,后面会创建这个类的,再导入这个类,就可以了。

泛型的使用:

解释:

<T>:表示的是泛型。

ResponseResult<T>:表示ResponseResult类中,List,不限定接收数据的类型,可以是 BookInfo,也可以是 UserInfo。

定义 Controller层 接口的时候:

多个参数,封装成 PageRequest:

方法参数,有多个,建议封装成对象:

当然,如果前端没有指定分页的起始页数,和每一页的个数,我们可以自己在后端定义:

在 modul包下,创建 PageRequest:

java 复制代码
import lombok.Data;

@Data
public class PageRequest {
    private Integer currentPage = 1;
    private Integer pageSize = 10;

    private Integer offset;

    public Integer getOffset() {
        return (currentPage - 1) * pageSize;
    }
}

方法处,就传递对象即可。

业务逻辑的实现:

  1. 参数校验
  2. 返回数据(执行 sql语句)

service 返回的数据,包括两部分:

  1. 第 pageNum 页的图书列表(selectBooksByPage)
  2. 总图书数(用于分页)( count( ) )

我们这里,直接使用返回的数据就行,返回给前端。

完整代码:

BookController类:

java 复制代码
    @RequestMapping(value = "/getListByPage")
    public ResponseResult<BookInfo> getListByPage(PageRequest pageRequest){
//        1.参数校验
//        2.返回结果
        
        return bookService.getListByPage(pageRequest);

    }

1.4 mapper层(数据持久层):

以下代码,均在 BookInfoMapper 中,进行编写。

返回第 pageNum 页的图书列表:

java 复制代码
    @Select("select * from book_info where `status` != 0 limit #{offset},#{pageSize}")
    List<BookInfo> selectBookByPage(PageRequest pageRequest);
    

offset的计算,可以放在 service,也可以放在 PageRequest 里面。

此处,offset的计算,放在 PageRequest 里面:

PageRequest代码:

java 复制代码
import lombok.Data;

@Data
public class PageRequest {
    private Integer currentPage = 1;
    private Integer pageSize = 10;

    private Integer offset;

    public Integer getOffset() {
        return (currentPage - 1) * pageSize;
    }
}

总图书数:

java 复制代码
    @Select("select count(1) from book_info where `status` != 0")
    Integer countBook();

完整代码:

java 复制代码
    @Select("select * from book_info where `status` != 0 limit #{offset},#{pageSize}")
    List<BookInfo> selectBookByPage(PageRequest pageRequest);
    
    @Select("select count(1) from book_info where `status` != 0")
    Integer countBook();

1.5 Service层(业务层):

service层代码,做的工作有两个:

  1. 获得总的图书数
  2. 获取当前页的数据

service层初始代码:

简化代码:

简化创建,返回步骤:

创建 ResponseResult对象,还要set,最后返回,代码有点冗余,我们可以借助全参的构造方法,一键返回:

ResponseResult类:

简化之后的代码:

简化状态处理代码(枚举类型):

对图书的状态,进行处理,这里的代码比较冗余,而且判断条件是写死的,不太方便,我们可以借助 枚举类型。

将判断逻辑,进行封装,封装成方法,在枚举代码中编写:

枚举类(BookStatusEnum)的代码:

java 复制代码
package org.example.book_system_20251107.book.enums;


public enum BookStatusEnum {
    DELETED(0,"删除"),
    NORMAL(1,"正常"),
    FORBIDDEN(2,"不可借阅");

    private Integer code; // 错误码
    private String desc; // 错误信息

    BookStatusEnum(Integer code, String desc) {
        this.code = code;
        this.desc = desc;
    }
    
    public static BookStatusEnum getStatusByCode(Integer code) {
        switch (code) {
            case 0:return BookStatusEnum.DELETED;
            case 1:return BookStatusEnum.NORMAL;
            case 2:return BookStatusEnum.FORBIDDEN;
            default:return null;
        }
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }
}

使用枚举,简化状态处理代码:

service层最终代码:

java 复制代码
    public ResponseResult<BookInfo> getListByPage(PageRequest pageRequest){
//        1.获取图书总数
//        2.获取当前页的数据

        Integer count = bookInfoMapper.countBook();

        List<BookInfo> bookInfoList = bookInfoMapper.selectBookByPage(pageRequest);
//        对结果进行处理,设置图书的借阅状态:
        for(BookInfo bookInfo:bookInfoList){
//            使用 try语句,处理空指针异常:
            try {
                bookInfo.setStatusCN(BookStatusEnum.getStatusByCode(bookInfo.getStatus()).getDesc());
            }catch (Exception e){
                log.error("e:{}",e.getMessage());
            }

//            if (bookInfo.getStatus() == 1){
//                bookInfo.setStatusCN("可借阅");
//            }else {
//                bookInfo.setStatusCN("不可借阅");
//            }
        }

//        返回结果给 Controller
        return new ResponseResult<BookInfo>(count, bookInfoList,pageRequest);

//        set方法,在返回
//        ResponseResult<BookInfo> bookInfoResponseResult = new ResponseResult<>();
//        bookInfoResponseResult.setTotal(count);
//        bookInfoResponseResult.setRecords(bookInfoList);
//        return bookInfoResponseResult;
    }

1.6 接口测试:

使用 Postman:

1.7 注意事项:

枚举类型,无法使用 @Data

枚举类型是无法使用 @Data 的:

只能手写(快速生成代码):

Alt + Insert,选择需要创建的方法即可。

分页的参数

PageRequest代码:

java 复制代码
import lombok.Data;

@Data
public class PageRequest {
    private Integer currentPage = 1;
    private Integer pageSize = 10;

    private Integer offset;

    public Integer getOffset() {
        return (currentPage - 1) * pageSize;
    }
}

这是后端定义的 currentPage 和 pageSize。

但是,前端也会传递这两个参数。

实际工作中,使用哪个,需要前后端的开发人员商量。

在这里,我们自己实现,就自己定义就行。

2. 图书列表前端代码

后端人员,对前端代码,能够掌握其思路,逻辑即可,对代码完全手写,没有强制性要求。

虽然前端能够直接获取后端的 currentPage 和 pageSize,但是,为了避免前端代码,太多插件的使用,我们直接在后端的

ResponseResult返回 PageRequest对象,让前端自己取他们需要的参数(currentPage 和 pageSize等等)。

前端代码:

book_add.html:

html 复制代码
  <script>
    function add ()
    {
      //前端应该进行参数校验,此处省略
      //提交请求到后端
      $.ajax({
        type: "post",
        url: "/book/addBook",
        data: $("#addBook").serialize(),
        success: function (result)
        {
          if (result == "") {
            //添加图书成功
            alert("添加图书成功");
            location.href = "book_list.html?currentPage=1";
          } else {
            //失败
            alert(result);
          }
        }
      });

      // alert("添加成功");
      // location.href = "book_list.html";
    }
  </script>

book_list.html:

html 复制代码
function getBookList ()
      {
        $.ajax({
          type: "get",
          url: "/book/getListByPage" + location.search,
          success: function (result)
          {
            if (result == null || result.records == null) {
              return null;
            }

            var books = result.records;
            var wholeHtml = "";
            for (var book of books) {
              // 拼接列表
              wholeHtml += '<tr>';
              wholeHtml += '<td><input type="checkbox" name="selectBook" value="' + book.bookID + '" id="selectBook" class="book-select"></td>';
              wholeHtml += '<td>' + book.id + '</td>';
              wholeHtml += '<td>' + book.bookName + '</td>';
              wholeHtml += '<td>' + book.author + '</td>';
              wholeHtml += '<td>' + book.count + '</td>';
              wholeHtml += '<td>' + book.price + '</td>';
              wholeHtml += '<td>' + book.publish + '</td>';
              wholeHtml += '<td>' + book.statusCN + '</td>';
              wholeHtml += '<td><div class="op">';
              wholeHtml += '<a href="book_update.html?bookId=' + book.bookID + '">修改</a>';
              wholeHtml += '<a href="javascript:void(0)" onclick="deleteBook(' + book.bookID + ')">删除</a>';
              wholeHtml += '</div></td></tr>';
            }
            $("tbody").html(wholeHtml);

            //翻页信息
            $("#pageContainer").jqPaginator({
              totalCounts: result.total, //总记录数
              pageSize: 10, //每页的个数
              visiblePages: 5, //可视页数
              currentPage: result.pageRequest.currentPage, //当前页码
              first: '<li class="page-item"><a class="page-link">首页</a></li>',
              prev: '<li class="page-item"><a class="page-link" href="javascript:void(0);">上一页<\/a><\/li>',
              next: '<li class="page-item"><a class="page-link" href="javascript:void(0);">下一页<\/a><\/li>',
              last: '<li class="page-item"><a class="page-link" href="javascript:void(0);">最后一页<\/a><\/li>',
              page: '<li class="page-item"><a class="page-link" href="javascript:void(0);">{{page}}<\/a><\/li>',
              //页面初始化和页码点击时都会执行
              onPageChange: function (page, type)
              {
                if (type == "change") {
                  location.href = "book_list.html?currentPage=" + page;
                }
              }
            });

          }
        });
      }

网页效果:

可能遇到的问题:

这个问题,可以不解决。

解决方案:

在线生成ICO图标 - 免费ICO转换工具 (xbgjw.com)

进入这个网站,生成一个 ico图标,放到项目文件的 static文件夹 即可

注意图片名字必须是:favicon.ico

前端代码执行流程梳理:

3. 总结:

列表接口,是最难理解的,在这个图书管理系统中。

其中,涉及到分页,枚举,代码封装等方面的知识。

建议多写几遍。

我们这个代码中,使用的分页,是最原始的分页操作。

Mybatis本身就提供了分页的插件,给我们使用。

分页功能,一般是由后端实现的,前端传递参数(起始页面,每一页的个数)过来后端。

最后,如果这篇博客能帮到你的,请你点点赞,有写错了,写的不好的,欢迎评论指出,谢谢!

下一篇博客:图书管理系统(3)修改图书接口

相关推荐
醇氧6 分钟前
PowerPoint 批量转换为 PDF
java·spring boot·spring·pdf·powerpoint
小箌17 分钟前
JavaWeb_02
java·数据库·maven·mybatis
gxy19902618 分钟前
【springboot】Spring 官方抛弃了 Java 8!新idea如何创建java8项目
java·spring boot·spring
Chan1627 分钟前
双非 Java 后端首次实习 | 个人经验分享总结
java·开发语言·spring boot·spring·java-ee·intellij-idea
HalvmånEver9 小时前
7.高并发内存池大页内存申请释放以及使用定长内存池脱离new
java·spring boot·spring
凤山老林9 小时前
SpringBoot 使用 H2 文本数据库构建轻量级应用
java·数据库·spring boot·后端
dreamread10 小时前
【SpringBoot整合系列】SpringBoot3.x整合Swagger
java·spring boot·后端
把你毕设抢过来10 小时前
基于Spring Boot的社区智慧养老监护管理平台(源码+文档)
数据库·spring boot·后端
闻哥11 小时前
深入Redis的RDB和AOF两种持久化方式以及AOF重写机制的分析
java·数据库·spring boot·redis·spring·缓存·面试
wsxlgg13 小时前
mybatis自动生成mapper和xml文件
mybatis