使用SpringBoot + Thymeleaf + MyBatisPlus实现一个简单的书籍管理系统-demo2
一 准备环境
1 数据库环境搭建
CREATE DATABASE IF NOT EXISTS db_book;
USE db_book;
CREATE TABLE book (
id INT PRIMARY KEY AUTO_INCREMENT COMMENT '书籍唯一标识',
name VARCHAR(255) NOT NULL COMMENT '书籍名称',
author VARCHAR(100) NOT NULL COMMENT '作者',
press VARCHAR(100) NOT NULL COMMENT '出版社',
status VARCHAR(20) NOT NULL COMMENT '书籍状态(例如:在库、借出、已预订等)'
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='书籍信息表';
2 Java环境搭建
3 Maven环境搭建
二 实验过程
1 实验目的
- 掌握 MyBatis-Plus 的基本配置与使用方法
- 理解并实践基于 MyBatis-Plus 的 CRUD 操作
- 学会使用 MyBatis-Plus 的条件构造器进行复杂查询
- 掌握分页查询的实现方式
- 理解实体类与数据库表的映射关系
- 培养基于 SpringBoot+MyBatis-Plus 进行实际项目开发的能力
2 实验要求
- 创建一个图书管理系统,实现图书信息的增删改查功能
- 使用 MyBatis-Plus 简化数据访问层代码
- 实现按图书名称、作者、类别等条件进行查询
- 实现图书信息的分页展示
- 对图书信息进行必要的验证(如非空验证)
- 代码结构清晰,符合 SpringBoot 开发规范
- 编写必要的测试用例验证功能正确性
3 实验过程

1. 创建 Maven 项目
使用 IDEA 创建名为E3-BookManagementSystem的Maven项目,pom.xml依赖如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.yqd</groupId>
<artifactId>E3-BookManagementSystem</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>E3-BookManagementSystem</name>
<description>E3-BookManagementSystem</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- MyBatisPlus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.37</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2. 配置文件(application.properties)
在src/main/resources下创建配置文件:
spring:
application:
name: E3-BookManagementSystem
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/db_book?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
username: root # 替换为你的数据库用户名
password: 123456 # 替换为你的数据库密码
# MyBatisPlus配置
mybatis-plus:
mapper-locations: classpath:mapper/*.xml # mapper.xml存放路径
type-aliases-package: com.yqd.entity # 实体类包路径
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 打印SQL日志(可选)
server:
port: 8081
3 启动类
package com.yqd;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.yqd.mapper") // 扫描Mapper接口所在包
public class E3BookManagementSystemApplication {
public static void main(String[] args) {
SpringApplication.run(E3BookManagementSystemApplication.class, args);
}
}
4. 实体类(Book.java)
package com.yqd.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Book {
private Integer id;
private String name;
private String author;
private String press;
private String status;
}
5. Mapper 接口
package com.yqd.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yqd.entity.Book;
import org.apache.ibatis.annotations.Mapper;
@Mapper // 标记为MyBatis的Mapper接口
public interface BookMapper extends BaseMapper<Book> {
// 无需手动编写CRUD方法,BaseMapper已提供
}
6. 服务层接口(BookService.java)
package com.yqd.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.yqd.entity.Book;
import java.util.List;
public interface BookService extends IService<Book> {
// 继承IService,获得更多增强CRUD方法
// 在BookService接口中添加
List<Book> findByStatus(String status);
}
7. 服务层实现(BookServiceImpl.java)
package com.yqd.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.yqd.entity.Book;
import com.yqd.mapper.BookMapper;
import com.yqd.service.BookService;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import java.util.List;
@Service // 标记为服务层组件
public class BookServiceImpl extends ServiceImpl<BookMapper, Book> implements BookService {
@Override
public List<Book> findByStatus(String status) {
// 使用QueryWrapper构建条件
QueryWrapper<Book> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("status", status); // 等价于 WHERE status = ?
return baseMapper.selectList(queryWrapper);
}
}
8. 控制器(BookController.java)
实现增删改查接口:
package com.yqd.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yqd.entity.Book;
import com.yqd.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Controller
@RequestMapping("/books")
public class BookController {
@Autowired
private BookService bookService;
// 1. 查询所有书籍(列表页)
@GetMapping
public String list(Model model) {
// MyBatis-Plus的list()方法等价于JPA的findAll()
List<Book> books = bookService.list();
model.addAttribute("books", books);
return "book/list";
}
// 2. 跳转新增/编辑页
@GetMapping("/form")
public String form(@RequestParam(required = false) Integer id, Model model) {
if (id != null) {
// MyBatis-Plus的getById()等价于JPA的findById()
Book book = bookService.getById(id);
model.addAttribute("book", book != null ? book : new Book());
} else {
model.addAttribute("book", new Book());
}
return "book/form";
}
// 3. 保存书籍(新增/更新)
@PostMapping("/save")
public String save(Book book) {
// MyBatis-Plus的saveOrUpdate()方法自动判断新增/更新(根据ID是否为空)
bookService.saveOrUpdate(book);
return "redirect:/books";
}
// 4. 异步删除书籍
@DeleteMapping("/{id}")
@ResponseBody
public Map<String, Object> delete(@PathVariable Integer id) {
Map<String, Object> result = new HashMap<>();
try {
// MyBatis-Plus的removeById()等价于JPA的deleteById()
boolean success = bookService.removeById(id);
result.put("success", success);
result.put("msg", success ? "删除成功" : "删除失败:书籍不存在");
} catch (Exception e) {
result.put("success", false);
result.put("msg", "删除失败:" + e.getMessage());
}
return result;
}
// 5. 异步查询单本书籍
@GetMapping("/{id}")
@ResponseBody
public Book getBookById(@PathVariable Integer id) {
return bookService.getById(id);
}
// 6. 分页查询示例(在Controller中)
@GetMapping("/page")
@ResponseBody
public IPage<Book> getPage(@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize) {
Page<Book> page = new Page<>(pageNum, pageSize);
return bookService.page(page); // 返回分页结果(包含总条数、当前页数据等)
}
}
9. 前端页面实现(Thymeleaf + jQuery)
9.1 页面目录结构
在src/main/resources/templates下创建:
templates/
├── index.html // 首页
└── book/
├── list.html // 书籍列表页
└── form.html // 新增/编辑页
9.2 首页(index.html)
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<h1 style="text-align: center; margin-top: 100px;">
<a href="/books">进入书籍管理系统</a>
</h1>
</body>
</html>
9.3 书籍列表页(list.html)
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>书籍列表</title>
<script th:src="@{js/jquery.min.js}"></script>
<style>
table { width: 80%; margin: 20px auto; border-collapse: collapse; }
th, td { border: 1px solid #ccc; padding: 10px; text-align: center; }
.btn { padding: 5px 10px; margin: 0 5px; cursor: pointer; }
.add-btn { margin: 20px 0 0 10%; }
/* 新增消息样式 */
.msg {
padding: 10px;
margin: 10px 0;
border-radius: 4px;
text-align: center;
}
.success-msg { background: #d5f5e3; color: #27ae60; }
.error-msg { background: #fadbd8; color: #e74c3c; }
</style>
</head>
<body>
<h1 style="text-align: center;">书籍管理系统</h1>
<!-- 在list.html的.container内添加以下代码(h1标签下方) -->
<div th:if="${not #strings.isEmpty(successMsg)}" class="msg success-msg" th:text="${successMsg}"></div>
<div th:if="${not #strings.isEmpty(errorMsg)}" class="msg error-msg" th:text="${errorMsg}"></div>
<button class="btn add-btn" onclick="window.location.href='/books/form'">新增书籍</button>
<table>
<tr>
<th>ID</th>
<th>书名</th>
<th>作者</th>
<th>出版社</th>
<th>状态</th>
<th>操作</th>
</tr>
<tr th:each="book : ${books}">
<td th:text="${book.id}"></td>
<td th:text="${book.name}"></td>
<td th:text="${book.author}"></td>
<td th:text="${book.press}"></td>
<td th:text="${book.status}"></td>
<td>
<button class="btn edit-btn" th:data-id="${book.id}">编辑</button>
<button class="btn delete-btn" th:data-id="${book.id}">删除</button>
</td>
</tr>
</table>
<script>
$(function() {
// 编辑按钮点击事件
$('.edit-btn').click(function() {
const id = $(this).data('id');
window.location.href = `/books/form?id=${id}`; // 跳转到编辑页
});
// 删除按钮点击事件
$('.delete-btn').click(function() {
const id = $(this).data('id');
if (confirm(`确定删除ID为${id}的书籍吗?`)) {
deleteBook(id, $(this).closest('tr'));
}
});
});
// 异步删除书籍
function deleteBook(id, $tr) {
$.ajax({
url: `/books/${id}`,
type: 'DELETE',
success: function(res) {
if (res.success) {
alert(res.msg);
$tr.remove(); // 移除当前行
} else {
alert(res.msg);
}
},
error: function() {
alert('删除失败,请重试');
}
});
}
<!-- 消息自动消失脚本 -->
$(function() {
// 3秒后自动隐藏提示消息
setTimeout(() => {
$('.msg').fadeOut(1000);
}, 3000);
});
</script>
</body>
</html>
9.4 新增 / 编辑页(form.html)
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title th:text="${book.id != null ? '编辑书籍' : '新增书籍'}"></title>
<script th:src="@{js/jquery.min.js}"></script>
<style>
.container { width: 500px; margin: 50px auto; }
h2 { text-align: center; margin-bottom: 30px; }
.form-group { margin: 15px 0; }
label { display: inline-block; width: 100px; text-align: right; margin-right: 10px; }
input, select { width: 300px; padding: 8px; border: 1px solid #ddd; border-radius: 4px; }
.msg {
padding: 10px;
margin: 10px 0;
border-radius: 4px;
text-align: center;
}
.success-msg { background: #d5f5e3; color: #27ae60; } /* 成功提示 */
.error-msg { background: #fadbd8; color: #e74c3c; } /* 错误提示 */
.btn-group { margin-top: 20px; margin-left: 110px; }
.btn { padding: 8px 20px; border: none; border-radius: 4px; cursor: pointer; }
.save-btn { background: #3498db; color: white; margin-right: 10px; }
.cancel-btn { background: #95a5a6; color: white; }
.save-btn:disabled { background: #95a5a6; cursor: not-allowed; }
</style>
</head>
<body>
<div class="container">
<h2 th:text="${book.id != null ? '编辑书籍' : '新增书籍'}"></h2>
<form id="bookForm" th:action="@{/books/save}" method="post">
<!-- 隐藏域:用于传递ID(编辑时需要) -->
<input type="hidden" name="id" th:value="${book.id}">
<div class="form-group">
<label for="name">书名:</label>
<input type="text" id="name" name="name" th:value="${book.name}" required>
</div>
<div class="form-group">
<label for="author">作者:</label>
<input type="text" id="author" name="author" th:value="${book.author}" required>
</div>
<div class="form-group">
<label for="press">出版社:</label>
<input type="text" id="press" name="press" th:value="${book.press}" required>
</div>
<div class="form-group">
<label for="status">状态:</label>
<select id="status" name="status" required>
<option value="在库" th:selected="${book.status == '在库'}">在库</option>
<option value="借出" th:selected="${book.status == '借出'}">借出</option>
<option value="已预订" th:selected="${book.status == '已预订'}">已预订</option>
</select>
</div>
<div class="form-group">
<button type="submit" class="btn save-btn" id="saveBtn">保存</button>
<button type="button" class="btn" onclick="window.location.href='/books'">取消</button>
</div>
</form>
</div>
<script>
// 防止重复提交:点击保存后禁用按钮
$(function() {
$('#bookForm').submit(function() {
$('#saveBtn').prop('disabled', true).text('保存中...');
return true; // 允许表单提交
});
// 3秒后自动隐藏提示消息
setTimeout(() => {
$('.msg').fadeOut(1000);
}, 3000);
});
</script>
</body>
</html>
4 运行与测试
1. 启动项目
运行E3BookManagementSystemApplication.java的main方法,控制台显示如下信息表示启动成功:
o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8081 (http) with context path ''
2. 访问系统
打开浏览器,输入http://localhost:8081,点击 "进入书籍管理系统" 进入书籍列表页。
3. 功能测试
(1)新增书籍
- 点击 "新增书籍" 按钮,填写表单(如书名 "Java 编程"、作者 "张三"、出版社 "编程出版社"、状态 "在库")。
- 点击 "保存",页面跳转至列表页,显示 "新增成功",列表中出现新增的书籍。
(2)编辑书籍
- 点击某条书籍的 "编辑" 按钮,修改表单内容(如修改状态为 "借出")。
- 点击 "保存",页面跳转至列表页,显示 "更新成功",列表中数据已更新。
(3)删除书籍
- 点击某条书籍的 "删除" 按钮,确认后弹出 "删除成功" 提示,列表中该书籍消失。

浙公网安备 33010602011771号