目录
一、数据库表设计
二、引入MyBatis 和MySQL 驱动依赖
三、配置数据库 & 日志
四、Model创建
五、枚举类 + 常量类用户登录
六、用户登录
七、添加图书
八、图书列表
九、修改图书
十、删除图书
十一、批量删除
十二、强制登录
十三、前端代码
(1)login.html
(2)book_add.html
(3)book_list.html
(4)book.update.html
十四、测试
(1)登录界面
(2)添加图书
(3)翻页,图书列表
(4)修改图书
(5)删除图书
(6)批量删除图书
之前写的内容地址:综合性练习(后端代码练习4)——图书管理系统-CSDN博客 ,项目的gitte地址:https://gitee.com/cool_tao6/studying-java-ee-advanced/tree/master/Books_Management_System
一、数据库表设计
数据库表是应用程序开发中的一个重要环节,数据库表的设计往往会决定我们的应用需求是否能顺利实现,甚至决定我们的实现方式。如何设计表以及这些表有哪些字段,这些表存在哪些关系 也是非常重要的。
数据库表设计是依据业务需求来设计的。如何设计出优秀的数据库表,与经验有很大关系。
数据库表通常分为两种:实体表 和 关系表。
分析我们的需求,图书管理系统相对来说比较简单,只有两个实体:用户和图书,并且用户和图书之间没有关联关系。
表的具体字段设计,也与需求有关。
用户表有用户名和密码即可(复杂的业务可能还涉及昵称,年龄等资料)
图书表有哪些字段,也是参考需求页面(通常不是一个页面决定的,而是要对整个系统进行全面分析观察后定的)。
创建数据库 book_test,SQL如下:
-- 创建数据库
DROP DATABASE IF EXISTS book_test;
CREATE DATABASE book_test DEFAULT CHARACTER SET utf8mb4;
-- 用户表
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 和MySQL 驱动依赖
修改pom.xml文件,内容如下:
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
或者使用Edit Starters添加,如图:
三、配置数据库 & 日志
在properties.yml下添加如下内容:
spring.application.name:
Books_Management_System
# 数据库连接配置
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/mybatis_test?characterEncoding=utf8&useSSL=false
username: root
password: 1234
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
configuration: #配置驼峰自动转换
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
map-underscore-to-camel-case: true #打印sql语句
mapper-locations: classpath:mapper/*Mapper.xml
四、Model创建
BookInfo类 实体书类:
@Data
public class BookInfo {
private Integer id;
private String bookName;
private String author;
private Integer count;
private BigDecimal price;
private String publish;
private Integer status;//1-可借阅 2-不可借阅
private String statusCN;
private Date createTime;
private Date update_time;
}
UserInfo类 用户类:
@Data
public class UserInfo {
private Integer id;
private String userName;
private String password;
private Integer deleteFlag;
private Date createTime;
private Date updateTime;
}
PageRequest类 查询图书列表 前端发来的请求类:
import lombok.Data;
@Data
public class PageRequest {
private Integer pageNum = 1;
private Integer pageSize = 10;
private Integer offset;
public Integer getOffset() {
return (pageNum - 1) * pageSize;
}
}
PageResult类 查询图书列表 返回的结果类:
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@AllArgsConstructor//全参构造函数
@NoArgsConstructor//无参构造函数
@Data
public class PageResult <T>{
private List<T> records;
private Integer count;
private PageRequest pageRequest;
}
Result类 查询图书列表 返回的结果类(对的PageResult 进行扩展):
import com.example.books_management_system.enums.ResultStatus;
import lombok.Data;
@Data
public class Result <T> {
private ResultStatus code; //业务码, 200-成功, -2-失败, -1-失败未登录
private String errMsg; //错误信息,如果业务成功,errMsg为空
private T data;
public static <T> Result success(T data) {
Result<PageResult<BookInfo>> result = new Result<>();
result.setCode(ResultStatus.SUCCESS);
result.setData((PageResult<BookInfo>) data);
return result;
}
public static <T> Result nologin() {
Result<PageResult<BookInfo>> result = new Result<>();
result.setCode(ResultStatus.NOLOGIN);
result.setErrMsg("用户未登录");
result.setData(null);
return result;
}
public static <T> Result fail(String msg) {
Result<PageResult<BookInfo>> result = new Result<>();
result.setCode(ResultStatus.FAIL);
result.setErrMsg(msg);
return result;
}
public static <T> Result fail(ResultStatus resultStatus, String msg) {
Result<PageResult<BookInfo>> result = new Result<>();
result.setCode(resultStatus);
result.setErrMsg(msg);
return result;
}
}
五、枚举类 + 常量类用户登录
BookStatus枚举类 枚举图书的状态(删除、可借阅、不可借阅):
public enum BookStatus {
DELETE(0, "删除"),
NORMAL(1, "可借阅"),
FORBIDDEN(2, "不可借阅")
;
private Integer code;
private String desc;
BookStatus(Integer code, String desc) {
this.code = code;
this.desc = desc;
}
/**
* 根据code,返回描述信息
* 其余两种写法:
* // public static String getDescByCode2(Integer code) {
* // switch (code) {
* // case 0: return DELETE.getDesc();
* // case 1: return NORMAL.getDesc();
* // case 2:
* // default:
* // return FORBIDDEN.getDesc();
* // }
* // }
* // static Map<Integer, String> map = new HashMap<>();
* // public static String getDescByCode3(Integer code) {
* // return map.get(code);
* // }
*/
public static BookStatus getDescByCode(Integer code) {
switch (code) {
case 0: return DELETE;
case 1: return NORMAL;
case 2:
default:
return FORBIDDEN;
}
}
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;
}
}
ResultStatus枚举类 枚举返回结果的code(SUCCESS、FAIL、NOLOGIN):
public enum ResultStatus {
SUCCESS(200),
FAIL(-1),
NOLOGIN(-2),
;
private int code;
ResultStatus(int code) {
this.code = code;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
}
Constant常量类 session的常量值:
public class Constant {
public static final String USER_SESSION_KEY = "user_session_key";
}
六、用户登录
UserController类:
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("/login")
public String login(String userName, String password, HttpSession session) {
//1、校验参数
//2、校验密码是否正确
//3、返回响应结果
System.out.println(userName + " " + password);
if(!StringUtils.hasLength(userName) || !StringUtils.hasLength(password)) {
return "用户名或者密码为空";
}
UserInfo userInfo = userService.getUserInfoByName(userName);
if(userInfo == null) {
return "用户不存在";
}
if(!password.equals(userInfo.getPassword())) {
return "密码错误";
}
//根据用户名称,去数据库查询用户信息,如果未查询到,说明用户不存在
//如果查询到用户信息,比对密码是否正确
//正确的情况
session.setAttribute(Constant.USER_SESSION_KEY, userInfo);
return "";
}
}
UserService类:
@Service
public class UserService {
@Autowired
private UserInfoMapper userInfoMapper;
public UserInfo getUserInfoByName(String userName) {
return userInfoMapper.getUserInfoByName(userName);
}
}
UserInfoMapper类:
@Mapper
public interface UserInfoMapper {
@Select("select * from user_info where user_name = #{name} and delete_flag = 0")
UserInfo getUserInfoByName(String name);
}
七、添加图书
BookController类:
@Slf4j
@RestController
@RequestMapping("/book")
public class BookController {
@Autowired
BookService bookService;
/**
* 添加图书
*/
@RequestMapping("/addBook")
public String addBook(BookInfo bookInfo) {
//校验参数
log.info("添加图书,接收到参数:bookInfo {}", bookInfo);
if(!StringUtils.hasLength(bookInfo.getBookName())
|| !StringUtils.hasLength(bookInfo.getAuthor())
|| bookInfo.getCount() == null
|| bookInfo.getPrice() == null
|| !StringUtils.hasLength(bookInfo.getPublish())
|| bookInfo.getStatus() == null) {
return "输入参数不合法";
}
//添加图书
try {
Integer result = bookService.insertBook(bookInfo);
if(result > 0) {
//添加成功
return "";
}
}catch (Exception e) {
log.error("添加图书异常, e: " +e);
}
return "添加失败";
}
}
BookService类:
@Slf4j
@Service
public class BookService {
@Autowired
BookMapper bookMapper;
public Integer insertBook(BookInfo bookInfo) {
return bookMapper.insertBook(bookInfo);
}
}
BookMapper类:
@Mapper
public interface BookMapper {
/**
* 插入图书
*/
@Insert("insert into book_info(book_name, author, count, price, publish, `status`) " +
"values (#{bookName}, #{author}, #{count}, #{price}, #{publish}, #{status})")
Integer insertBook(BookInfo bookInfo);
}
八、图书列表
BookController类:
@Slf4j
@RestController
@RequestMapping("/book")
public class BookController {
@Autowired
BookService bookService;
/**
* 查询图书列表
*/
@RequestMapping("/getBookListByPage")
public Result<PageResult<BookInfo>> getBookListByPage(PageRequest pageRequest, HttpSession session) {
log.info("查询图书的列表, 请求参数pageRequest: {}", pageRequest);
//从session中获取用户信息
//如果用户信息为空, 说明用户未登录
UserInfo loginUserInfo = (UserInfo) session.getAttribute(Constant.USER_SESSION_KEY);
if(loginUserInfo == null || loginUserInfo.getId() < 0) {
return Result.nologin();
}
//参数校验
if(pageRequest.getPageNum() == null) {
//返回默认第一页,如果pageSize也没设置,则会使用默认的10
pageRequest.setPageNum(1);
}
PageResult<BookInfo> bookList = bookService.getBookListByPage(pageRequest);
return Result.success(bookList);
}
}
BookService类:
@Slf4j
@Service
public class BookService {
@Autowired
BookMapper bookMapper;
public PageResult<BookInfo> getBookListByPage(PageRequest pageRequest) {
//1、获取总记录数
Integer count = bookMapper.count();
//2、获取当前页的记录
List<BookInfo> bookInfos = bookMapper.queryBookByPage(pageRequest.getOffset(), pageRequest.getPageSize());
//3、处理状态
for(BookInfo bookInfo : bookInfos) {
bookInfo.setStatusCN(BookStatus.getDescByCode(bookInfo.getStatus()).getDesc());//得到的是枚举类型
// bookInfo.setStatusCN(BookStatus.getDescByCode3(bookInfo.getStatus()));//hash映射
// bookInfo.setStatusCN(BookStatus.getDescByCode2(bookInfo.getStatus()));//得到的是字符串
// if(bookInfo.getStatus() == 2) {
// bookInfo.setStatusCN("不可借阅");
// } else {
// bookInfo.setStatusCN("可借阅");
// }
}
return new PageResult<BookInfo>(bookInfos, count, pageRequest);
}
}
BookMapper类:
@Mapper
public interface BookMapper {
/**
* 查询列表
*/
@Select("select * from book_info where status != 0 order by id desc limit #{offset}, #{limit}")
List<BookInfo> queryBookByPage(Integer offset, Integer limit);
}
九、修改图书
BookController类:
@Slf4j
@RestController
@RequestMapping("/book")
public class BookController {
@Autowired
BookService bookService;
/**
* 更新图书
*/
@RequestMapping("/updateBook")
public String updateBook(BookInfo bookInfo) {
log.info("更新图书, bookInfo: {}", bookInfo);
try {
Integer result = bookService.updateBookById(bookInfo);
if(result > 0) {
return "";
}
return "内部错误, 请练习管理员11111";
}catch (Exception e) {
log.error("更新图书失败, e: " + e);
return "内部错误, 请练习管理员";
}
}
/**
* 查询图书信息
* @param bookId
* @return
*/
@RequestMapping("/queryBookById")
public BookInfo queryBookById(Integer bookId) {
log.info("根据ID查询图书信息, id:" + bookId);
return bookService.queryBookById(bookId);
}
}
BookService类:
@Slf4j
@Service
public class BookService {
@Autowired
BookMapper bookMapper;
public Integer updateBookById(BookInfo bookInfo) {
log.info("更新图书, bookInfo: {}", bookInfo);
return bookMapper.updateBookById(bookInfo);
}
/**
* 根据ID查询图书信息
* @param bookId
* @return
*/
public BookInfo queryBookById(Integer bookId) {
BookInfo bookInfo = bookMapper.queryBookById(bookId);
bookInfo.setStatusCN(BookStatus.getDescByCode(bookInfo.getStatus()).getDesc());
return bookInfo;
}
}
BookMapper类:
@Mapper
public interface BookMapper {
/**
* 更新图书
*/
Integer updateBookById(BookInfo bookInfo);
/**
* 查询图书信息
*/
@Select("select * from book_info where id = #{bookId}")
BookInfo queryBookById(Integer bookId);
}
更新图书的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.books_management_system.mapper.BookMapper">
<update id="updateBookById">
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>
十、删除图书
BookController类:
@Slf4j
@RestController
@RequestMapping("/book")
public class BookController {
@Autowired
BookService bookService;
/**
* 删除图书
*/
@RequestMapping("/deleteBook")
public String deleteBook(Integer bookId) {
log.info("删除图书, bookId: {}", bookId);
try{
Integer result = bookService.deleteBookById(bookId);
if(result > 0) {
return "";
}
return "内部错误, 请练习管理员11111";
}catch (Exception e) {
log.error("删除图书失败, e: " + e);
return "内部错误, 请练习管理员";
}
}
}
BookService类:
@Slf4j
@Service
public class BookService {
@Autowired
BookMapper bookMapper;
/**
* 删除图书
*/
public Integer deleteBookById(Integer bookId) {
log.info("删除图书, bookId: {}", bookId);
BookInfo bookInfo = new BookInfo();
bookInfo.setId(bookId);
bookInfo.setStatus(0);
return bookMapper.deleteBookById(bookInfo);
}
}
BookMapper类:
@Mapper
public interface BookMapper {
/**
* 删除图书
*/
Integer deleteBookById(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.books_management_system.mapper.BookMapper">
<update id="deleteBookById">
update book_info
<set>
<if test="status != null">
status = #{status}
</if>
</set>
where id = #{id}
</update>
</mapper>
十一、批量删除
BookController类:
@Slf4j
@RestController
@RequestMapping("/book")
public class BookController {
@Autowired
BookService bookService;
/**
* 批量删除图书
*/
@RequestMapping("/batchDeleteBook")
public String batchDeleteBook(@RequestParam List<Integer> ids) {
log.info("批量删除图书, ids: {}", ids);
try{
Integer result = bookService.batchDeleteBookById(ids);
if(result > 0) {
return "";
}
return "内部错误, 请练习管理员11111";
}catch (Exception e) {
log.error("批量删除图书失败, e: " + e);
return "内部错误, 请练习管理员";
}
}
}
BookService类:
@Slf4j
@Service
public class BookService {
@Autowired
BookMapper bookMapper;
public Integer batchDeleteBookById(List<Integer> ids) {
return bookMapper.batchDeleteBookByIds(ids);
}
}
BookMapper类:
@Mapper
public interface BookMapper {
/**
* 批量删除
*/
Integer batchDeleteBookByIds(List<Integer> ids);
}
批量删除图书的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.books_management_system.mapper.BookMapper">
<delete id="batchDeleteBookByIds">
update book_info set status = 0
where id in
<!-- (1, 2, 3, 4)-->
<foreach collection="ids" open="(" close=")" item="id" separator=",">
#{id}
</foreach>
</delete>
</mapper>
十二、强制登录
强制登录是指不让用户在没登录的情况下访问某一列表,会使用到session,如果没登录直接访问图书某一列表的url,则让用户跳转到登录界面进行强制登录,所以不用这里每一个接口都要增添一个强制登录的功能,但已经写好了代码的接口,逐一添加会耗时好力,这里只给 图书列表 接口增添这个功能,后面学习了SpringBoot 统一功能处理再解决其他接口,添加强制登录。
图书列表也就是在Controller层进行修改,增添强制登录的功能,和上面图书列表的Controller代码一样:
@Slf4j
@RestController
@RequestMapping("/book")
public class BookController {
@Autowired
BookService bookService;
/**
* 查询图书列表
*/
@RequestMapping("/getBookListByPage")
public Result<PageResult<BookInfo>> getBookListByPage(PageRequest pageRequest, HttpSession session) {
log.info("查询图书的列表, 请求参数pageRequest: {}", pageRequest);
//从session中获取用户信息
//如果用户信息为空, 说明用户未登录
UserInfo loginUserInfo = (UserInfo) session.getAttribute(Constant.USER_SESSION_KEY);
if(loginUserInfo == null || loginUserInfo.getId() < 0) {
return Result.nologin();
}
//参数校验
if(pageRequest.getPageNum() == null) {
//返回默认第一页,如果pageSize也没设置,则会使用默认的10
pageRequest.setPageNum(1);
}
PageResult<BookInfo> bookList = bookService.getBookListByPage(pageRequest);
return Result.success(bookList);
}
}
十三、前端代码
(1)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() {
var userName = $("#userName").val();
var password = $("#password").val();
$.ajax({
url: "/user/login",
type: "post",
data: {
userName: $("#userName").val(),
password: $("#password").val()
},
success: function(result) {
if(result == "") {
location.href = "book_list.html?pageNum=1";
} else{
alert(result);
}
}
});
}
</script>
</body>
</html>
(2)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({
url: "/book/addBook",
type: "post",
data: $("#addBook").serialize(),
success: function(result) {
if(result == "") {
//添加成功
location.href = "book_list.html";
} else {
alert(result);
}
}
})
}
</script>
</body>
</html>
(3)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({
url: "/book/getBookListByPage" + location.search,
type: "get",
success: function (result) {
if (result.code == "NOLOGIN") {//用户登录
location.href = "login.html";
}
if (result.data != null && result.data.records != null) {
console.log("拿到参数")
var finalHtml = "";
for (book of result.data.records) {
finalHtml += '<tr>';
finalHtml += '<td><input type="checkbox" name="selectBook" value="' + book.id + '" id="' + book.id + '" 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>';
finalHtml += '<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>';
finalHtml += '</td>';
finalHtml += '</tr>';
}
$("tbody").html(finalHtml);
var data = result.data;
//翻页信息
$("#pageContainer").jqPaginator({
totalCounts: data.count, //总记录数
pageSize: 10, //每页的个数
visiblePages: 5, //可视页数
currentPage: data.pageRequest.pageNum, //当前页码
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);
if (type == "change") {
location.href = "book_list.html?pageNum=" + page;
}
}
});
}
}
});
}
function deleteBook(id) {
var isDelete = confirm("确认删除?");
if (isDelete) {
//删除图书
$.ajax({
url: "/book/deleteBook",
type: "post",
data: {
bookId: id
},
success: function (result) {
if (result == "") {
// location.href = "book_list.html" + location.search;
location.href = "book_list.html";
} else {
alert(result);
}
}
});
// 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);
$.ajax({
url: "/book/batchDeleteBook?ids=" + ids,
type: "post",
success: function (result) {
if (result == "") {
//删除成功
location.href = "book_list.html";
} else {
alert(result);
}
}
});
// alert("批量删除成功");
}
}
</script>
</div>
</body>
</html>
(4)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>
$.ajax({
url: "/book/queryBookById" + location.search,
type: "get",
success: function(book) {
$("#bookId").val(book.id);
$("#bookName").val(book.bookName);
$("#bookAuthor").val(book.author);
$("#bookStock").val(book.count);
$("#bookPrice").val(book.price);
$("#bookPublisher").val(book.publish);
$("#bookStatus").val(book.status)
}
});
function update() {
$.ajax({
url: "/book/updateBook",
type: "post",
data: $("#updateBook").serialize(),
success: function(result) {
if(result == "") {
//更新成功
location.href = "book_list.html";
} else{
alert(result);
}
}
})
// alert("更新成功");
// location.href = "book_list.html"
}
</script>
</body>
</html>
十四、测试
(1)登录界面
输入URL:http://127.0.0.1:8080/login.html
不输入密码:
输入错误密码:
输入正确用户名、密码:
登录成功,跳转到下面界面:
(2)添加图书
不安规定,少输入信息进行添加,会出现下面结果:
正确添加图书:
多出现了刚刚添加的图书信息
数据库内容:
(3)翻页,图书列表
第一页:
第二页:
(4)修改图书
点击确定,成功修改:
数据库内容:
(5)删除图书
点击确定,删除成功:
数据库内容:
(6)批量删除图书
勾选图书ID为19、18、17、16、15
点击批量删除,结果如下:
数据库内容: