【JavaEE】MyBatis 综合练习(图书管理系统)
目录
- 一、数据库表
- 二、引入依赖:
- 三、Model创建
- 四、用户登录
- 五、添加图书
- 六、图书列表
- 七、修改图书
- 八、删除图书
- 九、批量删除
- 十、强制登录

图书管理系统
一、数据库表
我们使用两张表,一张用户表uset_test来记录登录的用户信息,一张图书表book_test记录图书信息。
-- 创建数据库
DROP DATABASE IF EXISTS book_test;
CREATE DATABASE book_test DEFAULT CHARACTER SET utf8mb4;use book_test;-- 用户表
DROP TABLE IF EXISTS user_info;
CREATE TABLE user_info (`id` INT NOT NULL AUTO_INCREMENT,`user_name` VARCHAR ( 128 ) NOT NULL,`password` VARCHAR ( 128 ) NOT NULL,`delete_flag` TINYINT ( 4 ) NULL DEFAULT 0,`create_time` DATETIME DEFAULT now(),`update_time` DATETIME DEFAULT now() ON UPDATE now(),
PRIMARY KEY ( `id` ),
UNIQUE INDEX `user_name_UNIQUE` ( `user_name` ASC )) ENGINE = INNODB DEFAULT
CHARACTER
SET = utf8mb4 COMMENT = '用户表';-- 图书表
DROP TABLE IF EXISTS book_info;
CREATE TABLE `book_info` (`id` INT ( 11 ) NOT NULL AUTO_INCREMENT,`book_name` VARCHAR ( 127 ) NOT NULL,`author` VARCHAR ( 127 ) NOT NULL,`count` INT ( 11 ) NOT NULL,`price` DECIMAL (7,2 ) NOT NULL,`publish` VARCHAR ( 256 ) NOT NULL,`status` TINYINT ( 4 ) DEFAULT 1 COMMENT '0-无效, 1-正常, 2-不允许借阅',`create_time` DATETIME DEFAULT now(),`update_time` DATETIME DEFAULT now() ON UPDATE now(),
PRIMARY KEY ( `id` )
) ENGINE = INNODB DEFAULT CHARSET = utf8mb4;
-- 初始化数据
INSERT INTO user_info ( user_name, PASSWORD ) VALUES ( "admin", "admin" );
INSERT INTO user_info ( user_name, PASSWORD ) VALUES ( "zhangsan", "123456" );
-- 初始化图书数据
INSERT INTO `book_info` (book_name,author,count, price, publish)
VALUES ('活着', '余华', 29, 22.00, '北京文艺出版社');
INSERT INTO `book_info` (book_name,author,count, price, publish)
VALUES ('平凡的世界', '路遥', 5, 98.56, '北京十月文艺出版社');
INSERT INTO `book_info` (book_name,author,count, price, publish)
VALUES ('三体', '刘慈欣', 9, 102.67, '重庆出版社');
INSERT INTO `book_info` (book_name,author,count, price, publish)
VALUES ('金字塔原理', '麦肯锡', 16, 178.00, '民主与建设出版社');
二、引入依赖:
引入MyBatis配置:
记得修改为自己的数据库名和密码。
spring:application:name: librarydatasource:url: jdbc:mysql://127.0.0.1:3306/book_test?characterEncoding=utf8&useSSL=falseusername: rootpassword: 1234driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:configuration:map-underscore-to-camel-case: true #配置驼峰自动转换log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #打印sql语句mapper-locations: classpath:dao/*.xml
三、Model创建
在model包路径下,创建用户表和图书表对应的类。
用户类:
package com.example.library.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;
}
图书类:
package com.example.library.model;import lombok.Data;import java.math.BigDecimal;
import java.util.Date;@Data
public class BookInfo {//图书IDprivate Integer id;//书名private String bookName;//作者private String author;//数量private Integer count;//定价private BigDecimal price;//出版社private String publish;//状态 0-⽆效 1-允许借阅 2-不允许借阅private Integer status;private String statusCN;//创建时间private Date createTime;//更新时间private Date updateTime;}
四、用户登录
前后端交互约定:
[请求]
/user/login
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
[参数]
name=zhangsan&password=123456
[响应]
true //账号密码验证正确, 否则返回false
controller层代码:
- 在去数据库查询之前,我们要先校验穿的参数是否合法,不为空
- 数据库查询后,要看的到的是不是空值,防止空指针异常
- 最后校验
package com.example.library.controller;import com.example.library.model.UserInfo;
import com.example.library.service.UserInfoService;
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 LoginController {@Autowiredprivate UserInfoService userInfoService;@RequestMapping("/login")public Boolean login(String userName, String password, HttpSession session) {//校验参数if(!StringUtils.hasLength(userName)|| !StringUtils.hasLength(password) ) {return false;}//查询用户UserInfo userInfo = userInfoService.selectByName(userName);if(null == userInfo) return false;if(userInfo.getPassword().equals(password)) {session.setAttribute("userName",userName);return true;}return false;}
}
Service层代码:
- 这一层代码只需要简单调用数据持久层代码即可。
package com.example.library.service;import com.example.library.dao.UserInfoDao;
import com.example.library.model.UserInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class UserInfoService {@Autowiredprivate UserInfoDao userInfoDao;public UserInfo selectByName(String userName) {return userInfoDao.selectByName(userName);}
}
dao层:
- 简单根据名字条件查询一下,因为数据量小直接使用的 * 。
package com.example.library.dao;import com.example.library.model.UserInfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;@Mapper
public interface UserInfoDao {@Select("select * from user_info where user_name = #{userName}")UserInfo selectByName(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({type: "post",url: "/user/login",data: {userName: $("#userName").val(),password: $("#password").val()},success: function(result){if(result === true){//账号密码正确location.href = "book_list.html?currentPage=1";}else {alert("账号或密码错误");}}});// location.href = "book_list.html";}
</script>
</body></html>
五、添加图书
前后端接口:
[请求]
/book/addBook
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
[参数]
bookName=图书1&author=作者1&count=23&price=36&publish=出版社1&status=1
[响应]
“” //失败信息, 成功时返回空字符串
controller层代码:
- 我们在前端传给我们对象后,要根据我们设定的数据库的参数类型及条件,进行相应的判空以及超出条件。
- 在添加图书前我们打印日志,出现异常时也打印日志。
package com.example.library.controller;import com.example.library.model.BookInfo;
import com.example.library.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.RestController;import java.math.BigDecimal;
import java.util.List;
@Slf4j
@RequestMapping("/book")
@RestController
@Component
public class BookController {@Autowiredprivate BookService bookService;@RequestMapping("/getList")public List<BookInfo> getList() {return bookService.getList();}@RequestMapping("/addBook")public String addBook(BookInfo bookInfo) {log.info("添加图书: " + bookInfo);if(null == bookInfo) return "不能为空";if(!StringUtils.hasLength(bookInfo.getBookName()) || bookInfo.getBookName().length() > 127) return "图书名不合规";if(!StringUtils.hasLength(bookInfo.getAuthor()) || bookInfo.getAuthor().length() > 127) return "图书作者不合规";if(null == bookInfo.getCount() || bookInfo.getCount() < 0) return "余量不合规";if(null == bookInfo.getPrice() || bookInfo.getPrice().compareTo(new BigDecimal(0)) <= 0) return "价格不合规";if(!StringUtils.hasLength(bookInfo.getPublish()) || bookInfo.getPublish().length() > 127) return "出版社不合规";if(null == bookInfo.getStatus()|| (0 != bookInfo.getStatus().compareTo(1) && 0 != bookInfo.getStatus().compareTo(2))) return "状态不合规";try {bookService.addBook(bookInfo);return "";} catch (Exception e) {log.error("添加图书错误: " + e);return "添加图书错误";}}
}
Service层:
- 直接简单调用dao层代码即可。
package com.example.library.service;import com.example.library.dao.BookDao;
import com.example.library.model.BookInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;import java.util.ArrayList;
import java.util.List;@Service
public class BookService {@AutowiredBookDao bookDao ;//插入一行图书public void addBook(BookInfo bookInfo) {bookDao.addBook(bookInfo);}}
dao层:
- 根据条件插入即可。
package com.example.library.dao;import com.example.library.model.BookInfo;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Service;import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;@Mapper
public interface BookDao {@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"><pathd="M8.5 2.687c.654-.689 1.782-.886 3.112-.752 1.234.124 2.503.523 3.388.893v9.923c-.918-.35-2.107-.692-3.287-.81-1.094-.111-2.278-.039-3.213.492V2.687zM8 1.783C7.015.936 5.587.81 4.287.94c-1.514.153-3.042.672-3.994 1.105A.5.5 0 0 0 0 2.5v11a.5.5 0 0 0 .707.455c.882-.4 2.303-.881 3.68-1.02 1.409-.142 2.59.087 3.223.877a.5.5 0 0 0 .78 0c.633-.79 1.814-1.019 3.222-.877 1.378.139 2.8.62 3.681 1.02A.5.5 0 0 0 16 13.5v-11a.5.5 0 0 0-.293-.455c-.952-.433-2.48-.952-3.994-1.105C10.413.809 8.985.936 8 1.783z" /></svg><span>添加图书</span></h2></div><form id="addBook"><div class="form-group"><label for="bookName">图书名称:</label><input type="text" class="form-control" placeholder="请输入图书名称" id="bookName" name="bookName"></div><div class="form-group"><label for="bookAuthor">图书作者</label><input type="text" class="form-control" placeholder="请输入图书作者" id="bookAuthor" name="author" /></div><div class="form-group"><label for="bookStock">图书库存</label><input type="text" class="form-control" placeholder="请输入图书库存" id="bookStock" name="count"/></div><div class="form-group"><label for="bookPrice">图书定价:</label><input type="number" class="form-control" placeholder="请输入价格" id="bookPrice" name="price"></div><div class="form-group"><label for="bookPublisher">出版社</label><input type="text" id="bookPublisher" class="form-control" placeholder="请输入图书出版社" name="publish" /></div><div class="form-group"><label for="bookStatus">图书状态</label><select class="custom-select" id="bookStatus" name="status"><option value="1" selected>可借阅</option><option value="2">不可借阅</option></select></div><div class="form-group" style="text-align: right"><button type="button" class="btn btn-info btn-lg" onclick="add()">确定</button><button type="button" class="btn btn-secondary btn-lg" onclick="javascript:history.back()">返回</button></div></form>
</div>
<script type="text/javascript" src="js/jquery.min.js"></script>
<script>function add() {//前端应该进行参数校验, 此处省略//提交请求到后端$.ajax({type: "post",url: "/book/addBook",data: $("#addBook").serialize(),success: function(result){if(result== ""){//添加图书成功location.href = "book_list.html";}else {//失败alert(result);}}});}
</script>
</body></html>
六、图书列表
前后端接口:
[请求]
/book/getListByPage?currentPage=1&pageSize=10
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
[参数]
[响应]
Content-Type: application/json
{"total": 25,"records": [{"id": 25,"bookName": "图书21","author": "作者2","count": 29,"price": 22.00,"publish": "出版社1","status": 1,"statusCN": "可借阅"}, {......} ]
}
由于我们要使用分页查询,所以我们要将每一次查询的页数和每页的数据数,都要当成参数传给后端。
后端返回的是当前的图书总数量,和当前的页下的图书。
所以我们将请求和响应都封装起来,在响应中我们把请求也作为成员,以便前端拿取参数。
响应:
package com.example.library.model;import lombok.Data;import java.util.List;@Data
public class PageResult <T>{private Integer total;private List<T> records;private PageRequest pageRequest;
}
请求:
- 我们在使用limit setoff传参的时候,第一个传的数字是当前页开始的前一个在的位置,所以我们直接在这个类中进行计算,避免在数据持久层进行计算。
package com.example.library.model;import lombok.Data;@Data
public class PageRequest {private int currentPage = 1;private int pageSize = 10;private int offset;public Integer getOffset() {return pageSize * (currentPage-1);}
}
controller层代码:
@RequestMapping("/getListByPage")public PageResult<BookInfo> getListByPage(PageRequest pageRequest) {//参数校验if(null == pageRequest) return null;if(pageRequest.getPageSize() <= 0 || pageRequest.getCurrentPage() <= 0) return null;PageResult<BookInfo> request = bookService.getListByPage(pageRequest);return request;}
Service层代码:
因为我们要根据图书的状态,反映图书可不可借阅的属性,由于是一一对应的关系,所以我们使用枚举类封装一下。
枚举类:
- lombok的@Data注解是不能对枚举类使用的。
package com.example.library.enums;public enum BookStatusEnum {DELETE(0 , "无效"),NORMAL(1,"可借阅"),FORBIDDEN(2,"不可借阅");public static String getStatusByCode(int code) {switch (code) {case 1: return NORMAL.getDesc();case 2: return FORBIDDEN.getDesc();case 0: return DELETE.getDesc();default: return null;}}private int code;private String desc;BookStatusEnum(int code, String desc) {this.code = code;this.desc = desc;}public int getCode() {return code;}public void setCode(int code) {this.code = code;}BookStatusEnum() {}public String getDesc() {return desc;}public void setDesc(String desc) {this.desc = desc;}
}
- 调用dao层的方法,以及对状态映射即可。
public PageResult<BookInfo> getListByPage(PageRequest pageRequest) {List<BookInfo> bookInfos = bookDao.getListByPage(pageRequest);//对状态进行映射for (BookInfo x:bookInfos) {x.setStatusCN(BookStatusEnum.getStatusByCode(x.getStatus()));}PageResult<BookInfo> result = new PageResult<>();//图书总数result.setTotal(bookDao.count());//当前页图书result.setRecords(bookInfos);//请求,以便前端拿取参数result.setPageRequest(pageRequest);return result;}
dao层:
因为返回的响应中,既有当前页面的图书数据,又有总的图书数,那么我们需要两个查询函数来实现。
@Select("select * from book_info where status != 0 limit #{offset}, #{pageSize}")List<BookInfo> getListByPage(PageRequest pageRequest);@Select("select count(1) from book_info where status != 0")int count();
前端代码:
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({type: "get",url: "/book/getListByPage"+location.search ,success: function(result){if(result == null || result.records == null || result.pageRequest == null) return;var books = result.records;var finalHtml = "";for(var book of books){finalHtml += '<tr>';finalHtml += '<td><input type="checkbox" name="selectBook" value="'+book.bookId+'" 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><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>';}$("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) {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) {//获取复选框的idvar ids = [];$("input:checkbox[name='selectBook']:checked").each(function () {ids.push($(this).val());});console.log(ids);alert("批量删除成功");}}</script>
</div>
</body></html>
七、修改图书
接口定义:
进⼊修改⻚⾯, 需要显⽰当前图书的信息:
[请求]
/book/queryBookById?bookId=25
[参数]
⽆
[响应]
{"id": 25,"bookName": "图书21","author": "作者2","count": 999,"price": 222.00,"publish": "出版社1","status": 2,"statusCN": null,"createTime": "2023-09-04T04:01:27.000+00:00","updateTime": "2023-09-05T03:37:03.000+00:00"
}
controller层:
@RequestMapping("queryBookById")public BookInfo queryBookById(Integer bookId) {log.info("查询图书信息,图书id: "+bookId);return bookService.queryBookById(bookId);}
Service层:
public BookInfo queryBookById(Integer bookId) {return bookDao.queryBookById(bookId);}
dao层:
@Select("select * from book_info where status != 0 and id = #{bookId}")BookInfo queryBookById(Integer bookId);
点击修改按钮,修改图书信息,接口定义:
[请求]
/book/updateBook
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
[参数]
id=1&bookName=图书1&author=作者1&count=23&price=36&publish=出版社1&status=1
[响应]
“” //失败信息, 成功时返回空字符串
controller层:
- 参数可以为空,但是要满足数据库字段的定义
@RequestMapping("updateBook")public String updateBook(BookInfo bookInfo) {log.info("修改图书: " + bookInfo);//校验参数,参数可以为空if(null == bookInfo) return "不能为空";if(StringUtils.hasLength(bookInfo.getBookName()) && bookInfo.getBookName().length() > 127) return "图书名不合规";if(StringUtils.hasLength(bookInfo.getAuthor()) && bookInfo.getAuthor().length() > 127) return "图书作者不合规";if(null != bookInfo.getCount() && bookInfo.getCount() < 0) return "余量不合规";if(null != bookInfo.getPrice() && bookInfo.getPrice().compareTo(new BigDecimal(0)) <= 0) return "价格不合规";if(StringUtils.hasLength(bookInfo.getPublish()) && bookInfo.getPublish().length() > 127) return "出版社不合规";if(null != bookInfo.getStatus()&& (0 != bookInfo.getStatus().compareTo(1) && 0 != bookInfo.getStatus().compareTo(2))) return "状态不合规";try {bookService.updateBook(bookInfo);return "";}catch (Exception e) {log.error("修改图书错误:"+e);return "修改图书错误";}}
servic层:
public void updateBook(BookInfo bookInfo) {bookDao.updateBook(bookInfo);}
dao层:
- 因为我们传的参数是可以为空的,那么我们要是有动态sql来写,xml文件更适合。
Integer updateBook(BookInfo bookInfo);
xml文件:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace = "com.example.library.dao.BookDao"><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>
前端代码:
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"><pathd="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>getBookInfo();function getBookInfo(){$.ajax({type: "get",url: "/book/queryBookById"+ location.search,success: function(bookInfo){if(bookInfo !=null ){$("#bookId").val(bookInfo.id);$("#bookName").val(bookInfo.bookName);$("#bookAuthor").val(bookInfo.author);$("#bookStock").val(bookInfo.count);$("#bookPrice").val(bookInfo.price);$("#bookPublisher").val(bookInfo.publish);$("#bookStatus").val(bookInfo.status);}}});}function update() {$.ajax({type: "post",url: "/book/updateBook",data: $("#updateBook").serialize(),success: function(result){if(result === ""){location.href = "book_list.html?currentPage=1";}else {alert(result);}}});}
</script>
</body></html>
八、删除图书
删除操作一般不是真删除,而是将表示删除的状态变成删除,在客户端不显示出来。
接口定义:
[请求]
/book/deleteBook
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
[参数]
id=1&status=0
[响应]
“” //失败信息, 成功时返回空字符串
我们调用直接调用修改的代码即可,只需要封装一下接口。
controller层:
@RequestMapping("deleteBook")public String deleteBook(Integer bookId) {log.info("删除图书: " + bookId);BookInfo bookInfo = new BookInfo();bookInfo.setId(bookId);bookInfo.setStatusCN(BookStatusEnum.getStatusByCode(0));try {bookService.updateBook(bookInfo);return "";}catch (Exception e) {log.error("修改图书错误:"+e);return "修改图书错误";}}
九、批量删除
接口定义:
[请求]
/book/batchDeleteBook
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
[参数]
[响应]
“” //失败信息, 成功时返回空字符串
controller层:
@RequestMapping("/batchDeleteBook")public Boolean batchDeleteBook(Integer[] ids) {log.info("批量删除图书:bookIds: {}", ids);try {bookService.batchDeleteBook(ids);return true;}catch (Exception e) {log.error("删除图书错误" + e);return false;}}
Service层:
public void batchDeleteBook(Integer[] ids) {bookDao.batchDeleteBook(ids);}
dao层:
Integer batchDeleteBook(Integer[] ids);
xml文件:
<update id="batchDeleteBook">update book_info set status = 0 where id in<foreach collection="ids" item="bookId" open="(" close=")" separator=",">#{bookId}</foreach></update>
前端方法:
function batchDelete() {var isDelete = confirm("确认批量删除?");if (isDelete) {//获取复选框的idvar 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"}}});}}
十、强制登录
⽤⼾登录时, 我们已经把登录⽤⼾的信息存储在了Session中. 那就可以通过Session中的信息来判断⽤
⼾都是登录.
- 如果Session中可以取到登录⽤⼾的信息, 说明⽤⼾已经登录了, 可以进⾏后续操作
- 如果Session中取不到登录⽤⼾的信息, 说明⽤⼾未登录, 则跳转到登录⻚⾯.
我们的报错信息是很多的,我们单独使用null或者空对象是没法区分到底是什么原因造成的。所以我们将这个信息单独封装成类。
- 因为我们的状态与错误信息是一一对应的,所以封装成枚举类,
- 报错信息,在很多地方都要使用,所以我们提供方法,以便拿到
package com.example.library.model;import com.example.library.enums.ResultStatusEnum;
import lombok.Data;@Data
public class Result <T>{private ResultStatusEnum code;private String errMsg;private T data;/*** 业务执⾏成功时返回的⽅法** @param data* @return*/public static <T> Result success(T data) {Result result = new Result();result.setCode(ResultStatusEnum.SUCCESS);result.setErrMsg("");result.setData(data);return result;}/**** * 用户未登录* ** * @param data* * @return*/public static Result unLogin () {Result result = new Result();result.setCode(ResultStatusEnum.UNLOGIN);result.setErrMsg("用户未登录");result.setData(null);return result;}/**** * 出错* ** * @param String errMsg* * @return*/public static <T> Result fail (String errMsg) {Result result = new Result();result.setCode(ResultStatusEnum.FAIL);result.setErrMsg(errMsg);return result;}
}
枚举类:
package com.example.library.enums;public enum ResultStatusEnum {SUCCESS(200),UNLOGIN(-1),FAIL(-2);private int code;public int getCode() {return code;}public void setCode(int code) {this.code = code;}ResultStatusEnum(int code) {this.code = code;}
}
controller:
- 因为我们获取session时的参数,在很多地方都要使用,所以我们封装起来,以便后续更改,只需更改一处即可。
@RequestMapping("/getListByPage")public Result getListByPage(PageRequest pageRequest, HttpSession httpSession) {//登录验证if(null == httpSession.getAttribute(Constants.SESSION_USER_KEY)) return Result.unLogin();UserInfo userInfo = (UserInfo) httpSession.getAttribute(Constants.SESSION_USER_KEY);//用户未登录if(null == userInfo || 0 >= userInfo.getId()) {return Result.unLogin();}//参数校验if(null == pageRequest) return Result.fail("参数错误");if(pageRequest.getPageSize() <= 0 || pageRequest.getCurrentPage() <= 0) return Result.fail("参数错误");PageResult<BookInfo> request = bookService.getListByPage(pageRequest);return Result.success(request);}
package com.example.library.constant;public class Constants {public static final String SESSION_USER_KEY = "session_user_info";
}
由于获取session的参数变了,那么我们在登录的方法也要相应修改。
@RequestMapping("/login")public Boolean login(String userName, String password, HttpSession session) {//校验参数if(!StringUtils.hasLength(userName)|| !StringUtils.hasLength(password) ) {return false;}//查询用户UserInfo userInfo = userInfoService.selectByName(userName);if(null == userInfo) return false;if(userInfo.getPassword().equals(password)) {session.setAttribute(Constants.SESSION_USER_KEY,userInfo);return true;}return false;}
前端的获取图书列表的方法也要变。
getBookList();function getBookList() {$.ajax({type: "get",url: "/book/getListByPage"+location.search ,success: function(result){if(result == null || result.code == "UNLOGIN" ) {alert("用户未登录,请先登录");location.href = "login.html";}if(result == null || result.data == null) return;if(result.code == "FAIL" ) {alert("参数错误");}var data = result.data;var books = data.records;var finalHtml = "";for(var book of books){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><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>';}$("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 == "change") {location.href= "book_list.html?currentPage="+page;}}});}});}
相关文章:
【JavaEE】MyBatis 综合练习(图书管理系统)
目录 一、数据库表二、引入依赖:三、Model创建四、用户登录五、添加图书六、图书列表七、修改图书八、删除图书九、批量删除十、强制登录 图书管理系统 一、数据库表 我们使用两张表,一张用户表uset_test来记录登录的用户信息,一张图书表boo…...
ArkUI —— 组件导航
创建导航页 // src\main\ets\pages\Index.ets Entry Component struct Index {// 路由栈Provide(pathInfos) pathInfos: NavPathStack new NavPathStack()build() {Navigation(this.pathInfos) {}} }创建导航子页 this.navPath.pushPathByName(AccountTag, 账本分类管理)// …...
数据处理与机器学习入门
一、数据处理概述 数据处理是通过统计学、机器学习和数据挖掘方法从原始数据中提取有价值信息的过程。数据处理的目标是将杂乱无章的原始数据转化为可用于分析和建模的结构化数据。对于小规模数据处理,常用工具分为两类: • 可视化分析工具:…...
Markdown在线转word格式
1、打开网址 https://dillinger.io/ 2、输入markdown格式文章 3、直接转换为右边的word格式 4、复制粘贴即可。...
Redis延时队列在订单超时未报到场景的应用分享
一、引言 在电商、医疗预约等众多业务场景中,经常会遇到需要处理超时任务的情况。比如医疗预约订单,如果患者在支付成功后,到了预约结束时间还未报到,系统需要自动取消订单。为了实现这样的功能,我们可以利用 Redis 延…...
vue前端代码作业——待办事项
美化样式示意图: 后端IDEA代码示意图: 代码解释: 1. isAllChecked 计算属性的作用 isAllChecked 用于实现 “全选 / 全不选” 功能,它是一个 双向绑定 的计算属性(因为 v-model 需要同时支持读取和设置值)…...
docker镜像拉取失败
hub.docker.com中提供的docker pull命令在服务器拉取镜像时报错Error response from daemon: Get https://registry-1.docker.io/v2/: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers) 这个错误通常表明Docker客户…...
Ruby 简介
Ruby 简介 引言 Ruby 是一种广泛使用的动态、开源的编程语言,自 1995 年由日本程序员 Yukihiro Matsumoto(通称 Matz)设计以来,它以其优雅的语法、强大的库支持和跨平台特性赢得了全球开发者的青睐。本文将详细介绍 Ruby 的起源、特点、应用领域以及它在现代软件开发中的…...
解决 FFmpeg 使用 C/C++ 接口时,解码没有 shell 快的问题(使用多线程)
一、问题 硬件设备为香橙派 5Plus,最近需要使用硬件视频解码来加速 YOLO 的检测,shell 窗口的FFmpeg已经调通,详见文章: 编译支持 RKmpp 和 RGA 的 ffmpeg 源码_rk3588 ffmpeg mpp-CSDN博客https://blog.csdn.net/plmm__/article…...
sqlalchemy:将mysql切换到OpenGauss
说明 之前python的项目使用的mysql,近期要切换到国产数据库OpenGauss。 之前的方案是fastapisqlalchemy,测试下来发现不用改代码,只要改下配置即可。 切换方案 安装openGauss-connector-python-psycopg2 其代码工程在:https:…...
缓存使用纪要
一、本地缓存:Caffeine 1、简介 Caffeine是一种高性能、高命中率、内存占用低的本地缓存库,简单来说它是 Guava Cache 的优化加强版,是当下最流行、最佳(最优)缓存框架。 Spring5 即将放弃掉 Guava Cache 作为缓存机…...
Qt之Service开发
一、概述 基于Qt的用于开发系统服务(守护进程)和后台服务,有以下几个优秀的开源 QtService 框架和库。 1. QtService (官方解决方案) GitHub: https://github.com/qtproject/qt-solutions/tree/master/qtservice 特点: 官方提供的服务框架 支持 Windows 服务和 Linux 守护…...
ssm框架之Spring
Spring框架介绍 Spring框架是一个轻量级的企业级应用框架 通过它可以贯穿表现层、业务层、持久层。集成方便,简单易用,具有如下特点: Spring框架特色 Spring设计理念 是面向Bean的编程 Spring两大核心技术 控制反转(IoC:Inver…...
Flutter 开发环境配置--宇宙级教学!
目录 一、安装环境(Windows)二、Android 创建Flutter项目三、VSCode 搭建环境四、补充 一、安装环境(Windows) Flutter SDK 下载 推荐使用中国镜像站点下载 Flutter SDK,速度更快:中国环境 或者从官网下载…...
音视频 YUV格式详解
前言 本文介绍YUV色彩模型,YUV的分类和常见格式。 RGB色彩模型 在RGB颜色空间中,任意色光F都可以使用R、G、B三色不同的分量混合相加而成即: F = R + G + B.。即我们熟悉的三原色模型。 RGB色彩空间根据每个分量在计算机中占用的存储字节数可以分为以下几种类型,字节数…...
力扣 第 153 场双周赛 讲题
文章目录 Q1.字符串的反转度Q2.操作后最大活跃区段数I3500.将数组分割为子数组的最小代价 Q1.字符串的反转度 签到题,直接建立一个映射表即可 class Solution:def reverseDegree(self, s: str) -> int:# 先建立映射表ss "abcdefghijklmnopqrstuvwxyz"store {}i…...
grafana 配置页面告警
添加告警规则 1.登录grafana 点击 Alerting > Alert rules 点击 New alert rule 2.填写告警规则名字 3.配置告警规则 选择数据源为 Loki 单机 Builder 单机Label brower 单机 node_name 标签,选择一个主机,选好后单机 Show logs 这时候查询语…...
Cent OS7+Docker+Dify
由于我之前安装了Dify v1.0.0,出现了一些问题:无法删除,包括:知识库中的文件、应用、智能体、工作流,都无法删除。现在把服务器初始化,一步步重新安装,从0到有。 目录 1、服务器重装系统和配置…...
【自学笔记】PHP语言基础知识点总览-持续更新
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 1. PHP 简介2. PHP 环境搭建3. 基本语法变量与常量数据类型运算符 4. 控制结构条件语句循环语句 5. 函数函数定义与调用作用域 6. 数组7. 字符串8. 表单处理9. 会话…...
Android Gradle 下载插件或依赖太慢
问题与处理策略 问题描述 Android 项目中,settings.gradle 文件中,有如下配置,Gradle 插件或依赖下载速度慢 pluginManagement {repositories {gradlePluginPortal()google()mavenCentral()} }dependencyResolutionManagement {repositori…...
python-59-基于python内置库解析html获取标签关键信息
文章目录 1 html.parser1.1 初始化和基础使用1.1.1 handle_starttag(self, tag, attrs)1.1.2 handle_endtag(self, tag)1.1.3 handle_startendtag(self, tag, attrs)1.1.4 handle_data(self, data)1.1.5 handle_comment(self, data)1.2 解析HTML文档的流程2 百度搜索关键词链接…...
elementplus的el-tabs路由式
在使用 Element Plus 的 el-tabs 组件,实现路由式的切换(即点击标签页来切换不同的路由页面)。下面是一个基于 Vue 3 和 Element Plus 实现路由式 el-tabs 的基本步骤和示例。 步骤 1: 安装必要的库 在vue3项目安装 Vue Router 和 Element …...
ArcGIS地理信息系统空间分析实验教程学习
ArcGIS 作为地理信息系统领域的经典软件,以其强大的功能和广泛的应用场景,成为了众多学者、研究人员和专业人士的首选工具。它不仅可以高效地处理和可视化地理空间数据,还能通过复杂的空间分析模型,揭示地理现象背后的规律和趋势。…...
mac部署CAT监控服务
在 Mac 上部署美团点评开源的 CAT 监控服务端,可以按照以下步骤操作: 1. 环境准备 1.1 安装依赖 确保已安装以下工具: JDK 8(建议 OpenJDK 11) MySQL 5.7(存储监控数据)(8.0不支持…...
鸿蒙OS 5 架构设计探秘:从分层设计到多端部署
文章目录 鸿蒙OS架构设计探秘:从分层设计到多端部署一、鸿蒙的分层架构设计二、模块化设计的精髓三、智慧分发设计:资源的动态调度四、一次开发,多端部署的实践总结与思考 鸿蒙OS架构设计探秘:从分层设计到多端部署 最近两年来&a…...
深入解析:ElasticSearch Query 查询方式
全文目录: 开篇语前言摘要概述ElasticSearch Query 查询方式详解1. Match 查询(全文搜索)1.1 Match 查询示例1.2 Match 查询参数扩展 2. Term 查询(精准查询)2.1 Term 查询示例2.2 Terms 查询 3. Bool 查询(…...
HTML5贪吃蛇游戏开发经验分享
HTML5贪吃蛇游戏开发经验分享 这里写目录标题 HTML5贪吃蛇游戏开发经验分享项目介绍技术栈核心功能实现1. 游戏初始化2. 蛇的移动控制3. 碰撞检测4. 食物生成 开发心得项目收获后续优化方向结语 项目介绍 在这个项目中,我使用HTML5 Canvas和原生JavaScript实现了一…...
桥接模式_结构型_GOF23
桥接模式 桥接模式(Bridge Pattern)是一种结构型设计模式,核心思想是将抽象与实现分离,使两者能独立变化。它像一座连接两岸的桥梁,让“抽象层”和“实现层”自由组合,避免因多维度变化导致的“类爆炸”问…...
卡尔曼滤波入门(二)
核心思想 卡尔曼滤波的核心就是在不确定中寻找最优,那么怎么定义最优呢?答案是均方误差最小的,便是最优。 卡尔曼滤波本质上是一种动态系统状态估计器,它回答了这样一个问题: 如何从充满噪声的观测数据中,…...
有关pip与conda的介绍
Conda vs. Pip vs. Virtualenv 命令对比 任务Conda 命令Pip 命令Virtualenv 命令安装包conda install $PACKAGE_NAMEpip install $PACKAGE_NAMEX更新包conda update --name $ENVIRONMENT_NAME $PACKAGE_NAMEpip install --upgrade $PACKAGE_NAMEX更新包管理器conda update con…...
【Portainer】Docker可视化组件安装
Portainer Portainer 是用于管理容器化环境的一体化平台工程解决方案,提供广泛的定制功能,以满足个人开发人员和企业团队的需求。 官方地址: https://www.portainer.io/ 安装 在 WSL / Docker Desktop 上使用 Docker 安装 Portainer CE 通过命令或UI页…...
基于深度神经网络的图像防篡改检测方法研究
标题:基于深度神经网络的图像防篡改检测方法研究 内容:1.摘要 随着数字化时代的发展,图像篡改现象日益普遍,严重影响了图像信息的真实性和可靠性。本文旨在研究基于深度神经网络的图像防篡改检测方法,以有效识别被篡改的图像。通过收集大量真…...
MATLAB导入Excel数据
假如Excel中存在三列数据需要导入Matlab中。 保证该Excel文件与Matlab程序在同一目录下。 function [time, voltage, current] test(filename)% 读取Excel文件并提取时间、电压、电流数据% 输入参数:% filename: Excel文件名(需包含路径,如C:\data\…...
华为GaussDB数据库的手动备份与还原操作介绍
数据库的备份以A机上的操作为例。 1、使用linux的root用户登录到GaussDB服务器。 2、用以下命令切换到 GaussDB 管理员用户,其中,omm 为当前数据库的linux账号。 su - omm 3、执行gs_dump命令进行数据库备份: 这里使用gs_dump命令进行备…...
MySQL数据库BUG导致查询不到本该查到的数据
在数据库的日常使用中,我们常常会遇到一些看似匪夷所思的查询问。最近就看到一个因为MySQL BUG导致无法查到本该查询到数据的案例。 1. 问题背 数据库版本:MySQL8.0.40 假设我们创建了一个名为 product_info 的表,用于存储产品的相关信息。该…...
Dubbo(25)如何配置Dubbo的协议和端口?
配置Dubbo的协议和端口是设置分布式服务通信的基础步骤。Dubbo支持多种协议(如Dubbo、RMI、HTTP等),你可以根据需求选择合适的协议并配置相应的端口。下面以一个完整的Spring Boot项目为例,详细介绍如何配置Dubbo的协议和端口。 …...
服务器磁盘卷组缓存cache设置介绍
工具1: storcli a. 确认软件包是否安装 [rootlocalhost ~]#rpm -qa | grep storcli storcli-1.21.06-1.noarch 备注:若检索结果为空,需要安装对应的软件安装包。安装命令如下: #rpm -ivh storcli-xx-xx-1.noarch.rpm b. 查看逻辑…...
StarVector:开启多模态SVG生成的新纪元——开源AI模型的革新之作
在AI技术蓬勃发展的今天,图像生成模型已不再局限于像素级的输出。StarVector作为一款开源的多模态SVG生成模型,凭借其独特的代码与视觉融合能力,正在重新定义矢量图形的创作方式。它不仅让图像生成更灵活、更轻量化,还为设计师、开…...
MySQL日期时间函数
函数分类 函数名 功能描述 语法示例 获取当前日期和时间 NOW() 返回包含年、月、日、时、分、秒的完整时间戳,格式为 YYYY-MM-DD HH:MM:SS SELECT NOW(); CURDATE() / CURRENT_DATE() 获取当前日期,格式为 YYYY-MM-DD SELECT CURDATE(); 或 SE…...
WinSCP使用教程:(SFTP、SCP、FTP 和 WebDAV)
WinSCP 是一款免费开源的 Windows 环境下的 SFTP、SCP、FTP 和 WebDAV 客户端,主要用于在本地计算机与远程服务器之间安全地传输文件,并提供基本的文件管理功能。 WinSCP是Windows环境下使用SSH的开源图形化的SFTP的客户端 SSH 的全称是 Secure Shell&…...
备份是个好习惯
##解题思路 首先看到题目说备份是个好习惯,说明可能存在备份文件泄露 用dirsearch或者其他的目录扫描工具扫一扫,发现两个网址状态码正常,其中一个刚好是.bak的备份文件 至于flag文件,无法读取源码,都是空的 下载备份…...
centos 7 LVM管理命令
物理卷(PV)管理命令 pvcreate:用于将物理磁盘分区或整个磁盘创建为物理卷。 示例:sudo pvcreate /dev/sdb1 解释:将 /dev/sdb1 分区创建为物理卷。 pvdisplay:显示物理卷的详细信息,如大小、所属…...
使用 Spring Boot 3.2 集成 MinIO 8.5:实现高效对象存储
摘要 MinIO 是一款高性能的分布式对象存储服务,与云原生应用完美契合。本文将手把手教你如何在 Spring Boot 3.2 项目中集成 MinIO 8.5 版本,实现文件上传、下载和删除等核心功能,并提供完整代码示例和常见问题解决方案。 一、环境准备 JDK …...
【Qt】数据库管理
数据库查询工具开发学习笔记 一、项目背景与目标 背景:频繁编写数据库查询语句,希望通过工具简化操作,提升效率。 二、总体设计思路 1. 架构设计 MVC模式:通过Qt控件实现视图(UI),业务逻辑…...
C#:Time.deltaTime
目录 第一性原理:从最基本的问题开始 什么是Time.deltaTime? 1. 什么是“帧”? 2. 什么是“帧率”? 为什么需要它? 一个生活化的例子 更通俗的类比 在Unity中的特殊性 第一性原理:从最基本的问题开…...
鸿蒙富文本实践
01 鸿蒙中的文本展示-Text组件 Text 组件的普通用法和其他语言一样,可以直接使用字符串Text(我是一段文本) 通过点语法设置文本样式: Text(我是超长文本,超出的部分显示省略号。I am an extra long text, with ellipses displayed for any ex…...
【字符设备驱动开发–IMX6ULL】(二)Linux 设备号
【字符设备驱动开发–IMX6ULL】(二)Linux 设备号 文章目录 【字符设备驱动开发–IMX6ULL】(二)Linux 设备号1 设备号的组成2.设备号的分配 1 设备号的组成 为了方便管理,Linux 中每个设备都有一个设备号,设…...
Elasticsearch-实战案例
一、没有使用Elasticsearch的查询速度698ms 1.数据库模糊查询不走索引,在数据量较大的时候,查询性能很差。需要注意的是,数据库模糊查询随着表数据量的增多,查询性能的下降会非常明显,而搜索引擎的性能则不会随着数据增…...
电子文档安全管理系统V6.0接口backup存在任意文件下载漏洞
免责声明:本号提供的网络安全信息仅供参考,不构成专业建议。作者不对任何由于使用本文信息而导致的直接或间接损害承担责任。如涉及侵权,请及时与我联系,我将尽快处理并删除相关内容。 漏洞描述 电子文档安全管理系统 V6.0 reso…...
jmeter web压力测试 压测
下载地址 Apache JMeter - Download Apache JMeter 1. 设置线程组 2. 设置http请求头 3. 设置http请求体 4. 设置结果条目 常用函数 ${__RandomString(8, abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789)}${__javaScript( ${__Random(1000, 10000)} /…...