当前位置: 首页 > article >正文

项目实训:表白墙,图书管理系统

     1. 表白墙

         之前我们的表白墙里面的信息是存在我们的内存里面,如果我们重启一下数据就没有了,此时,我们需要完成的就是把数据进行持久化

        1> 加依赖

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>3.0.4</version>
        </dependency>
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>

        2> 在yml文件配置数据库

        我们先创建数据库

        这里注意一下  `update_time` DATETIME DEFAULT now() ON UPDATE now(),是表示在更新语句进行的时候会自动更改创建时间,并且创建时间设置为更新时间

DROP TABLE IF EXISTS message_info;
CREATE TABLE `message_info` (
        `id` INT ( 11 ) NOT NULL AUTO_INCREMENT,
        `from` VARCHAR ( 127 ) NOT NULL,
        `to` VARCHAR ( 127 ) NOT NULL,
        `message` VARCHAR ( 256 ) NOT NULL,
        `delete_flag` TINYINT ( 4 ) DEFAULT 0 COMMENT '0-正常, 1-删除',
        `create_time` DATETIME DEFAULT now(),
        `update_time` DATETIME DEFAULT now() ON UPDATE now(),
PRIMARY KEY ( `id` ) 
) ENGINE = INNODB DEFAULT CHARSET = utf8mb4;

        配置yml

spring:
  application:
    name=springMVC_tets
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/mybatis_test?characterEncoding=utf8&useSSL=false
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
  configuration: # 配置打印 MyBatis⽇志
  map-underscore-to-camel-case: true #配置驼峰⾃动转换

        3> 编写后端代码

        完善实体类(model)

        编写mapper

        1> 定义接口
        2> 具体实现

        

        注意一下,单引号的加入是看这个单词是不是关键字,不是关键字可以不加

        进行测试

         3> 连接contorller和service

         运行结果,我们发现我们的say是个null值

        

        也就是我们实体类的属性和数据库的字段对应不上

         解决方式:

        1> 修改say为message

        2> 修改映射关系  

2.图书管理系统

        数据库表设计(开发之前做的事情)

        在完善我们图书管理系统之前,我们先学习一下数据库表的设计

        数据库表一般分为: 实体表(刚刚的图书用户和图书,这个实体里面有多少个名词)和关系表(实体之间的关系,几对几的关系)

        然后我们正式进入到图书管理系统的完善

        准备工作: 

        1> 配置pom文件,添加依赖

       <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>3.0.4</version>
        </dependency>
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>

            2> 在yml文件配置数据库

        我们先创建实体表: 用户表和图书表

-- 创建数据库
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, '民主与建设出版社');

        然后我们配置yml 

spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/book_test?characterEncoding=utf8&useSSL=false
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
  configuration: # 配置打印 MyBatis⽇志
    map-underscore-to-camel-case: true #配置驼峰⾃动转换
# 设置日志文件
logging:
  file:
    name: spring-book.log

        3> 编写后端代码

        创建model

           定义接口

        实现接口并且测试

        登录接口

        后端代码

        service层: 主要就是对数据进行加工处理,也就是对DAO 层或 Repository里面的数据进行处理

        

          controller层:主要负责接收前端请求并返回响应

        mapper: 也称为 DAO 层或 Repository 层,是数据持久层的组件。Mapper 层的主要作用是访问数据库,执行数据的增删改查操作。它通常包含一些基本的 SQL 语句或使用 ORM 框架提供的 API 来执行数据库操作。

        

运行结果:

        

        前端代码

<!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(rs){//成功的话就接收后端的请求
                        if(rs == ""){
                            //密码正确
                            location.href="book_list.html"
                        }else{
                            alert(rs);
                        }
                }
            })
        }
    </script>
</body>

</html>

        我们直接进行测试

        登录成功后出现的页面

        添加图书接口

        接口的定义: 

[ 请求]

/book/addBook

Content-Type: application/x-www-form-urlencoded; charset=UTF-8 1 2 3 4

[参数]

bookName=图书1&author=作者1&count=23&price=36&publish=出版社1&status=1

[响应]

