【Spring+MyBatis】_图书管理系统(上篇)

目录

[1. MyBatis与MySQL配置](#1. MyBatis与MySQL配置)

[1.1 创建数据库及数据表](#1.1 创建数据库及数据表)

[1.2 配置MyBatis与数据库](#1.2 配置MyBatis与数据库)

[1.2.1 增加MyBatis与MySQL相关依赖](#1.2.1 增加MyBatis与MySQL相关依赖)

[1.2.2 配置application.yml文件](#1.2.2 配置application.yml文件)

[1.3 增加数据表对应实体类](#1.3 增加数据表对应实体类)

[2. 功能1:用户登录](#2. 功能1:用户登录)

[2.1 约定前后端交互接口](#2.1 约定前后端交互接口)

[2.2 后端接口](#2.2 后端接口)

[2.3 前端页面](#2.3 前端页面)

[2.4 单元测试](#2.4 单元测试)

[3. 功能2:图书列表](#3. 功能2:图书列表)

[3.1 约定前后端交互接口](#3.1 约定前后端交互接口)

[3.2 后端接口](#3.2 后端接口)

[3.3 前端页面](#3.3 前端页面)

[3.4 单元测试](#3.4 单元测试)


1. MyBatis与MySQL配置

1.1 创建数据库及数据表

创建数据库book_test,建议选择编码为utf8mb4;

创建用户表user_info和图书表book_info;

sql 复制代码
-- 用户表
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, '民主与建设出版社');
INSERT INTO `book_info` (book_name,author,count, price, publish) VALUES ('图书5',
'作者5', 20, 45, '出版社5');
INSERT INTO `book_info` (book_name,author,count, price, publish) VALUES ('图书6',
'作者6', 20, 45, '出版社6');
INSERT INTO `book_info` (book_name,author,count, price, publish) VALUES ('图书7',
'作者7', 20, 45, '出版社7');
INSERT INTO `book_info` (book_name,author,count, price, publish) VALUES ('图书8',
'作者8', 20, 45, '出版社8');
INSERT INTO `book_info` (book_name,author,count, price, publish) VALUES ('图书9',
'作者9', 20, 45, '出版社9');
INSERT INTO `book_info` (book_name,author,count, price, publish) VALUES ('图书10',
'作者10', 20, 45, '出版社10');
INSERT INTO `book_info` (book_name,author,count, price, publish) VALUES ('图书11',
'作者11', 20, 45, '出版社11');
INSERT INTO `book_info` (book_name,author,count, price, publish) VALUES ('图书12',
'作者12', 20, 45, '出版社12');
INSERT INTO `book_info` (book_name,author,count, price, publish) VALUES ('图书13',
'作者13', 20, 45, '出版社13');

1.2 配置MyBatis与数据库

1.2.1 增加MyBatis与MySQL相关依赖

1.2.2 配置application.yml文件

html 复制代码
# 端口配置
server:
  port: 8080
# 数据库连接配置
spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/book_test?characterEncoding=utf8&useSSL=false
    username: root
    password: xxxxxx
    driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl  #配置打印MyBatis日志
    map-underscore-to-camel-case: true   #配置转换驼峰
# 设置日志文件的文件名
logging:
  file:
    name: /logger/spring-book.log

1.3 增加数据表对应实体类

创建model包并在其下创建BookInfo和UserInfo类:

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

import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;

import java.math.BigDecimal;

@Data
public class BookInfo {
    private Integer id;
    private String bookName;
    private String author;
    private Integer count;
//    前端展示精度
    @JsonFormat(shape = JsonFormat.Shape.STRING)
    private BigDecimal price;
    private String publish;
    private Integer status;  //1-可借阅,2-不可借阅
    private String statusCN; // status的中文展示
}
java 复制代码
package com.example.bookmanagementsystem.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;
}

2. 功能1:用户登录

2.1 约定前后端交互接口

请求:/User/login

参数:userName=admin&&password=admin

响应:成功返回true,失败返回false

2.2 后端接口

UserController类:

java 复制代码
package com.example.bookmanagementsystem.controller;

import com.example.bookmanagementsystem.model.UserInfo;
import com.example.bookmanagementsystem.service.UserService;
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 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;
        }
//        1、根据用户名查找用户信息
        UserInfo userInfo =userService.getUserInfoByName(userName);

//        2、校验密码正确性
        if(userInfo == null || userInfo.getId()<=0){
            return false;
        }
        if(password.equals(userInfo.getPassword())){
            // 存Session
            userInfo.setPassword("");
            session.setAttribute("userName",userInfo);
            return true;
        }
        return false;
    }
}

UserService类:

java 复制代码
package com.example.bookmanagementsystem.service;

import com.example.bookmanagementsystem.mapper.UserInfoMapper;
import com.example.bookmanagementsystem.model.UserInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {
    @Autowired
    private UserInfoMapper userInfoMapper;
    public UserInfo getUserInfoByName(String name){
        return userInfoMapper.selectUserByName(name);
    }

}

UserMapper接口:

java 复制代码
package com.example.bookmanagementsystem.mapper;

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

@Mapper
public interface UserInfoMapper {
//    根据用户名查询用户信息
    @Select("select* from user_info where user_name=#{name}")
    UserInfo selectUserByName(String name);
}

2.3 前端页面

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.4 单元测试

1、后端接口测试:

可在服务器日志处查看相关信息:

2、前端页面测试:

输入账号admin和密码admin后,即可成功登录;

3. 功能2:图书列表

3.1 约定前后端交互接口

1、前端发送给后端:(封装为对象:PageRequest)

(1)当前页currentPage;

(2)每页显示的条数pageSize;

2、后端发送给前端:(封装为对象:PageResult)

(1)当前页的内容records(类型为List<BookInfo>);

(2)总条数total;

关于SQL语句的分页查询关键字:limit与offset,常用方式如下:

1、select* from table_name limit num2,num1;

2、select* from table_name limit num1 offset num2;

以上两句都表示从num2+1行开始返回num1行数据;

3.2 后端接口

BookController类:

java 复制代码
package com.example.bookmanagementsystem.controller;

import com.example.bookmanagementsystem.model.BookInfo;
import com.example.bookmanagementsystem.model.PageRequest;
import com.example.bookmanagementsystem.model.PageResult;
import com.example.bookmanagementsystem.service.BookService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@Slf4j
@RequestMapping("/Book")
@RestController
public class BookController {
    @Autowired
    private BookService bookService;

    @RequestMapping("/getBookListByPage")
    public PageResult<BookInfo> getBookListByPage(PageRequest pageRequest){
        log.info("接收到查询翻页信息:pageRequest:{}",pageRequest);
        if(pageRequest.getPageSize()<0 || pageRequest.getCurrentPage()<1) {
            return null;
        }
        PageResult<BookInfo> bookInfoPageResult=null;
        try{
            bookInfoPageResult = bookService.selectBookInfoByPage(pageRequest);
        }catch (Exception e){
            log.error("查询翻页信息错误:e{}",e);
        }
        return bookInfoPageResult;
    }
}

BookService类:

java 复制代码
package com.example.bookmanagementsystem.service;

import com.example.bookmanagementsystem.enums.BookStatusEnum;
import com.example.bookmanagementsystem.mapper.BookInfoMapper;
import com.example.bookmanagementsystem.model.BookInfo;
import com.example.bookmanagementsystem.model.PageRequest;
import com.example.bookmanagementsystem.model.PageResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
@Service
public class BookService {
    @Autowired
    private BookInfoMapper bookInfoMapper;
     public PageResult<BookInfo> selectBookInfoByPage(PageRequest pageRequest){
         if(pageRequest == null){
             return null;
         }
//         获取总记录数
         Integer count = bookInfoMapper.count();
//         获取当前页内容
         List<BookInfo> bookInfos = bookInfoMapper.selectBookInfoByPage(
                 pageRequest.getOffset(), pageRequest.getPageSize());
         if(bookInfos !=null && bookInfos.size()>0){
             for(BookInfo bookInfo: bookInfos){
//                 根据状status获取图书状态定义
                 bookInfo.setStatusCN(BookStatusEnum.getNameByCode(bookInfo.getStatus()).getName());
             }
         }
         return new PageResult<>(bookInfos,count,pageRequest);
     }
}

并为BookService创建BookServiceEnum枚举:

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

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

    private int code;
    private String name;

    public static BookStatusEnum getNameByCode(int code){
        switch(code){
            case 0: return BookStatusEnum.DELETE;
            case 1: return BookStatusEnum.NORMAL;
            case 2: return BookStatusEnum.FORBIDDEN;
            default:
                return BookStatusEnum.FORBIDDEN;
        }
    }
    BookStatusEnum(int code, String name) {
        this.code = code;
        this.name = name;
    }

    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;
    }
}

BookInfoMapper接口:

java 复制代码
package com.example.bookmanagementsystem.mapper;

import com.example.bookmanagementsystem.model.BookInfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;

import java.util.List;

@Mapper
public interface BookInfoMapper {
//    获取当前页信息
    @Select("select* from book_info where status!=0 " +
            "order by id desc limit #{offset}, #{pageSize}")
    List<BookInfo> selectBookInfoByPage(@Param("offset") Integer offset, @Param("pageSize") Integer pageSize);

//    获取总记录数
    @Select("select count(1) from book_info where status !=0")
    Integer count();
}

3.3 前端页面

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){
                        var finalHtml="";
                        // 加载列表
                        // 根据每一条记录拼接HTML,即一个<tr>
                        for(var book of result.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>';
                            finalHtml+=' <td>';
                            finalHtml+=' <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) {
                                console.log("第"+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>

3.4 单元测试

由于设有默认值,不传参数进行测试:

也可传参进行测试

通过前端页面进行测试:

相关推荐
小钊(求职中)3 小时前
Java开发实习面试笔试题(含答案)
java·开发语言·spring boot·spring·面试·tomcat·maven
嘵奇5 小时前
MyBatis 中 SqlMapConfig 配置文件详解
mybatis
拾忆,想起8 小时前
Spring 和 Spring MVC 的关系是什么?
java·spring boot·spring·spring cloud·微服务
best_virtuoso9 小时前
Mybatis MyBatis框架的缓存 一级缓存
spring·缓存·mybatis
宇神城主_蒋浩宇9 小时前
大道至简 少字全意 易经的方式看 缓存 mybatis缓存 rendis缓存场景 案例
java·缓存·mybatis
摇滚侠10 小时前
NCHAR_CS和CHAR_CS,导致UNION ALL 时,提示SQL 错误 [12704] [72000]: ORA-12704: 字符集不匹配
数据库·sql·oracle
two_rain10 小时前
Oracle Rac 多路径链路不稳定引发IO降速-光弱
数据库·oracle
逸狼11 小时前
【JavaEE进阶】Spring MVC(2)
spring·java-ee·mvc
_周游11 小时前
【Spring+MyBatis】_图书管理系统(下篇)
mybatis