图书管理系统(2)图书列表接口
文章目录
- 图书管理系统(2)图书列表接口
-
- 观前提醒:
-
- 无Mybatis版本获取:
- [基于 Mybatis版本 的获取:](#基于 Mybatis版本 的获取:)
- 目录结构:
- 个人建议:
- [1. 图书列表接口后端代码](#1. 图书列表接口后端代码)
-
- [1.1 翻页(分页)(原始操作,不是使用 Mybatis 插件的分页功能)](#1.1 翻页(分页)(原始操作,不是使用 Mybatis 插件的分页功能))
- [1.2 接口文档描述:](#1.2 接口文档描述:)
- [1.3 Controller层:](#1.3 Controller层:)
-
- ResponseResult类代码:
- 泛型的使用:
- [多个参数,封装成 PageRequest:](#多个参数,封装成 PageRequest:)
- 业务逻辑的实现:
- 完整代码:
- [1.4 mapper层(数据持久层):](#1.4 mapper层(数据持久层):)
- [1.5 Service层(业务层):](#1.5 Service层(业务层):)
- [1.6 接口测试:](#1.6 接口测试:)
- [1.7 注意事项:](#1.7 注意事项:)
-
- [枚举类型,无法使用 @Data](#枚举类型,无法使用 @Data)
- 分页的参数
- [2. 图书列表前端代码](#2. 图书列表前端代码)
- [3. 总结:](#3. 总结:)
观前提醒:
这个图书管理系统,非常的简陋,仅作为练习使用。不建议大家使用我介绍的 图书管理系统 ,去作为 课程设计。
如果你是第一次点击这篇博客的,需要你将我 图书管理系统 的博客列表中,看完这篇:
图书管理系统(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)
返回:
- 第 pageNum 页的图书列表
- 总图书数(用于分页)
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;
}
}
方法处,就传递对象即可。

业务逻辑的实现:
- 参数校验
- 返回数据(执行 sql语句)
service 返回的数据,包括两部分:
- 第 pageNum 页的图书列表(selectBooksByPage)
- 总图书数(用于分页)( 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层代码,做的工作有两个:
- 获得总的图书数
- 获取当前页的数据
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)修改图书接口