【JavaEE】MyBatis 综合练习(图书管理系统)

发布于:2025-04-01 ⋅ 阅读:(34) ⋅ 点赞:(0)


图书管理系统

一、数据库表

我们使用两张表,一张用户表uset_test来记录登录的用户信息,一张图书表book_test记录图书信息。

-- 创建数据库
DROP DATABASE IF EXISTS book_test;
CREATE DATABASE book_test DEFAULT CHARACTER SET utf8mb4;

use book_test;

-- 用户表 
DROP TABLE IF EXISTS user_info;
CREATE TABLE user_info (
 `id` INT NOT NULL AUTO_INCREMENT,
 `user_name` VARCHAR ( 128 ) NOT NULL,
 `password` VARCHAR ( 128 ) NOT NULL,
 `delete_flag` TINYINT ( 4 ) NULL DEFAULT 0,
 `create_time` DATETIME DEFAULT now(),
 `update_time` DATETIME DEFAULT now() ON UPDATE now(),
PRIMARY KEY ( `id` ),
UNIQUE INDEX `user_name_UNIQUE` ( `user_name` ASC )) ENGINE = INNODB DEFAULT 
CHARACTER 
SET = utf8mb4 COMMENT = '用户表';

-- 图书表 
DROP TABLE IF EXISTS book_info;
CREATE TABLE `book_info` (
 `id` INT ( 11 ) NOT NULL AUTO_INCREMENT,
 `book_name` VARCHAR ( 127 ) NOT NULL,
 `author` VARCHAR ( 127 ) NOT NULL,
 `count` INT ( 11 ) NOT NULL,
 `price` DECIMAL (7,2 ) NOT NULL,
 `publish` VARCHAR ( 256 ) NOT NULL,
 `status` TINYINT ( 4 ) DEFAULT 1 COMMENT '0-无效, 1-正常, 2-不允许借阅',
 `create_time` DATETIME DEFAULT now(),
 `update_time` DATETIME DEFAULT now() ON UPDATE now(),
PRIMARY KEY ( `id` ) 
) ENGINE = INNODB DEFAULT CHARSET = utf8mb4;
-- 初始化数据 
INSERT INTO user_info ( user_name, PASSWORD ) VALUES ( "admin", "admin" );
INSERT INTO user_info ( user_name, PASSWORD ) VALUES ( "zhangsan", "123456" );
-- 初始化图书数据 
INSERT INTO `book_info` (book_name,author,count, price, publish) 
VALUES ('活着', '余华', 29, 22.00, '北京文艺出版社');
INSERT INTO `book_info` (book_name,author,count, price, publish) 
VALUES ('平凡的世界', '路遥', 5, 98.56, '北京十月文艺出版社');
INSERT INTO `book_info` (book_name,author,count, price, publish) 
VALUES ('三体', '刘慈欣', 9, 102.67, '重庆出版社');
INSERT INTO `book_info` (book_name,author,count, price, publish) 
VALUES ('金字塔原理', '麦肯锡', 16, 178.00, '民主与建设出版社');

二、引入依赖:

引入MyBatis配置:
记得修改为自己的数据库名和密码。

