基于my Batis优化图书管理系统(二)

4. 图书列表

添加图书之后, 跳转到图书列表⻚⾯, 并没有显⽰刚才添加的图书信息, 接下来我们来实现图 书列表

4.1 需求分析

当查询到我们的图书数据很多的时候,一个页可能存放不了,所以我们进行分页处理数据,并且分页进行查询;如果想看非当前页的数据,我们可以进行点击其他页码进行查询;

分⻚时, 数据是如何展⽰的呢:

第1⻚: 显⽰1-10 条的数据

第2⻚: 显⽰11-20 条的数据

以此类推... 要想实现这个功能, 从数据库中进⾏分⻚查询,我们要使⽤ LIMIT 关键字,

格式为:limit 开始索引 每⻚显⽰的条数(开始索引从0开始

查询第1⻚的SQL语句

SELECT * FROM book_info LIMIT 0,10;

查询第2⻚的SQL语句

SELECT * FROM book_info LIMIT 10,10;

观察以上SQL语句,发现: 开始索引⼀直在改变, 每⻚显⽰条数是固定的 开始索引的计算公式: 开始索引 = (当前⻚码 - 1) * 每⻚显⽰条数
我们继续基于前端⻚⾯, 继续分析, 得出以下结论:

  1. 前端在发起查询请求时,需要向服务端传递的参数 :

currentPage 当前⻚码 //默认值为1 ,

pageSize 每⻚显⽰条数 //默认值为10

2、后端响应时, 需要响应给前端的数据 ◦:

records 所查询到的数据列表(存储到List 集合中)

total 总记录数 (⽤于告诉前端显⽰多少⻚, 显⽰⻚数为: (total + pageSize -1)/pageSize 显⽰⻚数;totalPage 计算公式为 : total % pagesize == 0 ? total / pagesize : (total / pagesize)+1 ;

翻⻚请求和响应部分, 我们通常封装在两个对象中 ;

1、翻⻚请求对象

java 复制代码
@Data
public class PageRequest {
    //当前页
    private Integer currentPage =1;
    //每页显示个数
    private Integer pageSize =10;
    /**
     * 从多少条记录开始查询
     */
    private Integer offset;

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

2、翻⻚列表结果类:

java 复制代码
@AllArgsConstructor//全参构造
@NoArgsConstructor//无参构造
@Data
public class PageResult<T> {
    private List<T> records;
    //当前的t是bookinfo类型
    private Integer count;//所有的记录数
    private PageRequest pageRequest;//当前页的记录数据
}

4.2 约定前后端交互接⼝

java 复制代码
[请求]
/book/getListByPage?currentPage=1&pageSize=10
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
[参数]
[响应]
Content-Type: application/json
{
 "total": 25,
 "records": [{
 "id": 25,
 "bookName": "图书21",
 "author": "作者2",
 "count": 29,
 "price": 22.00,
 "publish": "出版社1",
 "status": 1,
 "statusCN": "可借阅"
 }, {
 ......
 } ]
}

4.3 实现服务器代码

控制层: 完善 BookController

java 复制代码
 @RequestMapping("/getListByPage")
    public PageResult<BookInfo> getListByPage(PageRequest pageRequest) {
        log.info("获取图书列表, pageRequest:{}", pageRequest);
        PageResult<BookInfo> pageResult =
                bookService.getListByPage(pageRequest);
        return pageResult;
    }

业务层: BookService

java 复制代码
public PageResult<BookInfo> getBookListByPage(PageRequest pageRequest) {
 Integer count = bookInfoMapper.count();
 List<BookInfo> books = bookInfoMapper.queryBookListByPage(pageRequest);
 for (BookInfo book:books){
 if (book.getStatus()==0){
 book.setStatusCN("⽆效");
 }else if (book.getStatus()==1){
 book.setStatusCN("可借阅");
 }else {
 book.setStatusCN("不可借阅");
 }
 }
 return new PageResult<>(count,books);
}

存在的问题:

  1. 翻⻚信息需要返回数据的总数和列表信息, 需要查两次SQL

  2. 图书状态: 图书状态和数据库存储的status有⼀定的对应关系 如果后续状态码有变动, 我们需要修改项⽬中所有涉及的代码, 这种情况, 通常采⽤枚举类来处理映射关系

创建enmus⽬录, 创建BookStatus类:

java 复制代码
package com.example.book_manage_240827.enums;

public enum BookStatusEnums {
    DELETE(0,"无效"),
    NORMAL(1,"可借阅"),
    FORBIDDEN(2,"不可借阅"),
    ;
    private int code;
    private String desc;

    BookStatusEnums(int code, String desc) {
        this.code = code;
        this.desc = desc;
    }
    //根据code, 获取描述
    public static BookStatusEnums getDescByCode(int code){
        switch (code){
            case 0: return BookStatusEnums.DELETE;
            case 1: return BookStatusEnums.NORMAL;
            case 2: return BookStatusEnums.FORBIDDEN;
        }
        return BookStatusEnums.DELETE;
    }

    public int getCode() {
        return code;
    }

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

    public String getDesc() {
        return desc;
    }

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

getNameByCode: 通过code来获取对应的枚举, 以获取该枚举对应的中⽂名称 后续如果有状态变更, 只需要修改该枚举类即可

BookService的代码, 可以修改如下:

java 复制代码
 public PageResult<BookInfo> getListByPage(PageRequest pageRequest) {
        //1. 查询记录的总数
        Integer count = bookInfoMapper.count();
        //2. 查询当前页的数据
        List<BookInfo> bookInfos = bookInfoMapper.queryListBypage(pageRequest);
        for (BookInfo bookInfo:bookInfos){
            //根据状态, 设置描述
            bookInfo.setStateCN(BookStatusEnums.getDescByCode(bookInfo.getStatus()).getDesc());
        }
        return new PageResult<>(bookInfos,count,pageRequest);
    }

BookInfoMapper :

图书列表按id降序排列:

java 复制代码
 /**
     * 查询总数
     */
    @Select("select count(1) from book_info where `status` <>0")
    Integer count();
    /**
     * 查询当前页的数据
     */
    @Select("select * from book_info where `status` <>0 order by id desc limit #{offset}, #{pageSize}")
    List<BookInfo> queryListBypage(PageRequest pageRequest);

访问http://127.0.0.1:8082/book/getListByPage ,查询第一页的数据;

访问第二页的数据,http://127.0.0.1:8082/book/getListByPage?currentPage=2

4.4 实现客户端代码

浏览器访问 book_list.html ⻚⾯时, 就去请求后端, 把后端返回数据显⽰在⻚⾯上 调⽤后端请求: /book/getListByPage?currentPage=1

java 复制代码
 getBookList();
        getBookList();
        function getBookList() {
            $.ajax({
                type: "get",
                url: "/book/getListByPage",
                success: function (result) {
                    if (result != null) {
                        var finalHtml = "";
                        for (var book of result) {
                            finalHtml += '<tr>';
                            finalHtml += '<td><input type="checkbox" name="selectBook" ' +
                                'value="'+book.id+'" id="selectBook" class="book-select"></td>';
                            finalHtml += '<td>'+book.id+'</td>';
                            finalHtml += '<td>'+book.bookName+'</td>';
                            finalHtml += '<td>'+book.author+'</td>';
                            finalHtml += '<td>'+book.count+'</td>';
                            finalHtml += '<td>'+book.price+'</td>';
                            finalHtml += '<td>'+book.publish+'</td>';
                            finalHtml += '<td>'+book.statusCN+'</td>';
                            finalHtml += '<td><div class="op">';
                            finalHtml += '<a href="book_update.html?bookId='+book.id+'">修改</a>';
                            finalHtml += '<a href="javascript:void(0)"onclick="deleteBook('+book.id+')">删除</a>';
                            finalHtml += '</div></td>';
                            finalHtml += "</tr>";
                        }
                        $("tbody").html(finalHtml);
                    }
                }
            });
        }

此时, url还未设置 currentPage 参数 ,我们直接使⽤ location.search 从url中获取参数信息即可 ;

location.search : 获取url的查询字符串 (包含问号) 如: url: http://127.0.0.1:8082/book_list.html?currentPage=1

location.search : ?currentPage=1

java 复制代码
 $.ajax({
                type: "get",
                url: "/book/getListByPage"+ location.search,

接下来处理分页信息

分页插件 --->本案例中, 分⻚代码采⽤了⼀个分⻚组件 ;

分⻚组件⽂档介绍: jqPaginator分⻚组件 ,使⽤时, 只需要按照 [使⽤说明]部分的⽂档, 把代码复制粘贴进来就可以了(提供的前端代码中, 已经包含该部分内容)

onPageChange :回调函数,当换⻚时触发(包括初始化第⼀⻚的时候),会传⼊两个参数: 1、"⽬标⻚"的⻚码,Number类型

2、触发类型,可能的值:"init"(初始化),"change"(点击分⻚)
我们在图书列表信息加载之后, 需要分⻚信息, 同步加载:

分⻚组件需要提供⼀些信息:

totalCounts: 总记录数,

pageSize: 每⻚的个数,

visiblePages: 可视⻚数

currentPage: 当前⻚码

这些信息中, pageSize 和 visiblePages 前端直接设置即可. totalCounts 后端已经提供, currentPage 也可以从参数中取到, 但太复杂了, 咱们直接由后端返回即可.

修改后端代码

  1. 为避免后续还需要其他请求处的信息, 我们直接在PageResult 添加 PageRequest 属性

  2. 处理返回结果, 填充 PageRequest

PageResult:

java 复制代码
package com.example.book_manage_240827.model;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;

@AllArgsConstructor//全参构造
@NoArgsConstructor//无参构造
@Data
public class PageResult<T> {
    private List<T> records;
    //当前的t是bookinfo类型
    private Integer count;//所有的记录数
    private PageRequest pageRequest;//当前页的记录数据

    public PageResult(Integer count, PageRequest pageRequest, List<T> records)
    {
        this.count = count;
        this.pageRequest = pageRequest;
        this.records = records;
    }

}

BookService:

java 复制代码
    public PageResult<BookInfo> getListByPage(PageRequest pageRequest) {
        //1. 查询记录的总数
        Integer count = bookInfoMapper.count();
        //2. 查询当前页的数据
        List<BookInfo> bookInfos = bookInfoMapper.queryListBypage(pageRequest);
        for (BookInfo bookInfo:bookInfos){
            //根据状态, 设置描述
            bookInfo.setStateCN(BookStatusEnums.getDescByCode(bookInfo.getStatus()).getDesc());
        }
        return new PageResult<>(count,pageRequest,bookInfos);
    }

后端数据返回后, 我们加载⻚⾯信息, 把分⻚代码挪到getBookList⽅法中;

java 复制代码
   getBookList();
        getBookList();
        function getBookList() {
            $.ajax({
                type: "get",
                url: "/book/getListByPage"+ location.search,
                success: function (result) {
                    if (result != null) {
                        var finalHtml = "";
                        for (var book of result) {
                            finalHtml += '<tr>';
                            finalHtml += '<td><input type="checkbox" name="selectBook" ' +
                                'value="'+book.id+'" id="selectBook" class="book-select"></td>';
                            finalHtml += '<td>'+book.id+'</td>';
                            finalHtml += '<td>'+book.bookName+'</td>';
                            finalHtml += '<td>'+book.author+'</td>';
                            finalHtml += '<td>'+book.count+'</td>';
                            finalHtml += '<td>'+book.price+'</td>';
                            finalHtml += '<td>'+book.publish+'</td>';
                            finalHtml += '<td>'+book.statusCN+'</td>';
                            finalHtml += '<td><div class="op">';
                            finalHtml += '<a href="book_update.html?bookId='+book.id+'">修改</a>';
                            finalHtml += '<a href="javascript:void(0)"onclick="deleteBook('+book.id+')">删除</a>';
                            finalHtml += '</div></td>';
                            finalHtml += "</tr>";
                        }
                        $("tbody").html(finalHtml);
                        //翻页信息
                        $("#pageContainer").jqPaginator({
                            totalCounts: 100, //总记录数
                            pageSize: 10,    //每页的个数
                            visiblePages: 5, //可视页数
                            currentPage: 1,  //当前页码
                            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) {
                                console.log("第"+page+"页, 类型:"+type);
                            }
                        });
                    }
                }
            });
        }

完善⻚⾯点击代码: 当点击⻚码时: 跳转到⻚⾯: book_list.html?currentPage=? 修改上述代码代码:

java 复制代码
//页面初始化和页码点击时都会执行
  onPageChange: function (page, type) {
       if (type != 'init') {
           location.href = "book_list.html?currentPage=" + page;
         }

}

4.5 测试

浏览器访问地址http://127.0.0.1:8082/book_list.html ,即是图书列表第一页,结果如下:

点击第二页,结果如下:

5.修改图书

5.1 约定前后端交互接⼝

1、进⼊修改⻚⾯, 需要显⽰当前图书的信息:

java 复制代码
[请求]
/book/queryBookById?bookId=25
[参数]
⽆
[响应]
{
 "id": 25,
 "bookName": "图书21",
 "author": "作者2",
 "count": 999,
 "price": 222.00,
 "publish": "出版社1",
 "status": 2,
 "statusCN": null,
 "createTime": "2023-09-04T04:01:27.000+00:00",
 "updateTime": "2023-09-05T03:37:03.000+00:00"
}

即根据图书ID, 获取当前图书的信息;

2、点击修改按钮, 修改图书信息

java 复制代码
[请求]
/book/updateBook
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
[参数]
id=1&bookName=图书1&author=作者1&count=23&price=36&publish=出版社1&status=1
[响应]
"" //失败信息, 成功时返回空字符串

我们约定, 浏览器给服务器发送⼀个 /book/updateBook 这样的 HTTP 请求, form表单的形式来 提交数据 ,服务器返回处理结果, 返回""表⽰修改图书成功,;否则, 返回失败信息;

5.2 实现服务器代码

BookController:

java 复制代码
   @RequestMapping("/queryBookById")
    public BookInfo queryBookById(Integer bookId){
        if (bookId==null || bookId<=0){
            return new BookInfo();
        }
        BookInfo bookInfo = bookService.queryBookById(bookId);
        return bookInfo;
    }
    @RequestMapping("/updateBook")
    public String updateBook(BookInfo bookInfo) {
        log.info("修改图书:{}", bookInfo);
        try {
            bookService.updateBook(bookInfo);
            return "";
        } catch (Exception e) {
            log.error("修改图书失败", e);
            return e.getMessage();
        }
    }

业务层: BookService:

java 复制代码
  public BookInfo queryBookById(Integer bookId) {
        return bookInfoMapper.queryBookById(bookId);
    }

    public Integer updateBook(BookInfo bookInfo) {
        return bookInfoMapper.updateBook(bookInfo);
    }

数据层: 根据图书ID,查询图书信息

java 复制代码
  /**
     * 根据ID查询图书信息
     */
    @Select("select * from book_info where `status` <>0 and id= #{id}")
    BookInfo queryBookById(Integer id);
    /**
     * 根据ID, 修改图书信息
     */
    Integer updateBook(BookInfo bookInfo);

对于update接口,我们采⽤xml的⽅式来实现;

定义Mapper接⼝: BookInfoMapper

java 复制代码
 Integer updateBook(BookInfo bookInfo);

xml实现:

创建bookInfoMapper.xml文件:文件路径和代码如下如下:

java 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.book_manage_240827.mapper.BookInfoMapper">
    <update id="updateBook">
        update book_info
        <set>
            <if test="bookName!=null">
                book_name = #{bookName},
            </if>
            <if test="author!=null">
                author =#{author},
            </if>
            <if test="count!=null">
                count = #{count},
            </if>
            <if test="price!=null">
                price =#{price},
            </if>
            <if test="publish">
                publish = #{publish},
            </if>
            <if test="status!=null">
                status =#{status},
            </if>
        </set>
        where id = #{id}
    </update>
</mapper>

5.3 实现客户端代码

1、在列表⻚时, 我们已经补充了[修改] 的链接:

java 复制代码
 <div>
   <button class="btn btn-outline-info" type="button" onclick="location.href='book_add.html'">添加图书</button>
    </div>

2、点击[修改] 链接时, 就会⾃动跳转到 http://127.0.0.1:8082/book_update.html?bookId=2 (2为对应的图书ID)

进⼊[修改图书]⻚⾯时, 需要先从后端拿到当前图书的信息, 显⽰在⻚⾯上:

java 复制代码
$.ajax({
            type: "get",
            url: "/book/queryBookById"+location.search,
            success: function (result) {
                //前端根据后端返回结果, 针对不同的情况进行处理
                    if (result != null) {
                        console.log(result)
                        $("#bookId").val(result.id);
                        $("#bookName").val(result.bookName);
                        $("#bookAuthor").val(result.author);
                        $("#bookStock").val(result.count);
                        $("#bookPrice").val(result.price);
                        $("#bookPublisher").val(result.publish);
                        $("#bookStatus").val(result.status);
                    }
                }
        });

补全修改图书的⽅法:

java 复制代码
function update() {
            $.ajax({
            type:"post",
            url: "/book/updateBook",
            data: $("#updateBook").serialize(),
            success:function(result){
                if(result == "") {
                    location.href = "book_list.html"
                }else{
                    console.log(result);
                    alert("更新失败"+result);
                }
            },
                error: function (error) {
                    console.log(error);
                }
                });
        }

我们修改图书信息, 是根据图书ID来修改的, 所以需要前端传递的参数中, 包含图书ID. 有两种⽅式:

  1. 获取url中参数的值(⽐较复杂, 需要拆分url)

  2. 在form表单中, 再增加⼀个隐藏输⼊框, 存储图书ID, 随 $("#updateBook").serialize() ⼀起提交到后端

下面我们采⽤第⼆种⽅式 在form表单中,添加隐藏输⼊框

java 复制代码
<form id="updateBook">
 <input type="hidden" class="form-control" id="bookId" name="id">
 <div class="form-group">
 <label for="bookName">图书名称:</label>
 <input type="text" class="form-control" id="bookName" name="bookName">
 </div>

hidden 类型的 <input>元素 :隐藏表单, 用户不可⻅、不可改的数据,在用户提交表单时,这些数据会⼀并发送出

使⽤场景: 正被请求或编辑的内容的 ID. 这些隐藏的 input 元素在渲染完成的⻚⾯中完全不可⻅,⽽ 且没有⽅法可以使它重新变为可⻅.

⻚⾯加载时, 给该hidden框赋值

java 复制代码
 $("#bookId").val(result.id);
                        $("#bookName").val(result.bookName);
                        $("#bookAuthor").val(result.author);
                        $("#bookStock").val(result.count);
                        $("#bookPrice").val(result.price);
                        $("#bookPublisher").val(result.publish);
                        $("#bookStatus").val(result.status);

5.4 测试

点击修改,

出现修改页面:

在修改正确的数据之后点击确定,返回到图书列表页;

查看修改前与修改后的数据变化:

ps:本项目未完待续,如果对你有所帮助的话,就请一键三连哦!!!

相关推荐
数据小爬虫@1 分钟前
如何利用java爬虫获得淘宝商品评论
java·开发语言·爬虫
喜欢猪猪2 分钟前
面试题---深入源码理解MQ长轮询优化机制
java
天天进步201521 分钟前
Vue+Springboot用Websocket实现协同编辑
vue.js·spring boot·websocket
草莓base32 分钟前
【手写一个spring】spring源码的简单实现--bean对象的创建
java·spring·rpc
drebander1 小时前
使用 Java Stream 优雅实现List 转化为Map<key,Map<key,value>>
java·python·list
乌啼霜满天2491 小时前
Spring 与 Spring MVC 与 Spring Boot三者之间的区别与联系
java·spring boot·spring·mvc
tangliang_cn1 小时前
java入门 自定义springboot starter
java·开发语言·spring boot
程序猿阿伟1 小时前
《智能指针频繁创建销毁:程序性能的“隐形杀手”》
java·开发语言·前端
Grey_fantasy1 小时前
高级编程之结构化代码
java·spring boot·spring cloud
弗锐土豆1 小时前
工业生产安全-安全帽第二篇-用java语言看看opencv实现的目标检测使用过程
java·opencv·安全·检测·面部