完整代码已经打包放在结尾,如果只要源码的可以直接根据目录跳转到最后。
注:系统使用的架构是Spring+SpringBoot+SpringMVC+MyBatis,供学习使用,如果简历里面实在没有东西写了,可以参考本文章,毕竟看过就是了解,学过就是精通。
各个页面展示
登录页面:
图书列表页:
图书添加页:
图书修改页:
一.数据和环境准备
1.建立数据库表
图书系统的表比较简单,只有用户表和图书表。
-- 创建数据库
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, '民主与建设出版社');
2.创建项目,引入依赖
创建项目需要注意的点,语言选择Java,管理工具使用Maven,选择Java8版本和war包
创建项目的时候选择这些依赖,SpringBoot版本选择2.0开头的就行。
当然你也可以直接在pom文件当中添加依赖,如果依赖没有添加成功,可以去网上搜索适配自己的版本,我这里所有的依赖都适配Java8版本的。
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.5</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version> <!-- MySQL 5.7 对应的驱动版本 -->
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version> <!-- 适配 Java 8 的版本 -->
</dependency>
</dependencies>
3.配置数据库和日志
在application.properties配置文件中配置好自己的数据库,这里写自己数据库的名称和密码,如果忘了去网上搜一下教程,或者重装数据库。端口号一般都是3306,如果你自己没有改动的话,数据库表名称如果你是按照上面的建表来建立的话就是和我一样的book_test,如果不是就改成你自己数据库表的名称。
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/book_test?characterEncoding=utf8&useSSL=false
spring.datasource.username=root
spring.datasource.password=111111
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
mybatis.configuration.map-underscore-to-camel-case=true
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
mybatis.mapper-locations=classpath:mapper/**Mapper.xml
logging.file.name=spring-book.log
4.Java对象的创建
用户对象:
@Data
public class UserInfo {
private Integer id;
private String userName;
private String password;
private Integer deleteFlag;
private Date createTime;
private Date updateTime;
}
图书对象:
这里要注意的是price的数据类型不是浮点数float,而是使用了BigDecimal,这是因为浮点数在运算相加的时候可能会出现问题,所以使用专门处理这类数据的BigDecimal。除了必须用到的一些字段之外,还添加了创建时间和更新时间这两个字段。这是因为在企业中不论你创建什么样的数据库表都需要固定创建三个字段,id,createTime和updateTime。因为数据是一个企业的根本,所以对于数据库的创建和更改都必须做好记录,方便日后出现问题的时候寻找问题的来源。
@Data
public class BookInfo {
//图书ID
private Integer id;
//书名
private String bookName;
//作者
private String author;
//数量
private Integer count;
//定价(这里的注解是前端展示的精度)
@JsonFormat(shape = JsonFormat.Shape.STRING)
private BigDecimal price;
//出版社
private String publish;
//状态 0-⽆效 1-允许借阅 2-不允许借阅
private Integer status;
private String statusCN;
//创建时间
private Date createTime;
//更新时间
private Date updateTime;
}
总体的架构:
controller(控制层):接收用户请求,调用对应的业务逻辑层去处理数据
mapper(持久层):通过一系列的增删改查和数据库进行交互,用于数据的存储和读取
model(模型层):定义与业务数据相关的Java类,映射对应的数据库表字段,是应用数据的载体
service(业务逻辑层):真正处理业务逻辑的地方,处理各种复杂的业务场景,以及调用持久层的接口。
二:各个功能模块的实现
1.用户登录
约定好前后端接口
后端代码:
控制层:创建UserController类
注意这里的最后一个判断是为了强制登录写的,具体的实现我放在了图书列表页当中了。
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("/login")
public boolean login(String userName, String password, HttpSession session) {
// 校验发送的数据不为空
if (!StringUtils.hasLength(userName) || !StringUtils.hasLength(password)) {
return false;
}
// 根据用户名称查询对应的用户数据
UserInfo userInfo = userService.queryUserName(userName);
// 校验用户的密码是否正确
if (userInfo != null && userInfo.getPassword().equals(password)){
// 利用cookie和session建立会话机制,方便后面校验是否是登录状态去访问的其他页面
userInfo.setPassword("");
session.setAttribute(Constants.SESSION_USER_KEY,userInfo);
return true;
}
return false;
}
}
业务逻辑层:创建UserService类
@Slf4j
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public UserInfo queryUserName(String userName){
UserInfo result = null;
try {
// 调用持久层接口,查询用户表
result = userMapper.queryUserByName(userName);
}catch (Exception e){
// 处理异常,便于出错的时候定位后端问题
log.error(e.getMessage());
}
return result;
}
}
持久层:创建UserMapper类
@Mapper
public interface UserMapper {
@Select("select id,user_name,password,delete_flag,create_time,update_time " +
"from user_info where delete_flag = 0 and user_name = #{userName}")
UserInfo queryUserByName(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({
url:'/user/login',
type: 'post',
data:{
"userName":$("#userName").val(),
"password": $("#password").val()
},
success: function(result) {
if(result) {
location.href = "book_list.html";
}else{
alert("用户名或密码错误");
}
}
});
}
</script>
</body>
</html>
2.添加图书
约定前后端接口:
后端代码:
控制层:创建BookController类
@Slf4j
@Component
@RestController
@RequestMapping("/book")
public class BookController {
@Autowired
private BookService bookService;
//添加图书
@RequestMapping("/addBook")
public String addBook(BookInfo bookInfo){
log.info("添加图书:{}",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 {
bookService.addBook(bookInfo);
return "";
}catch (Exception e){
return e.getMessage();
}
}
}
业务逻辑层:创建BookService类
@Slf4j
@Service
public class BookService {
@Autowired
private BookMapper bookMapper;
public void addBook(BookInfo bookInfo) {
bookMapper.addBook(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 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>
$.ajax({
type: "post",
url: "/book/isLogin",
success:function (result){
if (result == null || result.data == null) {
alert("用户未登录!");
location.href = "login.html";
}
},
error:function (error) {
console.log(error);
}
});
function add() {
$.ajax({
type:"post",
url:"/book/addBook",
data:$("#addBook").serialize(),
success:function(result){
if(result == ""){
alert("添加成功");
location.href = "book_list.html";
}else{
console.log(result);
alert("添加失败"+result);
}
},
error:function(error) {
console.log(error);
}
});
}
</script>
</body>
</html>
3.图书列表
图书列表页是系统中最难实现的页面,因为这里涉及到分页。实现分页首先是使用分页插件,然后后端需要返回分页的数据:总的图书数量,每页展示数据的多少,当前页的图书数据。
我们可以根据分页查询语句来分析需要提供哪些数据给前端
查询第一页:select * from book_info limit 0,10;
查询第二页:select * from book_info limit 10,10;
查询第三页:select * from book_info limit 20,10;
总结:select * from book_info limit offset,pageSize;
首先我们可以看出pageSize是固定的,大小为10,每页展示数据为10;其次offset偏移量是变化的,它的大小正好等于(currentPage-1)*pageSize,所以我们就可以创建以下两个类。
pageRequest:
@Data
public class PageRequest {
private Integer currentPage = 1;//当前页的页码
private Integer pageSize = 10;//每页需要展示数据的数量
private Integer offset;
public int getOffset(){
return (currentPage-1)*pageSize;
}
}
pageResult:
@Data
public class PageResult<T> {
private Integer total;//总共的图书数
private List<T> records; //当页的图书列表
private PageRequest pageRequest;
public PageResult(Integer total, List<T> records,PageRequest pageRequest) {
this.total = total;
this.records = records;
this.pageRequest = pageRequest;
}
}
约定前后端接口:
后端代码:
图书列表这里还新增了一个功能:用户登录验证。
正常的逻辑中如果用户不进行登录就能直接访问其他页面这是不合理的,所以我们需要强制登录。用户登录验证一般会用到cookie和session的机制。我们在登录的时候已经将用户的信息存储在session中了,所以我们需要判断session中是否能取到用户的信息,如果取不到就直接跳转到登录页面。同时为了避免高内聚低耦合的情况我们将session中的key封装成一个类,把它定义为一个常量。
当我们去验证用户是否登录的时候会抛出一个新的问题,比如说我访问图书列表页的时候,后端返回的数据是null,那是用户未登录返回的null还是我后端处理数据的时候返回的null。所以我们需要加上两个字段去进行判断,一个是status(自定义状态码),一个是errorMessage(错误信息)。那除了图书列表页需要实现强制登录,图书修改页,图书添加页都需要进行验证。所以为了避免过多重复的代码,我们将后端返回的数据进行封装。这个Result类里面封装的就是,status,errorMessage,以及前端真正所需要的数据data。同样为了避免高内聚低耦合的情况,这里的status我们使用泛型来进行定义。总体代码如下:
Result类:
import lombok.Data;
@Data
public class Result<T> {
private ResultStatus status;
private String errorMessage;
private T data;
// 1.这里的思路是首先封装一个结果类,因为不止一个界面需要确认登录状态
// 2.结果类中包含三个成员:结果状态(),错误信息(),返回结果的数据部分()
// 3.不同的状态码对应不同的方法,传入的参数也不同,然后在对应的方法中去设置Result结果类的参数
/**
* 对应执行success的方法,这里的<T>只泛型方法的声明
*/
public static <T> Result success(T data){
Result<T> result = new Result<>();
result.setStatus(ResultStatus.SUCCESS);
result.setErrorMessage("");
result.setData(data);
return result;
}
/**
*对应用户未登录的情况
*/
public static Result unlogin(){
Result result = new Result();
result.setStatus(ResultStatus.UNLOGIN);
result.setErrorMessage("用户未登录!");
result.setData(null);
return result;
}
/**
* 对应报错的情况
*/
public static Result fail(String msg){
Result result = new Result();
result.setStatus(ResultStatus.FAIL);
result.setErrorMessage(msg);
result.setData(null);
return result;
}
}
ResultStatus类:
public enum ResultStatus {
SUCCESS(200),
UNLOGIN(-1),
FAIL(-2);
private Integer code;
ResultStatus(Integer code){
this.code = code;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
}
注意这里还有个BookStatus类:这个类主要的作用是为了区分图书是否被删除。
public enum BookStatus {
NORMAL(1,"可借阅"),
DELETE(0,"删除"),
FORBIDDEN(2,"不可借阅")
;
private int code;
private String name;
BookStatus(int code, String name) {
this.code = code;
this.name = name;
}
public static BookStatus getStatusCN(int code){
switch (code) {
case 1:
return BookStatus.NORMAL;
case 0:
return BookStatus.DELETE;
default:
return BookStatus.FORBIDDEN;
}
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
控制层:BookController类
//获取图书列表
@RequestMapping("/getBookListByPage")
public Result getBookListByPage(PageRequest pageRequest, HttpSession session){
log.info("获取图书列表:PageRequest:{}"+pageRequest);
if (session.getAttribute(Constants.SESSION_USER_KEY) == null){
return Result.unlogin();
}
UserInfo userInfo =(UserInfo)session.getAttribute(Constants.SESSION_USER_KEY);
if (userInfo == null || userInfo.getId() <= 0 || "".equals(userInfo.getUserName())) {
return Result.unlogin();
}
PageResult<BookInfo> result = bookService.getBookListByPage(pageRequest);
return Result.success(result);
}
//验证用户登录
@RequestMapping("/isLogin")
public Result isLogin(HttpSession session){
UserInfo userInfo = (UserInfo)session.getAttribute(Constants.SESSION_USER_KEY);
if (userInfo == null || userInfo.getId() <= 0 || "".equals(userInfo.getUserName())){
return Result.unlogin();
}
return Result.success("用户已登录");
}
业务逻辑层:BookService类
public PageResult<BookInfo> getBookListByPage(PageRequest pageRequest) {
Integer count = bookMapper.count();
List<BookInfo> bookInfos = bookMapper.queryBookInfoByPage(pageRequest);
if (bookInfos != null || bookInfos.size() > 0){
for (BookInfo bookInfo : bookInfos) {
String statusCn = BookStatus.getStatusCN(bookInfo.getStatus()).getName();
bookInfo.setStatusCN(statusCn);
}
}
PageResult<BookInfo> result = new PageResult<>(count,bookInfos,pageRequest);
return result;
}
持久层:BookMapper类
public interface BookMapper {
@Select("select * from book_info where status != 0 order by id desc limit #{offset},#{pageSize}")
List<BookInfo> queryBookInfoByPage(PageRequest pageRequest);
}
前端代码: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 == null || result.data == null){
location.href = "login.html";
return;
}
console.log(result);
var finalHtml = '';
var data = result.data;
for(var book of data.records) {
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>';
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></td></tr>';
}
console.log(finalHtml);
$("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 != 'init'){
console.log("第" + page + "页, 类型:" + type);
location.href = "book_list.html?currentPage="+page;
}
}
});
}
});
}
</script>
</div>
</body>
</html>
4.修改图书
修改图书第一步是点击修改按钮后跳转到修改页面的同时也需要将需要修改图书的信息返回给前端,第二步才是修改图书里面的数据,最后返回给后端保存到数据库。所以我们上面的图书列表的代码中,在执行跳转到修改页面的时候也将修改图书的book.Id传过去了。修改图书这一功能我们需要写两个接口,一个是根据book.id查询图书的接口,另一个是update修改图书的接口。除此之外,因为修改的操作比较复杂,如果使用注解的方式来实现动态sql会比较难,所以我们这里使用xml的方式实现
在resources中新建mapper包,然后新建BookMapper.xml文件,这里的命名最好和我一样,因为配置文件中需要和这里的名称对应。
BookMapper.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.springbook.demos.web.mapper.BookMapper">
<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>
后端代码:
控制层:BookController类
//单个图书查询
@RequestMapping("/queryBookById")
public BookInfo queryBookById(Integer bookId){
if (bookId == null || bookId <= 0) {
return null;
}
BookInfo result = bookService.queryBookById(bookId);
return result;
}
//修改图书(删除图书也是这个接口)
@RequestMapping("/updateBook")
public String updateBook(BookInfo bookInfo){
log.info("修改图书:{}",bookInfo);
if ("".equals(bookInfo.getBookName())
|| "".equals(bookInfo.getAuthor())
|| "".equals(bookInfo.getPublish())
|| bookInfo.getCount() == null
|| bookInfo.getPrice() == null
|| bookInfo.getStatus() == null) {
log.info("bookId:{}", bookInfo.getId());
return "0";
}
try {
Integer result = bookService.updateBook(bookInfo);
if (result == 0) {
return "新增图书失败,请联系管理员";
}
return "";
}catch (Exception e){
log.error("修改图书失败",e);
return e.getMessage();
}
}
业务逻辑层:BookService类
public BookInfo queryBookById(Integer bookId) {
BookInfo bookInfo = bookMapper.queryBookById(bookId);
return bookInfo;
}
public Integer updateBook(BookInfo bookInfo) {
Integer result = 0;
try{
result = bookMapper.updateBook(bookInfo);
}catch (Exception e){
log.error("更新图书失败,e:{}",e);
}
return result;
}
持久层:BookMapper类
@Mapper
public interface BookMapper {
@Select("select * from book_info where id = #{bookId} and status<> 0")
BookInfo queryBookById(Integer bookId);
Integer updateBook(BookInfo bookInfo);
}
前端代码: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({
type: "post",
url: "/book/isLogin",
success:function (result){
if (result == null || result.data == null) {
alert("用户未登录!");
location.href = "login.html";
}
},
error:function (error) {
console.log(error);
}
});
$.ajax({
url:'/book/queryBookById'+location.search,
type:'get',
success:function (book) {
if (book != null){
$("#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==""){
alert("更新成功");
location.href="book_list.html"
}else if (result === "0"){
alert("参数输入错误,请检查之后提交!")
} else {
console.log(result);
alert("修改失败:"+result)
}
},
error:function (error){
console.log(error);
}
});
}
</script>
</body>
</html>
5.删除图书
删除一般有逻辑删除和物理删除。物理删除好理解就是直接从数据库当中删除,逻辑删除一般是前端看不到这条数据,但是在数据库中这条数据依然存在。所以我们在数据库中增加status这个字段来判断数据是否被删除。再往深层次想,我们只需要修改status这个字段,之前在写修改图书功能的时候已经写过修改接口了。所以我们这里的删除完全可以由修改图书的接口来实现,并且只需要完善一下前端代码即可。
前端代码:
<!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 == null || result.data == null){
location.href = "login.html";
return;
}
console.log(result);
var finalHtml = '';
var data = result.data;
for(var book of data.records) {
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>';
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></td></tr>';
}
console.log(finalHtml);
$("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 != 'init'){
console.log("第" + page + "页, 类型:" + type);
location.href = "book_list.html?currentPage="+page;
}
}
});
}
});
}
function deleteBook(id) {
var isDelete = confirm("确认删除?");
if (isDelete) {
//删除图书
$.ajax({
url: '/book/updateBook',
type: 'post',
data:{
id: id,
status: 0
},
success:function (result){
alert("删除成功");
location.href = "book_list.html";
}
});
}
}
</script>
</div>
</body>
</html>
6.批量删除
批量删除数据其实就是批量修改数据,所以这里我们需要自己新写一个接口, 同时修改数据我们使用xml的方式来进行修改。大体的思路是前端将复选框中选中的id发送给后端,后端使用List的数据类型来接收。
后端代码:
完整的BookMapper.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.springbook.demos.web.mapper.BookMapper">
<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>
<update id="batchDeleteBook">
update book_info set status = 0 where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</update>
</mapper>
控制层:BookController类
@RequestMapping("/batchDeleteBook")
public boolean batchDeleteBook(@RequestParam List<Integer> ids){
log.info("批量删除图书:ids{}",ids);
try {
bookService.batchDeleteBook(ids);
}catch (Error e){
log.error("批量删除错误,e:",e);
return false;
}
return true;
}
业务逻辑层:BookService类
public Integer batchDeleteBook(List<Integer> ids) {
if (ids == null) {
return 0;
}
return bookMapper.batchDeleteBook(ids);
}
持久层:BookMapper类
@Mapper
public interface BookMapper {
Integer batchDeleteBook(@Param("ids") List<Integer> ids);
}
前端代码:完整的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 == null || result.data == null){
location.href = "login.html";
return;
}
console.log(result);
var finalHtml = '';
var data = result.data;
for(var book of data.records) {
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>';
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></td></tr>';
}
console.log(finalHtml);
$("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 != 'init'){
console.log("第" + page + "页, 类型:" + type);
location.href = "book_list.html?currentPage="+page;
}
}
});
}
});
}
function deleteBook(id) {
var isDelete = confirm("确认删除?");
if (isDelete) {
//删除图书
$.ajax({
url: '/book/updateBook',
type: 'post',
data:{
id: id,
status: 0
},
success:function (result){
alert("删除成功");
location.href = "book_list.html";
}
});
}
}
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"
}
}
});
}
}
</script>
</div>
</body>
</html>
三:省流版(各个模块的完整代码)
BookController:
package com.example.springbook.demos.web.controller;
import com.example.springbook.demos.web.model.*;
import com.example.springbook.demos.web.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.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpSession;
import java.util.List;
@Slf4j
@Component
@RestController
@RequestMapping("/book")
public class BookController {
@Autowired
private BookService bookService;
//添加图书
@RequestMapping("/addBook")
public String addBook(BookInfo bookInfo){
log.info("添加图书:{}",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 {
bookService.addBook(bookInfo);
return "";
}catch (Exception e){
return e.getMessage();
}
}
//获取图书列表
@RequestMapping("/getBookListByPage")
public Result getBookListByPage(PageRequest pageRequest, HttpSession session){
log.info("获取图书列表:PageRequest:{}"+pageRequest);
if (session.getAttribute(Constants.SESSION_USER_KEY) == null){
return Result.unlogin();
}
UserInfo userInfo =(UserInfo)session.getAttribute(Constants.SESSION_USER_KEY);
if (userInfo == null || userInfo.getId() <= 0 || "".equals(userInfo.getUserName())){
return Result.unlogin();
}
PageResult<BookInfo> result = bookService.getBookListByPage(pageRequest);
return Result.success(result);
}
//单个图书查询
@RequestMapping("/queryBookById")
public BookInfo queryBookById(Integer bookId){
if (bookId == null || bookId <= 0) {
return null;
}
BookInfo result = bookService.queryBookById(bookId);
return result;
}
//修改图书(删除图书也是这个接口)
@RequestMapping("/updateBook")
public String updateBook(BookInfo bookInfo){
log.info("修改图书:{}",bookInfo);
if ("".equals(bookInfo.getBookName())
|| "".equals(bookInfo.getAuthor())
|| "".equals(bookInfo.getPublish())
|| bookInfo.getCount() == null
|| bookInfo.getPrice() == null
|| bookInfo.getStatus() == null) {
log.info("bookId:{}", bookInfo.getId());
return "0";
}
try {
Integer result = bookService.updateBook(bookInfo);
if (result == 0) {
return "新增图书失败,请联系管理员";
}
return "";
}catch (Exception e){
log.error("修改图书失败",e);
return e.getMessage();
}
}
@RequestMapping("/batchDeleteBook")
public boolean batchDeleteBook(@RequestParam List<Integer> ids){
log.info("批量删除图书:ids{}",ids);
try {
bookService.batchDeleteBook(ids);
}catch (Error e){
log.error("批量删除错误,e:",e);
return false;
}
return true;
}
@RequestMapping("/isLogin")
public Result isLogin(HttpSession session){
UserInfo userInfo = (UserInfo)session.getAttribute(Constants.SESSION_USER_KEY);
if (userInfo == null || userInfo.getId() <= 0 || "".equals(userInfo.getUserName())){
return Result.unlogin();
}
return Result.success("用户已登录");
}
}
UserController:
package com.example.springbook.demos.web.controller;
import com.example.springbook.demos.web.model.Constants;
import com.example.springbook.demos.web.model.UserInfo;
import com.example.springbook.demos.web.service.UserService;
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;
import javax.servlet.http.HttpSession;
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("/login")
public boolean login(String userName, String password, HttpSession session) {
// 校验发送的数据不为空
if (!StringUtils.hasLength(userName) || !StringUtils.hasLength(password)) {
return false;
}
// 根据用户名称查询对应的用户数据
UserInfo userInfo = userService.queryUserName(userName);
// 校验用户的密码是否正确
if (userInfo != null && userInfo.getPassword().equals(password)){
// 利用cookie和session建立会话机制,方便后面校验是否是登录状态去访问的其他页面
userInfo.setPassword("");
session.setAttribute(Constants.SESSION_USER_KEY,userInfo);
return true;
}
return false;
}
}
BookMapper:
package com.example.springbook.demos.web.mapper;
import com.example.springbook.demos.web.model.BookInfo;
import com.example.springbook.demos.web.model.PageRequest;
import org.apache.ibatis.annotations.*;
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 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> queryBookInfoByPage(PageRequest pageRequest);
@Select("select * from book_info where id = #{bookId} and status<> 0")
BookInfo queryBookById(Integer bookId);
Integer updateBook(BookInfo bookInfo);
Integer batchDeleteBook(@Param("ids") List<Integer> ids);
}
UserMapper:
package com.example.springbook.demos.web.mapper;
import com.example.springbook.demos.web.model.UserInfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
@Mapper
public interface UserMapper {
@Select("select id,user_name,password,delete_flag,create_time,update_time " +
"from user_info where delete_flag = 0 and user_name = #{userName}")
UserInfo queryUserByName(String userName);
}
BookInfo:
package com.example.springbook.demos.web.model;
import com.fasterxml.jackson.annotation.JsonFormat;
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;
//定价(这里的注解是前端展示的精度)
@JsonFormat(shape = JsonFormat.Shape.STRING)
private BigDecimal price;
//出版社
private String publish;
//状态 0-⽆效 1-允许借阅 2-不允许借阅
private Integer status;
private String statusCN;
//创建时间
private Date createTime;
//更新时间
private Date updateTime;
}
BookStatus:
package com.example.springbook.demos.web.model;
public enum BookStatus {
NORMAL(1,"可借阅"),
DELETE(0,"删除"),
FORBIDDEN(2,"不可借阅")
;
private int code;
private String name;
BookStatus(int code, String name) {
this.code = code;
this.name = name;
}
public static BookStatus getStatusCN(int code){
switch (code) {
case 1:
return BookStatus.NORMAL;
case 0:
return BookStatus.DELETE;
default:
return BookStatus.FORBIDDEN;
}
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Constants:
package com.example.springbook.demos.web.model;
public class Constants {
public static final String SESSION_USER_KEY = "session_user_key";
}
PageRequest:
package com.example.springbook.demos.web.model;
import lombok.Data;
@Data
public class PageRequest {
private Integer currentPage = 1;
private Integer pageSize = 10;
private Integer offset;
public int getOffset(){
return (currentPage-1)*pageSize;
}
}
PageResult:
package com.example.springbook.demos.web.model;
import lombok.Data;
import java.util.List;
@Data
public class PageResult<T> {
private Integer total;//总共的图书数
private List<T> records; //当页的图书列表
private PageRequest pageRequest;
public PageResult(Integer total, List<T> records,PageRequest pageRequest) {
this.total = total;
this.records = records;
this.pageRequest = pageRequest;
}
}
Result:
package com.example.springbook.demos.web.model;
import lombok.Data;
@Data
public class Result<T> {
private ResultStatus status;
private String errorMessage;
private T data;
// 1.这里的思路是首先封装一个结果类,因为不止一个界面需要确认登录状态
// 2.结果类中包含三个成员:结果状态(),错误信息(),返回结果的数据部分()
// 3.不同的状态码对应不同的方法,传入的参数也不同,然后在对应的方法中去设置Result结果类的参数
/**
* 对应执行success的方法,这里的<T>只泛型方法的声明
*/
public static <T> Result success(T data){
Result<T> result = new Result<>();
result.setStatus(ResultStatus.SUCCESS);
result.setErrorMessage("");
result.setData(data);
return result;
}
/**
*对应用户未登录的情况
*/
public static Result unlogin(){
Result result = new Result();
result.setStatus(ResultStatus.UNLOGIN);
result.setErrorMessage("用户未登录!");
result.setData(null);
return result;
}
/**
* 对应报错的情况
*/
public static Result fail(String msg){
Result result = new Result();
result.setStatus(ResultStatus.FAIL);
result.setErrorMessage(msg);
result.setData(null);
return result;
}
}
ResultStatus:
package com.example.springbook.demos.web.model;
public enum ResultStatus {
SUCCESS(200),
UNLOGIN(-1),
FAIL(-2);
private Integer code;
ResultStatus(Integer code){
this.code = code;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
}
UserInfo:
package com.example.springbook.demos.web.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;
}
BookService:
package com.example.springbook.demos.web.service;
import com.example.springbook.demos.web.mapper.BookMapper;
import com.example.springbook.demos.web.model.BookInfo;
import com.example.springbook.demos.web.model.BookStatus;
import com.example.springbook.demos.web.model.PageRequest;
import com.example.springbook.demos.web.model.PageResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.util.List;
@Slf4j
@Service
public class BookService {
@Autowired
private BookMapper bookMapper;
public void addBook(BookInfo bookInfo) {
bookMapper.addBook(bookInfo);
}
public PageResult<BookInfo> getBookListByPage(PageRequest pageRequest) {
Integer count = bookMapper.count();
List<BookInfo> bookInfos = bookMapper.queryBookInfoByPage(pageRequest);
if (bookInfos != null || bookInfos.size() > 0){
for (BookInfo bookInfo : bookInfos) {
String statusCn = BookStatus.getStatusCN(bookInfo.getStatus()).getName();
bookInfo.setStatusCN(statusCn);
}
}
PageResult<BookInfo> result = new PageResult<>(count,bookInfos,pageRequest);
return result;
}
public BookInfo queryBookById(Integer bookId) {
BookInfo bookInfo = bookMapper.queryBookById(bookId);
return bookInfo;
}
public Integer updateBook(BookInfo bookInfo) {
Integer result = 0;
try{
result = bookMapper.updateBook(bookInfo);
}catch (Exception e){
log.error("更新图书失败,e:{}",e);
}
return result;
}
public Integer batchDeleteBook(List<Integer> ids) {
if (ids == null) {
return 0;
}
return bookMapper.batchDeleteBook(ids);
}
}
UserService:
package com.example.springbook.demos.web.service;
import com.example.springbook.demos.web.mapper.UserMapper;
import com.example.springbook.demos.web.model.UserInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Slf4j
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public UserInfo queryUserName(String userName){
UserInfo result = null;
try {
// 调用持久层接口,查询用户表
result = userMapper.queryUserByName(userName);
}catch (Exception e){
// 处理异常,便于出错的时候定位后端问题
log.error(e.getMessage());
}
return result;
}
}
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>
$.ajax({
type: "post",
url: "/book/isLogin",
success:function (result){
if (result == null || result.data == null) {
alert("用户未登录!");
location.href = "login.html";
}
},
error:function (error) {
console.log(error);
}
});
function add() {
$.ajax({
type:"post",
url:"/book/addBook",
data:$("#addBook").serialize(),
success:function(result){
if(result == ""){
alert("添加成功");
location.href = "book_list.html";
}else{
console.log(result);
alert("添加失败"+result);
}
},
error:function(error) {
console.log(error);
}
});
}
</script>
</body>
</html>
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 == null || result.data == null){
location.href = "login.html";
return;
}
console.log(result);
var finalHtml = '';
var data = result.data;
for(var book of data.records) {
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>';
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></td></tr>';
}
console.log(finalHtml);
$("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 != 'init'){
console.log("第" + page + "页, 类型:" + type);
location.href = "book_list.html?currentPage="+page;
}
}
});
}
});
}
function deleteBook(id) {
var isDelete = confirm("确认删除?");
if (isDelete) {
//删除图书
$.ajax({
url: '/book/updateBook',
type: 'post',
data:{
id: id,
status: 0
},
success:function (result){
alert("删除成功");
location.href = "book_list.html";
}
});
}
}
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"
}
}
});
}
}
</script>
</div>
</body>
</html>
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({
type: "post",
url: "/book/isLogin",
success:function (result){
if (result == null || result.data == null) {
alert("用户未登录!");
location.href = "login.html";
}
},
error:function (error) {
console.log(error);
}
});
$.ajax({
url:'/book/queryBookById'+location.search,
type:'get',
success:function (book) {
if (book != null){
$("#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==""){
alert("更新成功");
location.href="book_list.html"
}else if (result === "0"){
alert("参数输入错误,请检查之后提交!")
} else {
console.log(result);
alert("修改失败:"+result)
}
},
error:function (error){
console.log(error);
}
});
}
</script>
</body>
</html>
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({
url:'/user/login',
type: 'post',
data:{
"userName":$("#userName").val(),
"password": $("#password").val()
},
success: function(result) {
if(result) {
location.href = "book_list.html";
}else{
alert("用户名或密码错误");
}
}
});
}
</script>
</body>
</html>