spring:
  application:
    name: library
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/book_test?characterEncoding=utf8&useSSL=false
    username: root
    password: 1234
    driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
  configuration:
    map-underscore-to-camel-case: true #配置驼峰自动转换
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #打印sql语句
  mapper-locations: classpath:dao/*.xml

三、Model创建

在model包路径下,创建用户表和图书表对应的类。
用户类:

package com.example.library.model;

import lombok.Data;

import java.util.Date;
@Data
public class UserInfo {
    private Integer id;
    private String userName;
    private String password;
    private Integer deleteFlag;
    private Date createTime;
    private Date updateTime;
}

图书类:

package com.example.library.model;

import lombok.Data;

import java.math.BigDecimal;
import java.util.Date;

@Data
public class BookInfo {
    //图书ID
    private Integer id;
    //书名
    private String bookName;
    //作者
    private String author;
    //数量
    private Integer count;
    //定价
    private BigDecimal price;
    //出版社
    private String publish;
    //状态 0-⽆效 1-允许借阅 2-不允许借阅
    private Integer status;
    private String statusCN;
    //创建时间
    private Date createTime;
    //更新时间
    private Date updateTime;

}

四、用户登录

前后端交互约定:

[请求]
/user/login
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
[参数]
name=zhangsan&password=123456
[响应]
true //账号密码验证正确, 否则返回false

controller层代码:

  • 在去数据库查询之前,我们要先校验穿的参数是否合法,不为空
  • 数据库查询后,要看的到的是不是空值,防止空指针异常
  • 最后校验
package com.example.library.controller;

import com.example.library.model.UserInfo;
import com.example.library.service.UserInfoService;
import jakarta.servlet.http.HttpSession;
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;

@RequestMapping("/user")
@RestController
public class LoginController {
    @Autowired
    private UserInfoService userInfoService;
    @RequestMapping("/login")
    public Boolean login(String userName, String password, HttpSession session) {
        //校验参数
        if(!StringUtils.hasLength(userName)|| !StringUtils.hasLength(password) ) {
            return false;
        }
        //查询用户
        UserInfo userInfo = userInfoService.selectByName(userName);
        if(null == userInfo) return false;
        if(userInfo.getPassword().equals(password)) {
            session.setAttribute("userName",userName);
            return true;
        }
        return false;

    }
}

Service层代码:

  • 这一层代码只需要简单调用数据持久层代码即可。
package com.example.library.service;

import com.example.library.dao.UserInfoDao;
import com.example.library.model.UserInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserInfoService {
    @Autowired
    private UserInfoDao userInfoDao;
    public UserInfo selectByName(String userName) {
        return userInfoDao.selectByName(userName);
    }
}

dao层:

  • 简单根据名字条件查询一下,因为数据量小直接使用的 * 。
package com.example.library.dao;

import com.example.library.model.UserInfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

@Mapper
public interface UserInfoDao {
    @Select("select * from user_info where  user_name  = #{userName}")
    UserInfo selectByName(String userName);

}

前端代码:
login_html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="css/bootstrap.min.css">
    <link rel="stylesheet" href="css/login.css">
    <script type="text/javascript" src="js/jquery.min.js"></script>
</head>

<body>
<div class="container-login">
    <div class="container-pic">
        <img src="pic/computer.png" width="350px">
    </div>
    <div class="login-dialog">
        <h3>登陆</h3>
        <div class="row">
            <span>用户名</span>
            <input type="text" name="userName" id="userName" class="form-control">
        </div>
        <div class="row">
            <span>密码</span>
            <input type="password" name="password" id="password" class="form-control">
        </div>
        <div class="row">
            <button type="button" class="btn btn-info btn-lg" onclick="login()">登录</button>
        </div>
    </div>
</div>
<script src="js/jquery.min.js"></script>
<script>
    function login() {
        $.ajax({
            type: "post",
            url: "/user/login",
            data: {
                userName: $("#userName").val(),
                password: $("#password").val()
            },
            success: function(result){
                if(result === true){
                    //账号密码正确
                    location.href = "book_list.html?currentPage=1";
                }else {
                    alert("账号或密码错误");
                }
            }

        });
        // location.href = "book_list.html";
    }
</script>
</body>

</html>

五、添加图书

前后端接口:

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

controller层代码:

  • 我们在前端传给我们对象后,要根据我们设定的数据库的参数类型及条件,进行相应的判空以及超出条件。
  • 在添加图书前我们打印日志,出现异常时也打印日志。
package com.example.library.controller;


import com.example.library.model.BookInfo;
import com.example.library.service.BookService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.math.BigDecimal;
import java.util.List;
@Slf4j
@RequestMapping("/book")
@RestController
@Component
public class BookController {

    @Autowired
    private BookService bookService;
    @RequestMapping("/getList")
    public List<BookInfo> getList() {
        return bookService.getList();
    }

    @RequestMapping("/addBook")
    public String addBook(BookInfo bookInfo) {
        log.info("添加图书: " + bookInfo);
        
        if(null == bookInfo) return "不能为空";
        if(!StringUtils.hasLength(bookInfo.getBookName()) || bookInfo.getBookName().length() > 127) return "图书名不合规";
        if(!StringUtils.hasLength(bookInfo.getAuthor()) || bookInfo.getAuthor().length() > 127)  return "图书作者不合规";
        if(null == bookInfo.getCount() || bookInfo.getCount() < 0) return "余量不合规";
        if(null == bookInfo.getPrice() || bookInfo.getPrice().compareTo(new BigDecimal(0)) <= 0) return "价格不合规";
        if(!StringUtils.hasLength(bookInfo.getPublish()) || bookInfo.getPublish().length() > 127) return "出版社不合规";
        if(null == bookInfo.getStatus()
                || (0 != bookInfo.getStatus().compareTo(1) && 0 != bookInfo.getStatus().compareTo(2))
        ) return "状态不合规";
        try {
            bookService.addBook(bookInfo);
            return "";
        } catch (Exception e) {
            log.error("添加图书错误: " + e);
            return "添加图书错误";
        }
    }
}


Service层:

  • 直接简单调用dao层代码即可。
package com.example.library.service;

import com.example.library.dao.BookDao;
import com.example.library.model.BookInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;

@Service
public class BookService {
    @Autowired
    BookDao bookDao ;

    //插入一行图书
    public void addBook(BookInfo bookInfo) {
        bookDao.addBook(bookInfo);
    }


}

dao层:

  • 根据条件插入即可。
package com.example.library.dao;

import com.example.library.model.BookInfo;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

@Mapper
public interface BookDao {

    @Insert("insert into book_info (book_name, author, count, price, publish, status) " +
            "values (#{bookName}, #{author}, #{count}, #{price}, #{publish}, #{status});")
    Integer addBook(BookInfo bookInfo);

}

前端代码:
book_add.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>添加图书</title>
    <link rel="stylesheet" href="css/bootstrap.min.css">
    <link rel="stylesheet" href="css/add.css">

</head>

<body>

<div class="container">

    <div class="form-inline">
        <h2 style="text-align: left; margin-left: 10px;"><svg xmlns="http://www.w3.org/2000/svg" width="40"
                                                              fill="#17a2b8" class="bi bi-book-half" viewBox="0 0 16 16">
            <path
                    d="M8.5 2.687c.654-.689 1.782-.886 3.112-.752 1.234.124 2.503.523 3.388.893v9.923c-.918-.35-2.107-.692-3.287-.81-1.094-.111-2.278-.039-3.213.492V2.687zM8 1.783C7.015.936 5.587.81 4.287.94c-1.514.153-3.042.672-3.994 1.105A.5.5 0 0 0 0 2.5v11a.5.5 0 0 0 .707.455c.882-.4 2.303-.881 3.68-1.02 1.409-.142 2.59.087 3.223.877a.5.5 0 0 0 .78 0c.633-.79 1.814-1.019 3.222-.877 1.378.139 2.8.62 3.681 1.02A.5.5 0 0 0 16 13.5v-11a.5.5 0 0 0-.293-.455c-.952-.433-2.48-.952-3.994-1.105C10.413.809 8.985.936 8 1.783z" />
        </svg>
            <span>添加图书</span>
        </h2>
    </div>

    <form id="addBook">
        <div class="form-group">
            <label for="bookName">图书名称:</label>
            <input type="text" class="form-control" placeholder="请输入图书名称" id="bookName" name="bookName">
        </div>
        <div class="form-group">
            <label for="bookAuthor">图书作者</label>
            <input type="text" class="form-control" placeholder="请输入图书作者" id="bookAuthor" name="author" />
        </div>
        <div class="form-group">
            <label for="bookStock">图书库存</label>
            <input type="text" class="form-control" placeholder="请输入图书库存" id="bookStock" name="count"/>
        </div>

        <div class="form-group">
            <label for="bookPrice">图书定价:</label>
            <input type="number" class="form-control" placeholder="请输入价格" id="bookPrice" name="price">
        </div>

        <div class="form-group">
            <label for="bookPublisher">出版社</label>
            <input type="text" id="bookPublisher" class="form-control" placeholder="请输入图书出版社" name="publish" />
        </div>
        <div class="form-group">
            <label for="bookStatus">图书状态</label>
            <select class="custom-select" id="bookStatus" name="status">
                <option value="1" selected>可借阅</option>
                <option value="2">不可借阅</option>
            </select>
        </div>

        <div class="form-group" style="text-align: right">
            <button type="button" class="btn btn-info btn-lg" onclick="add()">确定</button>
            <button type="button" class="btn btn-secondary btn-lg" onclick="javascript:history.back()">返回</button>
        </div>
    </form>
</div>
<script type="text/javascript" src="js/jquery.min.js"></script>
<script>
    function add() {
        //前端应该进行参数校验, 此处省略
        //提交请求到后端
        $.ajax({
            type: "post",
            url: "/book/addBook",
            data: $("#addBook").serialize(),
            success: function(result){
                if(result== ""){
                    //添加图书成功
                    location.href = "book_list.html";
                }else {
                    //失败
                    alert(result);
                }
            }
        });
    }
</script>
</body>

</html>

六、图书列表

前后端接口:

[请求]
/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": "可借阅"
	 }, {
	 ......
	 } ]
}

由于我们要使用分页查询,所以我们要将每一次查询的页数和每页的数据数,都要当成参数传给后端。
后端返回的是当前的图书总数量,和当前的页下的图书。
所以我们将请求和响应都封装起来,在响应中我们把请求也作为成员,以便前端拿取参数。

响应:

package com.example.library.model;

import lombok.Data;

import java.util.List;

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


请求:

  • 我们在使用limit setoff传参的时候,第一个传的数字是当前页开始的前一个在的位置,所以我们直接在这个类中进行计算,避免在数据持久层进行计算。
package com.example.library.model;

import lombok.Data;

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

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

controller层代码:

    @RequestMapping("/getListByPage")
    public PageResult<BookInfo> getListByPage(PageRequest pageRequest) {
        //参数校验
        if(null == pageRequest) return null;
        if(pageRequest.getPageSize() <= 0 || pageRequest.getCurrentPage() <= 0) return null;

        PageResult<BookInfo> request = bookService.getListByPage(pageRequest);
        return request;
    }

Service层代码:
因为我们要根据图书的状态,反映图书可不可借阅的属性,由于是一一对应的关系,所以我们使用枚举类封装一下。
枚举类:

  • lombok的@Data注解是不能对枚举类使用的。
package com.example.library.enums;

public enum BookStatusEnum {
    DELETE(0 , "无效"),
    NORMAL(1,"可借阅"),
    FORBIDDEN(2,"不可借阅");

    public static String getStatusByCode(int code) {
        switch (code) {
            case 1: return NORMAL.getDesc();
            case 2: return FORBIDDEN.getDesc();
            case 0: return DELETE.getDesc();
            default: return null;
        }
    }

    private int code;
    private String desc;

    BookStatusEnum(int code, String desc) {
        this.code = code;
        this.desc = desc;
    }

    public int getCode() {
        return code;
    }

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

    BookStatusEnum() {
    }

    public String getDesc() {
        return desc;
    }

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

  • 调用dao层的方法,以及对状态映射即可。
    public PageResult<BookInfo> getListByPage(PageRequest pageRequest) {
        List<BookInfo> bookInfos = bookDao.getListByPage(pageRequest);
        //对状态进行映射
        for (BookInfo x:
             bookInfos) {
            x.setStatusCN(BookStatusEnum.getStatusByCode(x.getStatus()));
        }

        PageResult<BookInfo> result = new PageResult<>();
        //图书总数
        result.setTotal(bookDao.count());
        //当前页图书
        result.setRecords(bookInfos);
        //请求,以便前端拿取参数
        result.setPageRequest(pageRequest);
        return result;
    }

dao层:
因为返回的响应中,既有当前页面的图书数据,又有总的图书数,那么我们需要两个查询函数来实现。

    @Select("select * from book_info where status != 0 limit #{offset},  #{pageSize}")
    List<BookInfo> getListByPage(PageRequest pageRequest);

    @Select("select count(1) from book_info where status != 0")
    int count();

前端代码:
book_list.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>图书列表展示</title>
    <link rel="stylesheet" href="css/bootstrap.min.css">

    <link rel="stylesheet" href="css/list.css">
    <script type="text/javascript" src="js/jquery.min.js"></script>
    <script type="text/javascript" src="js/bootstrap.min.js"></script>
    <script src="js/jq-paginator.js"></script>

</head>

<body>
<div class="bookContainer">
    <h2>图书列表展示</h2>
    <div class="navbar-justify-between">
        <div>
            <button class="btn btn-outline-info" type="button" onclick="location.href='book_add.html'">添加图书</button>
            <button class="btn btn-outline-info" type="button" onclick="batchDelete()">批量删除</button>
        </div>
    </div>

    <table>
        <thead>
        <tr>
            <td>选择</td>
            <td class="width100">图书ID</td>
            <td>书名</td>
            <td>作者</td>
            <td>数量</td>
            <td>定价</td>
            <td>出版社</td>
            <td>状态</td>
            <td class="width200">操作</td>
        </tr>
        </thead>
        <tbody>
        
        </tbody>
    </table>

    <div class="demo">
        <ul id="pageContainer" class="pagination justify-content-center"></ul>
    </div>
    <script>

        getBookList();
        function getBookList() {
            $.ajax({
                type: "get",
                url: "/book/getListByPage"+location.search ,
                success: function(result){
                    if(result == null || result.records == null || result.pageRequest == null) return;
                    var books = result.records;
                    var finalHtml = "";
                    for(var book of books){
                        finalHtml += '<tr>';
                        finalHtml += '<td><input type="checkbox" name="selectBook" value="'+book.bookId+'" 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></tr>';
                    }
                    $("tbody").html(finalHtml);
                    //翻页信息
                    $("#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;
                            }
                        }
                    });
                }
            });
        }


        function deleteBook(id) {
            var isDelete = confirm("确认删除?");
            if (isDelete) {
                //删除图书
                alert("删除成功");
            }
        }
        function batchDelete() {
            var isDelete = confirm("确认批量删除?");
            if (isDelete) {
                //获取复选框的id
                var ids = [];
                $("input:checkbox[name='selectBook']:checked").each(function () {
                    ids.push($(this).val());
                });
                console.log(ids);
                alert("批量删除成功");
            }
        }

    </script>
</div>
</body>

</html>

七、修改图书

接口定义:
进⼊修改⻚⾯, 需要显⽰当前图书的信息:

[请求]
/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"
}

controller层:

    @RequestMapping("queryBookById")
    public BookInfo queryBookById(Integer bookId) {
        log.info("查询图书信息,图书id: "+bookId);
        return bookService.queryBookById(bookId);
    }

Service层:

    public BookInfo queryBookById(Integer bookId) {
       return bookDao.queryBookById(bookId);
    }

dao层:

    @Select("select * from book_info where status != 0 and id = #{bookId}")
    BookInfo queryBookById(Integer bookId);

点击修改按钮,修改图书信息,接口定义:

[请求]
/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
[响应]
“” //失败信息, 成功时返回空字符串

controller层:

  • 参数可以为空,但是要满足数据库字段的定义
    @RequestMapping("updateBook")
    public String updateBook(BookInfo bookInfo) {
        log.info("修改图书: " + bookInfo);
        //校验参数,参数可以为空
        if(null == bookInfo) return "不能为空";
        if(StringUtils.hasLength(bookInfo.getBookName()) && bookInfo.getBookName().length() > 127) return "图书名不合规";
        if(StringUtils.hasLength(bookInfo.getAuthor()) && bookInfo.getAuthor().length() > 127)  return "图书作者不合规";
        if(null != bookInfo.getCount() && bookInfo.getCount() < 0) return "余量不合规";
        if(null != bookInfo.getPrice() && bookInfo.getPrice().compareTo(new BigDecimal(0)) <= 0) return "价格不合规";
        if(StringUtils.hasLength(bookInfo.getPublish()) && bookInfo.getPublish().length() > 127) return "出版社不合规";
        if(null != bookInfo.getStatus()
                && (0 != bookInfo.getStatus().compareTo(1) && 0 != bookInfo.getStatus().compareTo(2))
        ) return "状态不合规";

        try {
            bookService.updateBook(bookInfo);
            return "";
        }catch (Exception e) {
            log.error("修改图书错误:"+e);
            return "修改图书错误";
        }
    }

servic层:

    public void updateBook(BookInfo bookInfo) {
        bookDao.updateBook(bookInfo);
    }

dao层:

  • 因为我们传的参数是可以为空的,那么我们要是有动态sql来写,xml文件更适合。
    Integer updateBook(BookInfo bookInfo);

xml文件:

<?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.library.dao.BookDao">
    <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!=null">
                publish = #{publish} ,
            </if>
            <if test="status!=null">
                status = #{status}
            </if>
        </set>
        where id = #{id}
    </update>
</mapper>

前端代码:
book_update.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>修改图书</title>
    <link rel="stylesheet" href="css/bootstrap.min.css">
    <link rel="stylesheet" href="css/add.css">
</head>

<body>

<div class="container">
    <div class="form-inline">
        <h2 style="text-align: left; margin-left: 10px;"><svg xmlns="http://www.w3.org/2000/svg" width="40"
                                                              fill="#17a2b8" class="bi bi-book-half" viewBox="0 0 16 16">
            <path
                    d="M8.5 2.687c.654-.689 1.782-.886 3.112-.752 1.234.124 2.503.523 3.388.893v9.923c-.918-.35-2.107-.692-3.287-.81-1.094-.111-2.278-.039-3.213.492V2.687zM8 1.783C7.015.936 5.587.81 4.287.94c-1.514.153-3.042.672-3.994 1.105A.5.5 0 0 0 0 2.5v11a.5.5 0 0 0 .707.455c.882-.4 2.303-.881 3.68-1.02 1.409-.142 2.59.087 3.223.877a.5.5 0 0 0 .78 0c.633-.79 1.814-1.019 3.222-.877 1.378.139 2.8.62 3.681 1.02A.5.5 0 0 0 16 13.5v-11a.5.5 0 0 0-.293-.455c-.952-.433-2.48-.952-3.994-1.105C10.413.809 8.985.936 8 1.783z" />
        </svg>
            <span>修改图书</span>
        </h2>
    </div>

    <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>
        <div class="form-group">
            <label for="bookAuthor">图书作者</label>
            <input type="text" class="form-control" id="bookAuthor" name="author"/>
        </div>
        <div class="form-group">
            <label for="bookStock">图书库存</label>
            <input type="text" class="form-control" id="bookStock" name="count"/>
        </div>
        <div class="form-group">
            <label for="bookPrice">图书定价:</label>
            <input type="number" class="form-control" id="bookPrice" name="price">
        </div>
        <div class="form-group">
            <label for="bookPublisher">出版社</label>
            <input type="text" id="bookPublisher" class="form-control" name="publish"/>
        </div>
        <div class="form-group">
            <label for="bookStatus">图书状态</label>
            <select class="custom-select" id="bookStatus" name="status">
                <option value="1" selected>可借阅</option>
                <option value="2">不可借阅</option>
            </select>
        </div>
        <div class="form-group" style="text-align: right">
            <button type="button" class="btn btn-info btn-lg" onclick="update()">确定</button>
            <button type="button" class="btn btn-secondary btn-lg" onclick="javascript:history.back()">返回</button>
        </div>
    </form>
</div>
<script type="text/javascript" src="js/jquery.min.js"></script>
<script>
    getBookInfo();
    function getBookInfo(){
        $.ajax({
            type: "get",
            url: "/book/queryBookById"+ location.search,
            success: function(bookInfo){
                if(bookInfo !=null ){
                    $("#bookId").val(bookInfo.id);
                    $("#bookName").val(bookInfo.bookName);
                    $("#bookAuthor").val(bookInfo.author);
                    $("#bookStock").val(bookInfo.count);
                    $("#bookPrice").val(bookInfo.price);
                    $("#bookPublisher").val(bookInfo.publish);
                    $("#bookStatus").val(bookInfo.status);

                }
            }
        });
    }
    function update() {
        $.ajax({
            type: "post",
            url: "/book/updateBook",
            data: $("#updateBook").serialize(),
            success: function(result){
                if(result === ""){
                    location.href = "book_list.html?currentPage=1";
                }else {
                    alert(result);
                }
            }

        });
    }
</script>
</body>

</html>

八、删除图书

删除操作一般不是真删除,而是将表示删除的状态变成删除,在客户端不显示出来。
接口定义:

[请求]
/book/deleteBook
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
[参数]
id=1&status=0
[响应]
“” //失败信息, 成功时返回空字符串

我们调用直接调用修改的代码即可,只需要封装一下接口。
controller层:

    @RequestMapping("deleteBook")
    public String deleteBook(Integer bookId) {
        log.info("删除图书: " + bookId);
        BookInfo bookInfo = new BookInfo();
        bookInfo.setId(bookId);
        bookInfo.setStatusCN(BookStatusEnum.getStatusByCode(0));
        try {
            bookService.updateBook(bookInfo);
            return "";
        }catch (Exception e) {
            log.error("修改图书错误:"+e);
            return "修改图书错误";
        }
    }

九、批量删除

接口定义:

[请求]
/book/batchDeleteBook
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
[参数]
[响应]
“” //失败信息, 成功时返回空字符串

controller层:

    @RequestMapping("/batchDeleteBook")
    public Boolean batchDeleteBook(Integer[] ids) {
        log.info("批量删除图书:bookIds: {}", ids);
        try {
            bookService.batchDeleteBook(ids);
            return true;
        }catch (Exception e) {
            log.error("删除图书错误" + e);
            return false;
        }

    }

Service层:

    public void batchDeleteBook(Integer[] ids) {
        bookDao.batchDeleteBook(ids);
    }

dao层:

    Integer batchDeleteBook(Integer[] ids);

xml文件:

    <update id="batchDeleteBook">
        update book_info set status = 0 where id in
        <foreach collection="ids" item="bookId" open="(" close=")" separator=",">
            #{bookId}
        </foreach>
    </update>

前端方法:

        function batchDelete() {
            var isDelete = confirm("确认批量删除?");
            if (isDelete) {
                //获取复选框的id
                var ids = [];
                $("input:checkbox[name='selectBook']:checked").each(function () {
                    ids.push($(this).val());
                });
                console.log(ids);
                //批量删除
                $.ajax({
                    type: "post",
                    url: "/book/batchDeleteBook?ids="+ids,
                    success: function (result) {
                        if (result) {
                            alert("删除成功");
                            //重新刷新⻚⾯
                            location.href = "book_list.html"
                        }
                    }
                });
            }
        }

十、强制登录

⽤⼾登录时, 我们已经把登录⽤⼾的信息存储在了Session中. 那就可以通过Session中的信息来判断⽤
⼾都是登录.

  1. 如果Session中可以取到登录⽤⼾的信息, 说明⽤⼾已经登录了, 可以进⾏后续操作
  2. 如果Session中取不到登录⽤⼾的信息, 说明⽤⼾未登录, 则跳转到登录⻚⾯.

我们的报错信息是很多的,我们单独使用null或者空对象是没法区分到底是什么原因造成的。所以我们将这个信息单独封装成类。

  • 因为我们的状态与错误信息是一一对应的,所以封装成枚举类,
  • 报错信息,在很多地方都要使用,所以我们提供方法,以便拿到
package com.example.library.model;

import com.example.library.enums.ResultStatusEnum;
import lombok.Data;

@Data
public class Result <T>{
    private ResultStatusEnum code;
    private String errMsg;
    private T data;

    /**
     * 业务执⾏成功时返回的⽅法
     *
     * @param data
     * @return
     */

    public static <T> Result success(T data) {
        Result result = new Result();
        result.setCode(ResultStatusEnum.SUCCESS);
        result.setErrMsg("");
        result.setData(data);
        return result;
    }
    /**
     *
     * * 用户未登录
     *      *
     *      * @param data
     *      * @return
     */
    public static  Result unLogin () {
        Result result = new Result();
        result.setCode(ResultStatusEnum.UNLOGIN);
        result.setErrMsg("用户未登录");
        result.setData(null);
        return result;
    }
    /**
     *
     * * 出错
     *      *
     *      * @param String errMsg
     *      * @return
     */
    public static <T> Result fail (String errMsg) {
        Result result = new Result();
        result.setCode(ResultStatusEnum.FAIL);
        result.setErrMsg(errMsg);
        return result;
    }
}

枚举类:

package com.example.library.enums;

public enum ResultStatusEnum {
    SUCCESS(200),
    UNLOGIN(-1),
    FAIL(-2);
    private int code;

    public int getCode() {
        return code;
    }

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

    ResultStatusEnum(int code) {
        this.code = code;
    }
}

controller:

  • 因为我们获取session时的参数,在很多地方都要使用,所以我们封装起来,以便后续更改,只需更改一处即可。
    @RequestMapping("/getListByPage")
    public Result getListByPage(PageRequest pageRequest, HttpSession httpSession) {
        //登录验证
        if(null == httpSession.getAttribute(Constants.SESSION_USER_KEY)) return Result.unLogin();
        UserInfo userInfo = (UserInfo) httpSession.getAttribute(Constants.SESSION_USER_KEY);
        //用户未登录
        if(null == userInfo || 0 >= userInfo.getId()) {
            return Result.unLogin();
        }

        //参数校验
        if(null == pageRequest) return Result.fail("参数错误");
        if(pageRequest.getPageSize() <= 0 || pageRequest.getCurrentPage() <= 0) return Result.fail("参数错误");

        PageResult<BookInfo> request = bookService.getListByPage(pageRequest);
        return Result.success(request);
    }
package com.example.library.constant;


public class Constants {
    public static final String SESSION_USER_KEY = "session_user_info";
}

