图书管理系统(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)修改图书接口

相关推荐
tuokuac1 小时前
MyBatis-Plus调用getEntity()触发异常
java·mybatis
Coder_Boy_2 小时前
Java高级_资深_架构岗 核心知识点——高并发模块(底层+实践+最佳实践)
java·开发语言·人工智能·spring boot·分布式·微服务·架构
健康平安的活着3 小时前
AI之Toolcalling的使用案例(langchain4j+springboot)
人工智能·spring boot·后端
百锦再4 小时前
Java IO详解:File、FileInputStream与FileOutputStream
java·开发语言·jvm·spring boot·spring cloud·kafka·maven
百锦再4 小时前
Java InputStream和OutputStream实现类完全指南
java·开发语言·spring boot·python·struts·spring cloud·kafka
亓才孓4 小时前
【SQLSyntaxErrorException】SQL语法错误
数据库·sql·mybatis
亓才孓4 小时前
【MyBatis Exception】SQLSyntaxErrorException(按批修改不加配置会报错)
java·开发语言·mybatis
亓才孓4 小时前
【MyBatis Runtime Exception】自动驼峰映射对Map不生效,应该在查询中起别名
java·windows·mybatis
亓才孓4 小时前
【MyBatis Plus】MyBatis Plus框架
mybatis