前提条件:
具备基础的springboot 知识
Java基础
废话不多说!
创建项目
配置所需环境
将application.properties==>application.yml 配置以下环境
数据库连接MySQL
自己创建的数据库名称为book_test
server:port: 8080
spring:datasource:url: jdbc:mysql://localhost:3306/book_test?characterEncoding=utf8&useSSL=falseusername: rootpassword: rootdriver-class-name: com.mysql.cj.jdbc.Drivermvc:hiddenmethod:filter:enabled: truemybatis:mapper-locations: classpath:mapper/*.xmlconfiguration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl#一直出现的问题:引入依赖包的问题
追加两个依赖使得可以使用mapper-locations
数据库准备
图书以及简单的用户信息
CREATE TABLE `book_info` (`id` INT(10) NOT NULL AUTO_INCREMENT,`book_name` VARCHAR(127) NOT NULL COLLATE 'utf8mb4_0900_ai_ci',`author` VARCHAR(127) NOT NULL COLLATE 'utf8mb4_0900_ai_ci',`count` INT(10) NULL DEFAULT NULL,`price` DECIMAL(7,2) NOT NULL,`publish` VARCHAR(256) NOT NULL COLLATE 'utf8mb4_0900_ai_ci',`status` TINYINT(3) NULL DEFAULT '1' COMMENT '0-⽆效, 1-正常, 2-不允许借阅',`create_time` DATETIME NULL DEFAULT 'CURRENT_TIMESTAMP',`update_time` DATETIME NULL DEFAULT 'CURRENT_TIMESTAMP' ON UPDATE CURRENT_TIMESTAMP,PRIMARY KEY (`id`) USING BTREE
)
COLLATE='utf8mb4_0900_ai_ci'
ENGINE=InnoDB
AUTO_INCREMENT=25
;
CREATE TABLE `user_info` (`id` INT(10) NOT NULL AUTO_INCREMENT,`user_name` VARCHAR(128) NOT NULL COLLATE 'utf8mb4_0900_ai_ci',`password` VARCHAR(128) NOT NULL COLLATE 'utf8mb4_0900_ai_ci',`delete_flag` TINYINT(3) NULL DEFAULT '0',`create_time` DATETIME NULL DEFAULT 'CURRENT_TIMESTAMP',`update_time` DATETIME NULL DEFAULT 'CURRENT_TIMESTAMP' ON UPDATE CURRENT_TIMESTAMP,PRIMARY KEY (`id`) USING BTREE,UNIQUE INDEX `user_name_UNIQUE` (`user_name`) USING BTREE
)
COMMENT='用户表'
COLLATE='utf8mb4_0900_ai_ci'
ENGINE=InnoDB
AUTO_INCREMENT=3
;
spring MVC 运行逻辑
Mapper ==> Service ==> Controller
mapper接口层代码:
@Insert("INSERT INTO book_info (book_name, author, count, price, publish, status, create_time, update_time) VALUES " +"(#{bookName}, #{author}, #{count}, #{price}, #{publish}, #{status}, #{createTime}, #{updateTime})")@Options(useGeneratedKeys = true, keyProperty = "id")int insertBook(BookInfo book);@Select("SELECT id, book_name AS bookName, author, count, price, publish, status, " +"CASE status WHEN 0 THEN '无效' WHEN 1 THEN '允许借阅' ELSE '不允许借阅' END AS statusCN, " +"create_time, update_time FROM book_info WHERE id = #{id}")@Results(id = "bookResultMap", value = {@Result(property = "id", column = "id"),@Result(property = "bookName", column = "bookName"),@Result(property = "author", column = "author"),@Result(property = "count", column = "count"),@Result(property = "price", column = "price"),@Result(property = "publish", column = "publish"),@Result(property = "status", column = "status"),@Result(property = "statusCN", column = "statusCN"),@Result(property = "createTime", column = "create_time"),@Result(property = "updateTime", column = "update_time")})BookInfo selectBookById(Integer id);@Update("UPDATE book_info SET book_name = #{bookName}, author = #{author}, count = #{count}, price = #{price}, " +"publish = #{publish}, status = #{status}, update_time = #{updateTime} WHERE id = #{id}")int updateBook(BookInfo book);@Delete("DELETE FROM book_info WHERE id = #{id}")int deleteBook(Integer id);@Select("SELECT * FROM book_info")List<BookInfo> selectAllBooks();List<BookInfo> mockData();
创建用户bookinfo
@RequestMapping("/user")
@RestController
public class UserController {@RequestMapping("/login")public boolean login(String name, String password, HttpSession session){//账号或密码为空if (!StringUtils.hasLength(name) || !StringUtils.hasLength(password)){return false;}//模拟验证数据, 账号密码正确if("admin".equals(name) && "admin".equals(password)){session.setAttribute("userName",name);return true;}//账号密码错误return false;}
@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;
}
service层面:
/*** 添加图书* @param book 图书信息* @return 操作结果,成功返回true,失败返回false*/boolean addBook(BookInfo book);/*** 根据ID查询图书* @param id 图书ID* @return 图书信息*/BookInfo getBookById(Integer id);/*** 更新图书信息* @param book 图书信息* @return 操作结果,成功返回true,失败返回false*/boolean updateBook(BookInfo book);/*** 删除图书* @param id 图书ID* @return 操作结果,成功返回true,失败返回false*/boolean deleteBook(Integer id);/*** 获取所有图书列表* @return 图书列表*/List<BookInfo> getAllBooks();
接口层的实现
private BookInfoMapper bookMapper;@Overridepublic boolean addBook(BookInfo book) {// 可以在这里添加业务逻辑校验,例如检查图书信息是否完整int rowsAffected = bookMapper.insertBook(book);return rowsAffected > 0;}@Overridepublic BookInfo getBookById(Integer id) {return bookMapper.selectBookById(id);}@Overridepublic boolean updateBook(BookInfo book) {// 业务逻辑校验,例如确保ID不为空且存在对应的图书记录int rowsAffected = bookMapper.updateBook(book);return rowsAffected > 0;}@Overridepublic boolean deleteBook(Integer id) {// 可以添加逻辑判断,比如检查图书是否已被借阅不能删除int rowsAffected = bookMapper.deleteBook(id);return rowsAffected > 0;}@Overridepublic List<BookInfo> getAllBooks() {return bookMapper.selectAllBooks();}
控制层面:
@Autowiredprivate BookService bookService;// 添加图书@PostMapping("/add")public ResponseEntity<ResponseMessage> addBook(@RequestBody BookInfo book) {
// boolean isAdded = bookService.addBook(book);
// if (isAdded) {
// return ResponseEntity.status(200).body(book);
// } else {
// return ResponseEntity.badRequest().build();
// }try {boolean isAdded = bookService.addBook(book);if (isAdded) {// 创建ResponseMessage实例,表示成功ResponseMessage response = new ResponseMessage(200, "图书添加成功", book);return ResponseEntity.ok(response); // 注意这里返回的是ResponseEntity<ResponseMessage>} else {// 创建ResponseMessage实例,表示失败ResponseMessage response = new ResponseMessage(400, "图书添加失败,请检查输入信息", null);return ResponseEntity.badRequest().body(response); // 同样返回ResponseEntity<ResponseMessage>}} catch (Exception e) {// 异常处理,返回错误信息ResponseMessage response = new ResponseMessage(500, "添加图书时发生系统错误: " + e.getMessage(), null);return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response);}}// 根据ID获取图书信息@GetMapping("/{id}")public ResponseEntity<BookInfo> getBookById(@PathVariable Integer id) {BookInfo book = bookService.getBookById(id);if (book != null) {return ResponseEntity.ok(book);} else {return ResponseEntity.notFound().build();}}// 更新图书信息@PutMapping("/{id}")public ResponseEntity<Void> updateBook(@PathVariable Integer id, @RequestBody BookInfo book) {book.setId(id); // 确保请求体中的ID与路径变量ID一致boolean isUpdated = bookService.updateBook(book);if (isUpdated) {return ResponseEntity.noContent().build();} else {return ResponseEntity.notFound().build();}}// 删除图书@DeleteMapping("/{id}")public ResponseEntity<Void> deleteBook(@PathVariable Integer id) {boolean isDeleted = bookService.deleteBook(id);if (isDeleted) {return ResponseEntity.noContent().build();} else {return ResponseEntity.notFound().build();}}// 获取所有图书列表@GetMappingpublic ResponseEntity<List<BookInfo>> getAllBooks() {List<BookInfo> books = bookService.getAllBooks();return ResponseEntity.ok(books);}
public int getCode() {return code;}public void setCode(int code) {this.code = code;}public String getMessage() {return message;}public void setMessage(String message) {this.message = message;}public Object getData() {return data;}public void setData(Object data) {this.data = data;}public ResponseMessage(int code, String message, Object data) {this.code = code;this.message = message;this.data = data;}private int code; // 状态码,200表示成功,非200表示各种错误private String message; // 描述信息private Object data; // 可选,成功时返回的数据或错误时的额外信息
后端结束
前台交互
增加图书:
<!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="history.back()">返回</button></div></form></div><script type="text/javascript" src="js/jquery.min.js"></script><script>function add() {// 收集表单数据const formData = {bookName: $("#bookName").val(),author: $("#bookAuthor").val(),count: $("#bookStock").val(),price: $("#bookPrice").val(),publish: $("#bookPublisher").val(),status: $("#bookStatus").val()};// 使用Ajax发送数据到后端$.ajax({type: "POST",url: "/book/add", //请替换为您的后端接口地址contentType: "application/json; charset=utf-8",data: JSON.stringify(formData),success: function(response) {if(response.code === 200 || response.code === 201) { // 假设200或201都表示成功// alert(response.code);alert("添加成功");location.href = "book_list.html"; // 添加成功后跳转到列表页} else {alert("添加失败:" + response.message);}},error: function(xhr, status, error) {alert("添加图书时发生错误: " + error);}});}</script>
</body></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 class="table"><thead><tr><th><label for="selectAllBooks"></label><input type="checkbox" id="selectAllBooks"></th><th>ID</th><th>书名</th><th>作者</th><th>数量</th><th>定价</th><th>出版社</th><th>状态</th><th>操作</th></tr></thead><tbody></tbody></table><div class="demo"><ul id="pageContainer" class="pagination justify-content-center"></ul></div><script>$(document).ready(function() {getBookList();$("#selectAllBooks").click(function() {$("input[name='selectBook']").prop('checked', this.checked);});});function getBookList() {$.ajax({type: "get",url: "/book"+location.search,success: function (result) {console.log(result);if (result != null) {let finalHtml = "";for (const book of result) {finalHtml += '<tr>';finalHtml += '<td><input type="checkbox" name="selectBook" value="' + book.id + '"></td>'; // 修正了value属性的遗漏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 + '" class="btn btn-sm btn-primary">编辑</a>'; // 修正了链接的闭合finalHtml += '<button class="btn btn-sm btn-danger" onclick="deleteBook(' + book.id + ')">删除</button>';finalHtml += '</div></td>';finalHtml += '</tr>';}$("tbody").html(finalHtml);}}});}function deleteBook(id) {const isDelete = confirm("确认删除?");if (isDelete) {$.ajax({type: "DELETE",url: "/book/" + id,success: function() {alert("删除成功");// 刷新页面以反映删除后的数据变化getBookList();location.reload();},error: function(xhr, status, error) {if (xhr.status === 404) {alert("图书不存在");} else {alert("删除失败: " + error);}}});}}function batchDelete() {const isDelete = confirm("确认批量删除?");if (isDelete) {//获取复选框的idconst ids = [];$("input:checkbox[name='selectBook']:checked").each(function () {ids.push($(this).val());});console.log(ids);alert("批量删除成功");}}</script>
</div>
</body></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="history.back()">返回</button></div></form></div><script type="text/javascript" src="js/jquery.min.js"></script><!-- HTML 表单保持不变 --><script>function update() {// 收集表单数据const formData = {id: location.search.split("=")[1], // 确保隐藏字段包含图书IDbookName: $("#bookName").val(),author: $("#bookAuthor").val(),count: $("#bookStock").val(),price: $("#bookPrice").val(),publish: $("#bookPublisher").val(),status: $("#bookStatus").val()};// 对id进行Base64编码// 使用Ajax发送PUT请求到Spring Boot后端更新图书,URL中包含Base64编码的id// 构建正确的URL,使用模板字符串动态插入IDconsole.log("--------------"+formData)const urls = `/book/${formData.id}`;// 使用Ajax发送PUT请求到Spring Boot后端更新图书$.ajax({type: "PUT",url: urls, // 使用上面构建的正确URLcontentType: "application/json; charset=utf-8",data: JSON.stringify(formData),success: function(response, textStatus, jqXHR) {if(jqXHR.status === 204) {alert("更新成功");location.href = "book_list.html";} else if(response.status === 'SUCCESS') {alert("更新成功");location.href = "book_list.html";} else {alert("更新失败:" + response.message);}},error: function(xhr, status, error) {alert("更新图书时发生错误: " + xhr.responseText || error);}});}</script>
</body></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="713" alt=""></div><div class="login-dialog"><h3>登陆</h3><div class="row"><span>用户名</span><label for="userName"></label><input type="text" name="userName" id="userName" class="form-control"></div><div class="row"><span>密码</span><label for="password"></label><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: {name: $("#userName").val(),password: $("#password").val()},success: function (result) {if (result) {location.href = "book_list.html";} else {alert("账号或密码不正确!");}}});}</script>
</body></html>
总结:
src/main/java/bao/book_again
目录下的BookAgainApplication.java
是项目的启动类,它继承了Spring Boot的SpringBootApplication
,并使用@SpringBootApplication
注解开启自动配置和扫描。
Controller
目录下有多个控制器类,如BasicController.java
和PathVariableController.java
,它们使用Spring MVC的@RestController
或@Controller
注解,处理来自客户端的HTTP请求,并返回相应的响应。
Domain
目录下有领域模型类,如User.java
和BookInfo.java
,它们代表了应用中的实体对象。
Exception
目录下有异常处理类,如ResponseMessage.java
,可能是用于封装错误信息和响应状态的类。
Mapper
目录下有MyBatis的相关接口,如BookService.java
,它们定义了数据库操作的接口。
Service
目录下有服务层实现,如BookServiceImpl.java
,实现了业务逻辑。
resources/static
目录下有静态资源,如HTML文件和图片。
application.yml
是项目的配置文件,用于配置Spring Boot应用的各种属性。
test
目录下可能有单元测试或集成测试代码。这个项目使用了Spring Boot和Spring MVC,结合MyBatis作为持久层框架,实现了基本的CRUD操作。用户界面使用HTML模板,通过RESTful API与后端交互。整体来看,这是一个典型的MVC架构的Web应用,其中
Controller
负责处理HTTP请求,Service
层处理业务逻辑,Mapper
层处理数据库操作,Domain
层定义了领域模型,Exception
层处理异常,static
目录下存放了前端资源