由于获取session的参数变了,那么我们在登录的方法也要相应修改。

@RequestMapping("/login")
    public Boolean login(String userName, String password, HttpSession session) {
        //校验参数
        if(!StringUtils.hasLength(userName)|| !StringUtils.hasLength(password) ) {
            return false;
        }
        //查询用户
        UserInfo userInfo = userInfoService.selectByName(userName);
        if(null == userInfo) return false;
        if(userInfo.getPassword().equals(password)) {
            session.setAttribute(Constants.SESSION_USER_KEY,userInfo);
            return true;
        }
        return false;

    }

前端的获取图书列表的方法也要变。

getBookList();
        function getBookList() {
            $.ajax({
                type: "get",
                url: "/book/getListByPage"+location.search ,
                success: function(result){
                    if(result == null || result.code == "UNLOGIN" ) {
                        alert("用户未登录,请先登录");
                        location.href = "login.html";
                    }

                    if(result == null || result.data == null) return;
                    if(result.code == "FAIL" ) {
                        alert("参数错误");
                    }
                    var data = result.data;
                    var books = data.records;
                    var finalHtml = "";
                    for(var book of books){
                        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></tr>';
                    }
                    $("tbody").html(finalHtml);
                    //翻页信息
                    $("#pageContainer").jqPaginator({
                        totalCounts: data.total, //总记录数
                        pageSize: 10,    //每页的个数
                        visiblePages: 5, //可视页数
                        currentPage: data.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;
                            }
                        }
                    });
                }
            });
        }

网站公告

今日签到

点亮在社区的每一天
去签到