图书列表
实现服务器代码(计算图书总数量+查询当前页需要展示的书籍)
后端响应时,需要响应给前端的数据
records
:第 pageNum 页要展示的图书有哪些(存储到List集合中)total
:计算一共有多少本书(用于告诉前端显示多少页)
我们创建一个类 ResponseResult ,用于存放后端计算的 records,total,并且返回前端;
围绕着这两个功能,来完善我们的服务端代码;
控制层 BookController
为了可复用性,我们引入泛型(修改一个类名):
写好框架之后,我们在 Controller 层先调用这个接口:
对于接口参数,如果参数个数比较多,还是建议直接以对象作为参数:
我们给这两个属性先加上一个默认值:
数据层:BookInfoMapper
查询第1页的SQL语句
select * from book_info where `status` != 0 limit 0,10;
查询第2页的SQL语句
select * from book_info where `status` != 0 limit 10,10;
查询第3页的SQL语句
select * from book_info where `status` != 0 limit 20,10;
观察以上SQL语句,发现:开始索引(offset)一直在改变,每页显示条数(limit)是固定的
。
select * from book_info where status != 0 limit `offset`, `limit`;
开始索引的计算公式:开始索引 = (当前页码 - 1) * 每页显示条数
接下来,我们实现**对列表翻页时,查询第 PageNum 页的图书列表
** 的功能:
我们还需要计算出 offset 的值,不妨在 PageRequest 类中再增加一个属性:
并且,我们可以直接在这个类中重写 offset 的 getter() 方法,也就是在 PageRequest 类中,直接计算出 offset 的值,并且可以在其他类获取到这个值:
在 PageRequest 中并没有 limt 属性,所以我们修改一下 SQL 参数,避免出现 MyBatis 反射异常
通过以上操作,我们就实现了第一个功能:第 pageNum 页要展示的图书有哪些
接下来,我们实现第二个功能:计算一共有多少本书
插入数据后,我们使用聚合函数 count 计算一下数据库中有多少条记录:
接下来,我们回忆 BookInfo 中 status 属性的定义(0 - 无效,1 - 可借阅,2 - 不可借阅),因此,我们要先查询有效的书籍(status != 0):
我们这里有26条数据,如果一页展示十条记录,那么26条记录,就需要三页;
业务层 BookService
计算出书的数量和当前页要展示的书后,接下来,识别展示图书的 status,转换为中文说明 statusCN:
处理好 statusCN 后,我们根据方法返回类型 ResponseResult<BookInfo>
,设置好返回的书籍数量和当前页展示的书籍列表:
引入@AllArgsConstructor、 @NoArgsConstructor简化代码
这篇博客的5(2)有详细的 lombok 及相关注解的使用说明,包含 @AllArgsConstructor、 @NoArgsConstructor 的使用说明
引入枚举类 Enums 简化代码
这样的写法其实是有点啰嗦的,status 的状态只有(0,1,2)三种,因此,我们可以使用枚举类简化一下上面的代码:
通过上面的封装和注解的巧用,我们的 BookService 代码变得更加精简了:
总结:
需求/问题描述 | 解决方案 | 技术实现说明 |
---|---|---|
翻页信息 需要返回数据总数 和列表 | 需要执行两次SQL 查询 | 1. 第一次查询总数 (SELECT COUNT(*) )2. 第二次 查询分页数据 (LIMIT 语句) |
图书状态 与数据库status 字段 映射 | 使用枚举类 处理状态码映射 关系 | 定义枚举类 将状态码(如0,1,2) 与文字描述 (如"可借阅",“已借出”)绑定 |
状态码变动 导致多处代码修改 | 通过枚举类 集中管理状态码 | 修改时只需调整枚举类 ,无需全局搜索替换代码 |
测试接口
启动服务,访问后端程序:
http://127.0.0.1:9090/book/getListByPage 返回1-10条记录(按id降序)
http://127.0.0.1:9090/book/getListByPage?currentPage=2 返回11-20条记录
后端思维导图梳理
实现客户端代码
在 ResponseResult
类中添加 private PageRequest pageRequest
属性的主要目的是实现分页信息的完整闭环传递,具体作用包括:
- 前端状态保持:
- 让前端知道当前返回的数据是依据哪些分页参数查询的
- 避免前端在接收数据后丢失原始的分页请求参数
- 分页上下文传递:
- 当前页码(currentPage)
- 每页条数(pageSize)
- 从第几本书开始展示(offset)
- 扩展性考虑:
- 方便后续添加排序字段等扩展参数
为前端提供生成分页控件所需的完整信息
onPageChange:回调函数,当换页时触发(包括初始化第一页的时候),会传入两个参数:
- 1、"目标页"的页码,Number类型
- 2、触发类型,可能的值:“init”(初始化),“change”(点击分页)
测试接口
ctrl+s 保存前端代码,重新允许程序,访问登录页面 http://127.0.0.1:9090/login.html
登录后,跳转到图书列表第一页:
点击页码,页面信息得到正确的处理
按 F12 打开控制台,查看报错信息:
保存代码,重新运行程序,打开页面:
翻页试试效果:
完整服务器代码
PageRequest
package com.bit.book.model;
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;
}
}
ResponseResult
package com.bit.book.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@AllArgsConstructor
@NoArgsConstructor
@Data
public class ResponseResult<T> {
private Integer total; // 所查询到的数据列表(存储到List集合中)
private List<T> records; // 总记录数(用于告诉前端显示多少页)
private PageRequest pageRequest;
}
BookController
package com.bit.book.Controller;
import com.bit.book.model.BookInfo;
import com.bit.book.model.PageRequest;
import com.bit.book.model.ResponseResult;
import com.bit.book.service.BookService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/book")
@Slf4j
public class BookController {
@Autowired
private BookService bookService;
// 返回页数接口
@RequestMapping("/getListByPage")
public ResponseResult<BookInfo> getListByPage(PageRequest pageRequest){
// 参数校验, 此处涉及复杂的校验逻辑,由公司负责安全的团队负责,这里就省略了
// 接收前端发送的请求,调用底层处理请求
ResponseResult<BookInfo> listByPage = bookService.getListByPage(pageRequest);
// 接收 service 处理好的响应,并返回给前端
return listByPage;
}
}
BookService
package com.bit.book.service;
import com.bit.book.enums.BookStatusEnum;
import com.bit.book.mapper.BookMapper;
import com.bit.book.model.BookInfo;
import com.bit.book.model.PageRequest;
import com.bit.book.model.ResponseResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class BookService {
@Autowired
private BookMapper bookMapper;
public ResponseResult<BookInfo> getListByPage(PageRequest pageRequest){
// 1. 获取总的图书数
Integer count = bookMapper.count();
// 2. 获取当前页需要展示的图书有哪些
List<BookInfo> bookInfos = bookMapper.selectBooksByPage(pageRequest);
// 3. 识别展示图书的 status, 转换为中文说明 statusCN
for (BookInfo bookInfo : bookInfos) {
bookInfo.setStatusCN(BookStatusEnum.getStatusByCode(bookInfo.getStatus()).getDesc());
}
return new ResponseResult<>(count,bookInfos,pageRequest);
}
}
BookStatusEnum
package com.bit.book.enums;
public enum BookStatusEnum {
DELETED(0, "该图书不存在"),
NORMAL(1,"允许借阅"),
FORBIDDEN(2,"不允许借阅");
private Integer code;
private String 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;
}
}
BookStatusEnum(Integer code, String desc) {
this.code = code;
this.desc = desc;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
}
BookMapper
package com.bit.book.mapper;
import com.bit.book.model.BookInfo;
import com.bit.book.model.PageRequest;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@Mapper
public interface BookMapper {
@Insert("insert into book_info " +
"(book_name, author, count, price, publish, `status`) " +
"values (#{bookName}, #{author}, #{count}, #{price}, #{publish}, #{status})")
Integer addBook(BookInfo bookInfo);
@Select("select * from book_info " +
"where `status` !=0 " +
"limit #{offset}, #{pageSize}")
List<BookInfo> selectBooksByPage(PageRequest pageRequest);
@Select("select count(1) " +
"from book_info " +
"where `status` != 0")
Integer count();
}