目录
系统开发的步骤
数据库相关
创建数据库与表
引入Mybatis和MySQL依赖
数据库连接
后端
用户相关
图书相关
添加翻页功能
分页插件jqPaginator
设置图书状态(枚举)
设置价格精度
添加图书
接口定义
前后端实现
编辑
测试前后端
修改图书
接口定义
后端代码及测试
前端代码及测试
删除图书
接口定义
实现
批量删除
强制登录
实现展示
1.需求确认阶段:需求分析,需求评审
2.开发
1)方案设计
2)接口定义
3)开发业务代码
4)测试(自测+联调)
3.提测阶段
4.上线(发布阶段)
数据库设计:实体表(用户表、博客表、图书表)、关系表(一对多、多对多)
-- 创建数据库
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,'民主与建设出版社');
org.mybatis.spring.boot
mybatis-spring-boot-starter
2.3.1
com.mysql
mysql-connector-j
runtime
# 数据库连接配置
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/book_test?characterEncoding=utf8&useSSL=false
username: root #数据库用户名
password: password #密码
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语句
# 设置⽇志⽂件的⽂件名
logging:
file:
name: spring-book.log
UserController
@RequestMapping("/user")
@RestController
public class UserController {
@Autowired
public UserService userService;
@RequestMapping("/login")
public Boolean login(String userName, String password, HttpSession session){
//校验参数
if(!StringUtils.hasLength(userName) || !StringUtils.hasLength(password)) {
return false;
}
//if(userName.equals("admin")){}这种写法会报空指针异常0
//校验账号密码是否正确
//1.根据用户名查找用户信息
UserInfo userInfo=userService.getUserInfoByName(userName);
//比对密码是否正确
if(userInfo==null||userInfo.getId()<=0){
return false;
}
if(password.equals(userInfo.getPassword())){
//账号密码正确
//存session
userInfo.setPassword("");
session.setAttribute("userName",userInfo);
return true;
}
return false;
}
}
第一次请求的时候,请求第一页的内容,后端只需要返回第一页的信息即可。翻页可以由后端完成,也可以有前端完成。
前端完成需要一次性拿到所有的数据。
缺点:1)第一次请求响应时间长
2)数据如果要进行修改,前端无法感知
优点:后续响应快
后端完成,可以避免前端的缺点,但是需要多次请求。(本系统推荐后端,因为数据存在多次修改)
请求第一页的内容,后端只需要返回第一页的信息即可,请求第二页的内容,后端只需要返回第二页的内容。
select * from book_info limit ${offset},${limit}
在图书页添加一些数据以便展示
INSERT INTO `book_info` ( book_name, author, count, price, publish )
VALUES
( '图书2', '作者2', 29, 22.00, '出版社2' ),( '图书3', '作者2', 29, 22.00, '出版社3'),
( '图书4', '作者2', 29, 22.00, '出版社1' ),( '图书5', '作者2', 29, 22.00, '出版社1'),
( '图书6', '作者2', 29, 22.00, '出版社1' ),( '图书7', '作者2', 29, 22.00, '出版社1'),
( '图书8', '作者2', 29, 22.00, '出版社1' ),( '图书9', '作者2', 29, 22.00, '出版社1'),
( '图书10', '作者2', 29, 22.00, '出版社1'),( '图书11', '作者2', 29, 22.00, '出版社1'),
( '图书12', '作者2', 29, 22.00, '出版社1'),( '图书13', '作者2', 29, 22.00, '出版社1'),
( '图书14', '作者2', 29, 22.00, '出版社1'),( '图书15', '作者2', 29, 22.00, '出版社1'),
( '图书16', '作者2', 29, 22.00, '出版社1'),( '图书17', '作者2', 29, 22.00, '出版社1'),
( '图书18', '作者2', 29, 22.00, '出版社1'),( '图书19', '作者2', 29, 22.00, '出版社1'),
( '图书20', '作者2', 29, 22.00, '出版社1'),( '图书21', '作者2', 29, 22.00, '出版社1');
一共24条数据,设计一页展示10条,共三页。第一页返回1-10,第二页返回11-20,第三页返回21-24
通过MySQL语句实现
select * from book_info limit 0,10;
select * from book_info limit 10,10;
select * from book_info limit 20,4;
后端翻页select * from book_info limit ${offset},${limit}
limit就是每一页要显示的条数pageSize
前端需要告知的内容:
1.当前页:currentPage
2.每页显示的条数
后端返回结果:
1.当前页的内容:records
2.后端总条数
$('#id').jqPaginator({
totalPages: 100,
visiblePages: 10,
currentPage: 1,
onPageChange: function (num, type) {
$('#text').html('当前第' + num + '页');
}
});
/book/addBook
参数:BookInfo
返回结果:告诉前端是否添加成功
1)Boolean true:添加成功 false:添加失败
2)String " " 添加成功,不为空添加失败
3)对象 Boolean result 是否添加成功
String errorMsg 出错时候的错误原因
这里采用第二种方式:返回字符串
BookInfoMapper.java
@Insert("insert into book_info (book_name,author,count,price,publish,status)" +
"values(#{bookName},#{author},#{count},#{price},#{publish},#{status})")
Integer insertBook(BookInfo bookInfo);
BookController.java
@RequestMapping("/addBook")
public String addBook(BookInfo bookInfo) {
log.info("接收到添加图书请求,bookInfo:{}",bookInfo);
//参数校验
if (!StringUtils.hasLength(bookInfo.getBookName())
|| !StringUtils.hasLength(bookInfo.getAuthor())
|| bookInfo.getCount() < 0
|| bookInfo.getPrice() == null
|| !StringUtils.hasLength(bookInfo.getPublish())){
return "参数校验失败,请检查输入";
}
Integer result=bookService.addBook(bookInfo);
if(result<0){
log.error("添加图书出错:bookInfo:{}",bookInfo);
return "添加图书出错,请联系管理员";
}
return "";
}
BookService.java
//添加图书
public Integer addBook(BookInfo bookInfo){
Integer result=0;
try{
bookInfoMapper.insertBook(bookInfo);
}catch (Exception e){
log.error("添加图书出错,e:{}",e);
}
return result;
}
前端book_add.html前后端交互部分
后端
测试添加图书,使用postman测试:
每次测试前端页面时,记得进入页面看前端代码是否已经修改成最新的。如果没有则按F12再右键网页刷新按键。
再次确认页面代码是否为修改的最新版本。
Fiddlere抓包测试
在线解码工具查看发送的url内容是否正确
1.点击修改按钮时,希望把当前图书的信息显示出来
2.点击确定时把修改后的结果进行保存
1.查询图书信息,根据id
/book/queryBookInfoById
参数:bookId
返回结果:对应的图书信息 BookInfo
2.修改图书
/book/updateBook
参数:BookInfo
返回结果:是否修改成功(String " "空为成功。不为空,表示失败,返回错误信息。)
BookController.java
@RequestMapping("/updateBook")
public String updateBook(BookInfo bookInfo){
log.info("更新图书,接受到更新图书的请求,bookInfo:{}",bookInfo);
Integer result=bookService.updateBook(bookInfo);
if(result==0){
log.error("更新图书失败,请联系管理员");
return "更新图书失败,请联系管理员";
}
return "";
}
BookService.java
public Integer updateBook(BookInfo bookInfo){
Integer result=0;
try {
result=bookInfoMapper.updateBook(bookInfo);
}catch (Exception e){
log.error("更新图书失败,e:{}",e);
}
return result;
}
BookInfoMapper.java
Integer updateBook(BookInfo bookInfo);
使用xml配置文件动态操作数据库
application.properties文件中添加配置
mybatis.mapper-locations= classpath:mapper/**Mapper.xml
resource下建立文件目录结构
BookInfoMapper.xml文件中添加配置
BookInfoMapper.xml
update book_info
book_name=#{bookName},
author=#{author},
count=#{count},
price=#{price},
publish=#{publish},
status=#{status}
where id=#{id}
测试:postman
前后端交互部分