在实际 Java Web 开发学习路径中,图书管理系统是一个经典且综合性强的练手项目。它涵盖了用户认证、数据增删改查、业务逻辑分层、数据库设计等核心技能点,非常适合用于巩固 Java 基础、理解 MVC 模式以及掌握 Spring Boot + MyBatis 等主流技术栈。本文将基于 IntelliJ IDEA 开发环境,从零开始构建一个功能完整的图书管理系统,重点讲解项目结构设计、数据库表关系、核心功能实现、以及如何排查部署中的常见问题,最终交付一个可运行、可扩展的毕业设计或课程设计级别的项目。
1. 项目核心需求与技术栈选型
一个完整的图书管理系统至少需要实现以下核心功能模块:
- 用户管理:区分管理员与普通用户,实现登录、注册、权限控制。
- 图书管理:图书信息的增、删、改、查,包括书名、作者、出版社、库存等字段。
- 借阅管理:用户借书、还书流程,借阅记录查询,超期提醒或处罚逻辑。
- 数据统计:简单的数据可视化,如图书借阅排行榜、库存预警等。
基于这些需求,技术栈选择如下:
- 后端:Spring Boot 2.7.x(兼顾稳定性和新特性)、Spring MVC、MyBatis-Plus(简化 CRUD 操作)。
- 数据库:MySQL 8.0(或 5.7),使用 InnoDB 存储引擎支持事务。
- 前端:Thymeleaf 模板引擎(服务端渲染,适合初学者理解前后端交互)、Bootstrap 5(快速构建响应式界面)。
- 开发工具:IntelliJ IDEA 2022+,Maven 3.6+,JDK 17(与标题中提示的目标发行版一致)。
- 其他:Druid 数据源(监控 SQL 性能)、Lombok(简化 POJO 编写)。
注意:JDK 版本必须与项目配置一致。如果遇到 “源发行版 17 需要目标发行版 17” 警告,需检查 IDEA 中 Project Structure 的 Project 和 Modules 的 Language level 是否均为 17。
2. 数据库设计与表结构构建
数据库设计是项目的基石,合理的表结构能减少后期业务扩展的阻力。本系统至少需要以下四张核心表:
用户表 (user)
CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键ID', `username` varchar(50) NOT NULL UNIQUE COMMENT '用户名', `password` varchar(100) NOT NULL COMMENT '密码(加密存储)', `name` varchar(50) DEFAULT NULL COMMENT '真实姓名', `role` int(1) NOT NULL DEFAULT '0' COMMENT '角色:0-普通用户,1-管理员', `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';图书表 (book)
CREATE TABLE `book` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键ID', `isbn` varchar(20) DEFAULT NULL COMMENT 'ISBN号', `name` varchar(100) NOT NULL COMMENT '图书名称', `author` varchar(50) DEFAULT NULL COMMENT '作者', `publisher` varchar(100) DEFAULT NULL COMMENT '出版社', `total_count` int(11) NOT NULL DEFAULT '0' COMMENT '总库存', `current_count` int(11) NOT NULL DEFAULT '0' COMMENT '当前可借库存', `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '上架时间', PRIMARY KEY (`id`), KEY `idx_name` (`name`), KEY `idx_author` (`author`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='图书表';借阅记录表 (borrow_record)
CREATE TABLE `borrow_record` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键ID', `user_id` int(11) NOT NULL COMMENT '用户ID', `book_id` int(11) NOT NULL COMMENT '图书ID', `borrow_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '借书时间', `expected_return_time` datetime DEFAULT NULL COMMENT '预计归还时间', `actual_return_time` datetime DEFAULT NULL COMMENT '实际归还时间', `status` int(1) NOT NULL DEFAULT '0' COMMENT '状态:0-借阅中,1-已归还,2-超期未还', PRIMARY KEY (`id`), KEY `idx_user_id` (`user_id`), KEY `idx_book_id` (`book_id`), CONSTRAINT `fk_borrow_user` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`), CONSTRAINT `fk_borrow_book` FOREIGN KEY (`book_id`) REFERENCES `book` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='借阅记录表';关键设计要点:
- 密码字段使用
varchar(100)为加密后密文预留空间。 - 图书表分离
total_count和current_count,避免每次借阅都需查询历史记录计算可用库存。 - 借阅记录表通过
status字段管理生命周期,并设置外键约束保证数据一致性。 - 为常用查询字段(如图书名称、作者)建立索引提升查询效率。
3. Spring Boot 项目结构与核心配置
使用 IDEA 创建 Spring Boot 项目后,标准的多模块结构如下:
src/main/java/com/example/library/ ├── LibraryApplication.java # 启动类 ├── config/ │ ├── WebMvcConfig.java # MVC配置(拦截器、静态资源) │ └── MybatisPlusConfig.java # MyBatis-Plus 分页插件配置 ├── controller/ │ ├── LoginController.java # 登录/注册控制 │ ├── BookController.java # 图书管理 │ └── BorrowController.java # 借阅管理 ├── entity/ # 实体类(与数据库表对应) ├── mapper/ # MyBatis Mapper 接口 ├── service/ # 业务逻辑层 │ └── impl/ # 实现类 ├── util/ # 工具类(加密、日期处理等) └── resources/ ├── static/ # CSS/JS/图片 ├── templates/ # Thymeleaf 模板 ├── application.yml # 主配置文件 └── mapper/ # XML 映射文件application.yml 关键配置:
spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/library_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: your_password type: com.alibaba.druid.pool.DruidDataSource thymeleaf: cache: false # 开发时关闭缓存,修改模板立即生效 mode: HTML encoding: UTF-8 server: port: 8080 mybatis-plus: configuration: map-underscore-to-camel-case: true # 自动转换表字段到实体类属性 global-config: db-config: logic-delete-field: deleted # 全局逻辑删除字段 logic-delete-value: 1 logic-not-delete-value: 0注意:数据库连接参数需根据本地环境调整。若使用 Druid 数据源,需在 pom.xml 中显式引入依赖,并在配置类中开启监控功能(生产环境需配置访问权限)。
4. 核心业务逻辑实现与代码详解
4.1 用户登录与会话管理
登录流程需处理密码加密验证和会话创建。使用 Spring Boot 内置的HttpSession实现简单会话管理:
LoginController 关键代码:
@Controller public class LoginController { @Autowired private UserService userService; @PostMapping("/login") public String login(String username, String password, HttpSession session) { User user = userService.findByUsername(username); if (user != null && userService.checkPassword(password, user.getPassword())) { session.setAttribute("loginUser", user); if (user.getRole() == 1) { return "redirect:/admin/index"; // 管理员首页 } return "redirect:/user/index"; // 普通用户首页 } // 登录失败,返回错误信息 return "redirect:/login?error=1"; } @GetMapping("/logout") public String logout(HttpSession session) { session.invalidate(); return "redirect:/login"; } }密码加密工具类(使用 Spring Security 的 BCrypt):
public class PasswordEncoderUtil { private static final BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); public static String encode(String rawPassword) { return encoder.encode(rawPassword); } public static boolean matches(String rawPassword, String encodedPassword) { return encoder.matches(rawPassword, encodedPassword); } }关键点:不要明文存储密码。注册时对密码加密后存库,登录时使用
matches方法对比。
4.2 图书管理增删改查
利用 MyBatis-Plus 的强大功能,只需定义 Mapper 接口即可获得大部分 CRUD 能力:
BookController 列表查询与分页:
@Controller @RequestMapping("/admin/book") public class BookController { @Autowired private BookService bookService; @GetMapping("/list") public String list(@RequestParam(defaultValue = "1") Integer pageNum, @RequestParam(defaultValue = "10") Integer pageSize, Model model) { Page<Book> page = new Page<>(pageNum, pageSize); Page<Book> bookPage = bookService.page(page); model.addAttribute("pageInfo", bookPage); return "admin/book_list"; } @PostMapping("/save") public String save(Book book) { // 新增时初始化库存 if (book.getId() == null) { book.setCurrentCount(book.getTotalCount()); } bookService.saveOrUpdate(book); return "redirect:/admin/book/list"; } @PostMapping("/delete") public String delete(Integer id) { // 删除前检查是否有未归还记录 if (borrowService.existsBorrowingRecord(id)) { // 返回错误提示 return "redirect:/admin/book/list?error=1"; } bookService.removeById(id); return "redirect:/admin/book/list"; } }前端分页控件(Thymeleaf + Bootstrap):
<div th:if="${pageInfo.pages > 1}"> <nav> <ul class="pagination"> <li class="page-item" th:classappend="${pageInfo.current == 1} ? 'disabled'"> <a class="page-link" th:href="@{/admin/book/list(pageNum=1)}">首页</a> </li> <li th:each="pageNum : ${#numbers.sequence(1, pageInfo.pages)}" class="page-item" th:classappend="${pageNum == pageInfo.current} ? 'active'"> <a class="page-link" th:href="@{/admin/book/list(pageNum=${pageNum})}" th:text="${pageNum}"></a> </li> </ul> </nav> </div>4.3 借阅业务中的事务控制
借书操作需同时更新借阅记录和图书库存,必须保证事务原子性:
BorrowService 借书方法:
@Service public class BorrowService { @Autowired private BorrowRecordMapper borrowRecordMapper; @Autowired private BookMapper bookMapper; @Transactional(rollbackFor = Exception.class) // 声明式事务管理 public boolean borrowBook(Integer userId, Integer bookId) { // 1. 检查图书库存 Book book = bookMapper.selectById(bookId); if (book == null || book.getCurrentCount() <= 0) { return false; } // 2. 插入借阅记录 BorrowRecord record = new BorrowRecord(); record.setUserId(userId); record.setBookId(bookId); record.setBorrowTime(new Date()); record.setExpectedReturnTime(calculateReturnDate(30)); // 借期30天 record.setStatus(0); borrowRecordMapper.insert(record); // 3. 更新图书库存 book.setCurrentCount(book.getCurrentCount() - 1); bookMapper.updateById(book); return true; } private Date calculateReturnDate(int days) { Calendar calendar = Calendar.getInstance(); calendar.add(Calendar.DAY_OF_MONTH, days); return calendar.getTime(); } }关键点:
@Transactional注解确保三步操作要么全部成功,要么全部回滚。避免出现记录插入成功但库存未减少的数据不一致情况。
5. 部署运行与常见问题排查
5.1 项目启动步骤
- 环境准备:确保本地已安装 JDK 17、MySQL 8.0+、Maven 3.6+,并配置好 IDEA 开发环境。
- 数据库初始化:执行提供的 SQL 脚本创建数据库和表结构。
- 配置修改:在
application.yml中修改数据库连接参数。 - 依赖下载:在项目根目录执行
mvn clean compile下载依赖。 - 启动项目:运行
LibraryApplication主类,控制台输出Started Spring Boot即表示启动成功。 - 访问系统:打开浏览器访问
http://localhost:8080,使用默认管理员账号(admin/123456)登录。
5.2 常见问题与解决方案
| 问题现象 | 可能原因 | 检查方式 | 处理建议 |
|---|---|---|---|
| 启动时报端口占用 | 8080 端口被其他进程占用 | 执行netstat -ano | findstr 8080 | 修改server.port或终止占用进程 |
| 数据库连接失败 | 数据库服务未启动/密码错误/网络不通 | 检查 MySQL 服务状态,用客户端工具测试连接 | 确认服务启动,核对连接参数 |
| 页面模板解析错误 | Thymeleaf 模板语法错误或路径不对 | 查看控制台异常栈,检查模板文件位置 | 修正模板语法,确认文件在templates目录下 |
| 静态资源 404 | 静态文件未放在正确目录或路径引用错误 | 浏览器 F12 查看 Network 请求 | 将 CSS/JS 放入static目录,使用相对路径引用 |
| 插入中文乱码 | 数据库字符集不支持中文或连接参数未指定编码 | 检查数据库、表、字段的字符集是否为utf8mb4 | 确保连接 URL 包含useUnicode=true&characterEncoding=utf8 |
5.3 功能验证清单
部署完成后,按以下顺序验证系统功能是否正常:
- [ ] 能够访问登录页面(http://localhost:8080/login)
- [ ] 使用默认管理员账号成功登录并跳转到管理员首页
- [ ] 在图书管理页面能够添加新图书(填写完整信息)
- [ ] 添加后能在列表页看到新图书,库存显示正确
- [ ] 能够编辑图书信息并保存成功
- [ ] 能够删除无借阅记录的图书
- [ ] 注册新用户并能够以该用户身份登录
- [ ] 普通用户能够借阅有库存的图书
- [ ] 借阅后图书可用库存减少,借阅记录生成
- [ ] 归还图书后库存恢复,借阅记录状态更新
6. 生产环境部署建议与扩展方向
6.1 生产环境关键配置
学习环境与生产环境的主要差异在于安全性、性能和可观测性:
application-prod.yml 示例:
spring: datasource: url: jdbc:mysql://生产数据库IP:3306/library_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: 专用数据库账号 password: 强密码 druid: initial-size: 5 max-active: 20 min-idle: 5 # 配置监控统计和防SQL注入 filters: stat,wall web-stat-filter: enabled: true stat-view-servlet: enabled: true login-username: admin login-password: 监控密码 thymeleaf: cache: true # 生产环境开启模板缓存 logging: level: com.example.library.mapper: DEBUG # 生产环境可改为 INFO 或 WARN file: name: logs/library.log pattern: file: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{50} - %msg%n" server: port: 80 servlet: context-path: /library # 添加上下文路径,避免直接使用根路径6.2 系统扩展方向
基础功能实现后,可以考虑以下扩展点提升项目完整度:
- 权限细化:使用 Spring Security 替代简单 Session 管理,实现基于角色的细粒度权限控制。
- Redis 缓存:将热点图书数据、用户会话缓存到 Redis,减轻数据库压力。
- 异步任务:使用
@Async实现借阅超期提醒邮件发送等异步功能。 - API 接口:增加 RESTful API 层,支持移动端 App 调用。
- 数据可视化:集成 ECharts,展示借阅趋势、图书分类统计等图表。
- 全文检索:集成 Elasticsearch,实现图书名称、作者、简介的模糊搜索。
- 微服务化:将用户服务、图书服务、借阅服务拆分为独立微服务。
图书管理系统作为 Java Web 开发的经典案例,真正价值不在于功能有多复杂,而在于通过它掌握企业级应用的标准开发流程、数据设计原则和问题排查方法。建议在完成基础版本后,选择 1-2 个扩展方向深入实践,这将极大提升对分布式系统、性能优化等高级主题的理解。