"" //失败信息, 成功时返回空字符串   

        后端代码:

        mapper:

        controller:

       service:

        进行测试:

        前端代码:

        这里我们需要传递的参数太多了,因此我们使用序列化函数$().serialize()

        这里主意一个点:我们前端里面,id表示前端样式(或者js使用)name表示的是提交的后端参数名称.

        我们修改一下name值看看报错:

        运行结果:

     分页接口

        接口定义:   

        站在后端的角度: 当前页的数据(后端可以提供什么信息,返回给前端)

        站在前端的角度: 当前页的数据,总记录数(前端页面展示时,需要什么信息)

        url: /book/getBookListByPage

        参数: pageNum

        返回结果: list<>,count

        后端代码:                

        我们进行分页操作,先使用sql来进解释:

         假设我们一页有 10条记录

        第一页: select * from book_info limit 0,10;(我们从第0条数据开始往后数10条数据)

        第二页: select * from book_info limit 10,10;(我们从第11条数据开始往后数10条数据)

        第一个数字是起始位置offset,第二个数字是limit,表示我们取多少条数据.pageSize表示每页条数,page表示第几页

        offset = (page  -1)*pageSize;limit = pageSIze

        为了计算offset和limit,我们需要获取到pageNum和pageSize(这俩这个需要调用方告知)

         mapper:我拼接上述的sql语句需要参数: offset,limt.

        里面的count(1)表示我们计算有多少行数据.SELECT COUNT(1):这里的 COUNT(1) 意味着对表中的每一行返回一个数字 1,然后 COUNT 函数会对这些 1 进行计数,实际上就是统计行数。使用 COUNT(*) 或 COUNT(1) 都可以达到相同的目的,即计算表中的总行数。

        controller: 从前端获取offset

        service:调用mapper.getBookListBypage这个方法获取总的记录数和多少页,然后返回一个PageRequest对象

        moodel: 我们进行翻页需要传的参数我们搞成一个整体也就是PageRequest.而PageResult也就是返回结果,我们使用泛型就可以封装起来,提高代码的复用性.

                其中我们把图书的状态信息设置成了一个枚举类(提高代码的复用性)

        枚举的命名规范: 枚举项用大写显示,多个单词使用_进行分割.

        getDescByCode有以下几种实现方案

        1> 返回desc

        2> 返回枚举项  

        3> 写一个static代码块,把枚举信息存储在map中,get方法根据map的Key取查找(性能更高)

             三层架构的再次理解:

        controller:进行参数的校验,结果的返回,响应结果格式的转换

        service: 获取数据,调用mapper里面的方法进行业务处理

        mapper: 查询数据库

           前端代码:

        我们先介绍一个分页插件:jqPaginator分页组件

        直接引入下面代码即可使用:

$('#id').jqPaginator({
    totalPages: 100,
    visiblePages: 10,
    currentPage: 1,
    onPageChange: function (num, type) {
        $('#text').html('当前第' + num + '页');
    }
});

        我们使用一下:

                我们使用location.search来从页面上获取pageNum(多少页),我们用户点击到第几页就算第几页    

        图书列表显示,点击的时候依次跳转

        运行结果:

        修改图书接口 

        接口定义:

        1> 获取图书的信息,显示到页面上

        根据图书ID, 获取当前图书的信息

   

        2>  图书修改

        针对 一些废弃的接口,如果是新项目,可以直接删除,如果已经上线了,下线某个接口,要写文档,删除前需要通过监控等工具,确保没有调用方在使用了.如果有调用方在使用,需要告知调用方新的接口是什么,对于废弃的接口,我们可以使用@Deprecated注解标识一下

        获取图书信息

        代码:

        controller:

        mapper:

        service

        运行结果:

     更新图书

        后端代码:

        service:

        controller:

        

        mapper: 

        此刻拼接sql我们采用xml形式来写

        先配置yml文件:

spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/book_test?characterEncoding=utf8&useSSL=false
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
  configuration: # 配置打印 MyBatis⽇志
    map-underscore-to-camel-case: true #配置驼峰⾃动转换
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #打印sql语句
  mapper-locations: classpath:mapper/*Mapper.xml
# 设置日志文件
logging:
  file:
    name: spring-book.log

       然后我们通过xml来实现接口

        运行结果:

        前端代码:

        我们要从url中获取bookId,可以有以下方式

        对前端进行赋值 

        运行结果: 我们发现我们成功把值赋进去了

         更新新操作

运行结果:

删除图书接口

        删除一般分为俩种: 逻辑删除,物理删除

        逻辑删除: update book_info set status = 0 where id =1

        物理删除: delete from book_info where id = 25

        在企业上一般会有删除+归档的操作,sql的写法: SQL INSERT INTO SELECT 语句 | 菜鸟教程

        

        设计接口

        逻辑删除,我们只是把状态进行修改了,并没有真正的删除这个数据

          

        后端代码

        controller:

service:

mapper:

        前端代码

\

        批量删除接口

        后端代码:

        controller:

        service:

        mapper:

        

        xml实现接口:

        运行结果:

        前端代码:

        

        运行结果:

        

         强制登录接口

        虽然我们做了用户登录, 但是我们发现, 用户不登录, 依然可以操作图书. 这是有极⼤⻛险的. 所以我们需要进⾏强制登录.如果用户未登录就访问图书列表或者添加图书等页面, 强制跳转到登录⻚⾯.

        我们先复习一下session和cookie

        会话: 在一段事件内我和这个系统进行交流(通过在浏览器点击应用相关的功能)也就是客户端和服务器进行的交流.而cookie和session就是一个会话机制.http请求时一个无状态的,为了让它有记忆功能,我们就采用cookie和session(存储机制).客户端用cookie来记忆,服务器用sesson来记. 在登录完之后会把用户信息存在session里面,下一次客户端再来,服务器就晓得它是谁了(根据sessionId)

        host: ip

        domain: url,ip等(标识是哪个机器)

这个就是domain

这个也是domain

在我们登录图书管理系统的时候,cookie就会有信息:sessionID

这是因为我们后端,服务器里面存的session会响应前端它有个setCookie

        

        我们登录之后,session里面存了值,我们就可以去拿这个信息,如果发现userName和password都有值,那么这个用户就是登录了的.就可以进行更新,删除等操作.如果拿到的session值是空的,就说明没有登录,让用户跳转到登录界面.此时我们需要一个业务码进行判断

  http状态码和业务码的区别

        具体的流程

        

        后端代码

        model: Result

        PageResult

        

        枚举类: 我们的业务码设置为枚举类

        像sessionID这种会变的,我们设置为一个常量,常量的规范写法: 单词大写,单词与单词之间用下划线连接 

        判断session里面的sessionId获取Session信息,判断是否用户登录  

        我们可以进行进一步的封装:

        直接调用

        运行结果

        

        拿到session

        一般HttpSession session: 从Cookie中获取SessionID,根据SessionID获取Session信息,如果有Session获取session.没有session就创建session

        有时候报错可能是缓存问题

        前端清除缓存: ctrl+shift+delete 

        后端清除缓存: maven点clean

        前端代码

        小总结:

        我们在获取图书列表上增加了强制登录,我们也要分别在增加图书,删除图书上也增加强制登录,因此这个是个重复操作,我们此时就要用到统一功能的拦截器.因为篇幅比较长,我重新开一个博客继续介绍 


http://www.kler.cn/a/536673.html

相关文章:

  • 解决 ssh: connect to host github.com port 22: Connection timed out
  • SQL条件分支中的大讲究
  • MySQL----case的用法
  • 从零开始:OpenCV 图像处理快速入门教程
  • 2025.2.6
  • 4. Go结构体使用
  • windows 10/11 开启wsl2运行linux 使用cuda方法
  • Flink CDC YAML:面向数据集成的 API 设计
  • Excel 融合 deepseek
  • JumpServer堡垒机管理服务器与数据库资产
  • 【论文阅读】Adversarial Detection: Attacking Object Detection in Real Time
  • 前端 CSS 动态设置样式::class、:style 等技巧详解
  • 基于WOA鲸鱼优化的TCN时间卷积神经网络时间序列预测算法matlab仿真
  • 【玩转 Postman 接口测试与开发2_019】第15章:利用 Postman 初探 API 性能测试(含实战截图)
  • FFmpeg使用GPU编解码,及在C++代码中实现FFmpeg使用GPU编解码
  • C# LINQ与集合类 数据操作
  • postgresql-15(yum安装教程)
  • 让文物“活”起来,以3D数字化技术传承文物历史文化!
  • [RabbitMQ] 常见面试题汇总 工作流程 消息可靠性 消息顺序性 幂等性 高级特性 延迟队列 仲裁队列 工作模式 消息积压 推拉模式
  • easyxor
  • 赛博算命之 ”梅花易数“ 的 “JAVA“ 实现 ——从玄学到科学的探索
  • element-ui rate 组件源码分享
  • zsh: command not found: pip
  • Android Studio:键值对存储sharedPreferences
  • unity视频在场景中的使用
  • APP广告变现如何优化广告填充率,提升变现收益?