ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

SpringBoot图书管理网站实战:从需求拆解到部署全流程

SpringBoot图书管理网站实战:从需求拆解到部署全流程 做课程设计或者毕设的同学十有八九都会碰到“图书管理网站”这个题目。它看起来很简单——不就是书的增删改查嘛但真要做得完整、跑得顺畅、论文写得出来还是有不少细节值得掰开揉碎讲一讲的。这篇文章我以最近做的一个SpringBoot图书管理网站项目代号tlget为例从需求拆解、表结构设计、核心逻辑实现到最后的打包部署把整个过程捋一遍。不管你是刚接触SpringBoot的新手还是已经在写了但被各种报错卡住的老哥这篇文章都能给你一些可以直接抄作业的东西。这个项目的形态是一套典型的单体Web应用后端用SpringBoot提供接口和页面渲染数据库用MySQL存图书、读者、借阅记录这些核心数据前端配合Thymeleaf模板和服务端渲染整体既能演示CRUD的基本功又能覆盖登录鉴权、分页检索、借还书状态流转这类真实业务场景。相比单纯做接口练习这种带完整业务闭环的项目更适合作为课程设计或毕业设计的载体也方便后续往微服务、缓存、消息队列方向做扩展。1. 项目背景与整体需求拆解1.1 图书管理网站到底是做什么的图书管理网站的本质是把传统人工登记借阅的过程数字化。读者要查书、借书、还书管理员要管理书库、审核读者、处理逾期这些操作如果靠Excel或者纸质记录你会很快被数据一致性和查找效率搞崩溃。所以这类系统的核心诉求是图书信息统一管理书名、作者、ISBN、分类、库存、价格、上架状态。借阅流程线上化读者可以检索图书、提交借阅管理员可以审核和登记还书。读者与权限分离普通用户只能操作自己的借阅记录管理员拥有全量管理权限。数据统计可视化看看哪些书借得最多、哪个分类最受欢迎、每天有多少借还操作。项目代号tlget里的“tl”在我当时的理解里就是“图书列表”的缩写“get”就是获取和管理。你可以把它当作一个内部代号不用纠结命名重点是功能边界要清晰。1.2 功能清单与角色权限划分做需求拆解的时候我习惯先用一句话说清楚系统里有哪些角色每个角色能干什么。做出来之后再去画用例图、写文档思路会顺很多。角色核心权限主要功能管理员全量管理图书增删改查、分类管理、读者管理、借阅审核、还书登记、逾期处理、数据统计普通读者个人操作注册登录、检索图书、查看图书详情、提交借书申请、查询个人借阅记录、续借、查看公告功能细节上有几个点容易被忽略但必须做扎实图书检索要支持按书名模糊搜索、按ISBN精确匹配、按分类筛选还要支持分页。不要小看分页数据量一上去一次性查全部记录会让页面卡成PPT。借书必须做状态校验库存为0的书不能借同一本书同一读者不能重复借未还已经下架的书不能出现在可借列表里。还书要做逾期计算约定借期为30天超过这个期限要标记逾期状态并计算出逾期天数方便后续处理。1.3 适合谁来做、能不能扩展这个题目非常适合作为SpringBoot入门到进阶的过渡项目。如果你只会写HelloWorld和单表CRUD做完这个项目你至少能理解多表关联查询、事务控制、分页、登录拦截这几块硬骨头。如果你是马上要交毕设的同学这个项目的完整度足够支撑一篇1万字以上的论文因为它的业务闭环完整能画出来的图很多功能结构图、用例图、ER图、流程图、时序图、架构图。而且这个项目的扩展空间非常大。我见过有人加了Redis缓存热点图书有人接了Elasticsearch做全文检索还有人把借阅模块改成消息队列异步处理。基础打好了后续怎么玩都行。2. 技术选型与架构设计思路2.1 为什么是SpringBoot而不是别的现在做Java后端项目基本不用纠结框架选型SpringBoot就是默认答案。相比传统的SSMSpring SpringMVC MyBatis手动配置一堆XMLSpringBoot用自动配置把大部分基础设施都搞定了内嵌Tomcat让部署也变得极其简单一个jar包就能跑起来。具体到本项目有两个实际考量环境兼容性SpringBoot 2.x搭配JDK 1.8是目前最稳的组合我见过太多同学一上来就装JDK 17跑SpringBoot 3.x结果各种依赖对不上报错报得怀疑人生。毕设和课程设计选SpringBoot 2.7.x JDK 1.8基本不会踩坑。生态成熟度网上搜得到的资料、博客、视频90%以上都是基于SpringBoot 2.x的遇到问题很容易搜到解决方案。2.2 ORM框架的取舍和前端方案对比ORM层面有两条主流路线MyBatis-Plus和Spring Data JPA。我的选择是MyBatis-Plus原因很简单MyBatis-Plus对CRUD做了增强单表操作基本不用写SQL内置分页插件也省事。复杂查询仍然可以手写XML灵活性比JPA高出了问题排查起来直观。前端方案我推荐直接上Thymeleaf服务端渲染而不是SpringBoot Vue前后端分离。维度Thymeleaf服务端渲染Vue前后端分离上手难度低HTML加几个属性就行中需要Node环境、跨域处理项目复杂度和论文内容适合紧凑的课程设计适合想展示前后端分离能力的同学部署方式一个jar包搞定前端需要Nginx部署后端独立jar调试成本浏览器直接看模板渲染结果需要启动两个服务联调麻烦如果你不想给自己加戏就老老实实用Thymeleaf。把常见的管理系统页面做出来完全够用。2.3 项目结构怎么组织我习惯按经典的分层结构组织包名虽然看起来老套但对于课程设计和毕设论文来说这种结构最好讲也最好懂。com.example.library ├── controller # 控制器层接收请求、返回视图或数据 ├── service # 业务层处理核心逻辑借书、还书、统计 │ └── impl # 业务实现 ├── mapper # 数据访问层MyBatis-Plus的Mapper接口 ├── entity # 实体类对应数据库表 ├── config # 配置文件类拦截器、分页插件等 ├── common # 公共类统一返回结果、常量、异常处理 └── LibraryApplication.java # 启动类很多同学喜欢把所有代码堆在Controller里省事是省事但后面你写论文的时候会发现没办法清晰地描述模块划分和调用关系。分层隔离虽然多写几行代码但好处是测试、维护、写文档都舒服。3. 数据库设计与核心功能实现3.1 核心表结构设计与字段说明图书管理系统的数据库不需要搞得很复杂我最终设计成4张核心表用户表、图书表、图书分类表、借阅记录表。下面是建表SQL的核心片段直接拿来改改就能用。CREATE DATABASE library_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE library_db; CREATE TABLE sys_user ( id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT 主键, username VARCHAR(50) NOT NULL UNIQUE COMMENT 登录名, password VARCHAR(100) NOT NULL COMMENT 密码BCrypt加密, real_name VARCHAR(50) COMMENT 真实姓名, role TINYINT NOT NULL DEFAULT 0 COMMENT 角色0读者 1管理员, phone VARCHAR(20), create_time DATETIME DEFAULT CURRENT_TIMESTAMP, status TINYINT DEFAULT 1 COMMENT 账号状态1正常 0禁用 ); CREATE TABLE book_category ( id BIGINT PRIMARY KEY AUTO_INCREMENT, category_name VARCHAR(50) NOT NULL UNIQUE, sort_order INT DEFAULT 0 ); CREATE TABLE book ( id BIGINT PRIMARY KEY AUTO_INCREMENT, book_name VARCHAR(100) NOT NULL, isbn VARCHAR(20) COMMENT 国际标准书号, author VARCHAR(100), publisher VARCHAR(100), category_id BIGINT, total_stock INT NOT NULL DEFAULT 0 COMMENT 总库存, available_stock INT NOT NULL DEFAULT 0 COMMENT 可借库存, price DECIMAL(10,2), cover_url VARCHAR(255), status TINYINT DEFAULT 1 COMMENT 1上架 0下架, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, INDEX idx_book_name(book_name), INDEX idx_category(category_id) ); CREATE TABLE borrow_record ( id BIGINT PRIMARY KEY AUTO_INCREMENT, user_id BIGINT NOT NULL, book_id BIGINT NOT NULL, borrow_time DATETIME DEFAULT CURRENT_TIMESTAMP, due_time DATETIME COMMENT 应还时间, return_time DATETIME COMMENT 实际归还时间, status TINYINT NOT NULL DEFAULT 0 COMMENT 0借出中 1已归还 2逾期未还, overdue_days INT DEFAULT 0 COMMENT 逾期天数, INDEX idx_user(user_id), INDEX idx_book(book_id), INDEX idx_status(status) );字段设计上我有几个经验可以分享库存一定要分成total_stock和available_stock两个字段不要只在book表里存一个总数。借出和归还都只动可借库存总库存用于统计和展示。状态字段我统一用TINYINT加注释代码里用常量类定义不要直接在业务代码里写魔法数字0、1、2。时间字段统一用DATETIMEJava实体里对应LocalDateTime不要用java.util.Date处理起来麻烦。外键约束我没加因为业务逻辑在Service层控制就够了加了外键反而给调试和删除数据添乱。3.2 图书检索与分页查询实现图书检索是读者使用频率最高的功能核心是动态拼接查询条件。MyBatis-Plus配合LambdaQueryWrapper的写法可以省掉手写SQL的麻烦Override public PageBook searchBooks(String keyword, Long categoryId, Integer pageNum, Integer pageSize) { PageBook page new Page(pageNum, pageSize); LambdaQueryWrapperBook wrapper new LambdaQueryWrapper(); // 关键字搜索书名或作者模糊匹配 if (StringUtils.hasText(keyword)) { wrapper.and(w - w.like(Book::getBookName, keyword) .or().like(Book::getAuthor, keyword)); } // 分类筛选 if (categoryId ! null) { wrapper.eq(Book::getCategoryId, categoryId); } // 只展示上架的 wrapper.eq(Book::getStatus, 1); wrapper.orderByDesc(Book::getCreateTime); return bookMapper.selectPage(page, wrapper); }分页插件需要在配置类里注册Configuration public class MybatisPlusConfig { Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }这套写法配合前端表格展示可以做到点击分页、筛选条件不丢失。要注意的一点是查询条件不要直接在LambdaQueryWrapper里写eq之后再like需要组合条件的时候用and(w - ...)包一层否则SQL的逻辑会变成where name like ? and author like ?而不是where name like ? or author like ?。3.3 借书、还书、续借的业务逻辑借书是整个系统里最需要注重数据一致性的模块。核心逻辑是先检查读者状态和图书状态再扣减可借库存最后插入借阅记录。这三个步骤必须放在同一个事务里任何一个失败都要整体回滚。Transactional(rollbackFor Exception.class) public void borrowBook(Long userId, Long bookId, int borrowDays) { // 1. 校验图书是否存在、是否上架、可借库存是否大于0 Book book bookMapper.selectById(bookId); if (book null || book.getStatus() ! 1) { throw new BusinessException(图书不存在或已下架); } if (book.getAvailableStock() 0) { throw new BusinessException(该图书暂无可借库存); } // 2. 校验该读者是否已有未归还的同书记录 Long count borrowRecordMapper.selectCount(new LambdaQueryWrapperBorrowRecord() .eq(BorrowRecord::getUserId, userId) .eq(BorrowRecord::getBookId, bookId) .eq(BorrowRecord::getStatus, 0)); if (count 0) { throw new BusinessException(你已借阅该书且未归还不能重复借阅); } // 3. 扣减库存 book.setAvailableStock(book.getAvailableStock() - 1); bookMapper.updateById(book); // 4. 插入借阅记录 BorrowRecord record new BorrowRecord(); record.setUserId(userId); record.setBookId(bookId); record.setBorrowTime(LocalDateTime.now()); record.setDueTime(LocalDateTime.now().plusDays(borrowDays)); record.setStatus(0); borrowRecordMapper.insert(record); }Transactional这个注解至少值500字篇幅。我见过很多同学写借书逻辑不加事务结果就是库存扣了但记录没插进去或者记录插进去了库存没扣数据对不上只能手动去改数据库。SpringBoot里加事务很简单但要注意调用方式——只能在Service内部通过代理调用才生效同类内部方法direct调用是失效的。还书逻辑正好反过来更新借阅记录状态、计算逾期天数如果超期、把可借库存加回去。同样需要事务。续借的逻辑是在还书之前把due_time往后推一段时间但前提是不能已经逾期。3.4 登录鉴权与拦截器配置登录鉴权我选择了相对轻量的方案HandlerInterceptor Session而不是直接上Spring Security。原因很实在课程设计阶段Security的配置复杂度会淹没业务代码的主体地位论文写起来也会偏题。拦截器配置很简单public class LoginInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Object user request.getSession().getAttribute(loginUser); if (user null) { response.sendRedirect(/login); return false; } return true; } }然后注册到WebMvcConfigurer里按路径区分哪些需要登录、哪些不需要Configuration public class WebMvcConfig implements WebMvcConfigurer { Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LoginInterceptor()) .addPathPatterns(/**) .excludePathPatterns(/login, /register, /css/**, /js/**, /images/**); } }管理员接口的权限校验可以通过判断session里用户的role字段做二次拦截或者用更细粒度的拦截器统一处理。密码存储一定要用BCrypt加密不要存明文。MyBatis-Plus自带BCrypt工具类注册、登录时调一下就行。4. 调试部署实录与常见问题4.1 开发环境配置与application.yml要点我的开发环境如下这套组合经过大量项目验证很稳定JDK 1.8Maven 3.6.3IDEA 2021.x及以上MySQL 8.0SpringBoot 2.7.xapplication.yml的核心配置server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/library_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver thymeleaf: cache: false mybatis-plus: configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: id-type: auto几个配置层面的坑MySQL连接URL里必须带serverTimezoneAsia/Shanghai不然会报时区错误。characterEncodingutf8不要写成utf-8MySQL驱动认utf8。Thymeleaf的cache: false只在开发期有用部署上线后记得改回true否则页面修改不生效的问题会让人很困惑。4.2 常见报错速查与解决思路这个项目我从零搭到部署踩过的坑整理成了一张表你可以直接当字典查。现象根本原因解决办法启动报Port 8080 already in use端口被占用netstat -ano启动报数据库连接失败数据库未启动/密码不对检查MySQL服务、核对账号密码查询数据全是中文乱码数据库字符集不对/连接串没有编码参数建库用utf8mb4连接串加characterEncodingutf8Mapper接口报找不到未加Mapper注解或未扫描包启动类加MapperScan(com.example.library.mapper)页面加载404静态资源放错位置/路径写错HTML放templates目录CSS/JS放static目录分页功能不生效缺少分页插件检查MybatisPlusConfig是否正确注册分页插件Autowired注入报空指针Service没有被Spring管理确认Service实现类加了Service前端提交的日期格式报错类型转换失败Controller接收参数时用DateTimeFormat(pattern yyyy-MM-dd)我最想单独拎出来说的是Mapper扫描问题。如果你用了MyBatis-Plus启动类上一定记得加MapperScan否则100%报Invalid bound statement (not found)。这个错误几乎每个新手都会遇到而且网上答案五花八门其实一句话就能解决。4.3 打包部署与生产环境注意事项开发完成后打包部署也是一门必修课。SpringBoot打包很简单Maven里执行mvn clean package -DskipTests然后在target目录下得到library-0.0.1-SNAPSHOT.jar上传到服务器后执行java -jar library.jar --spring.profiles.activeprod建议把application.yml按环境拆分成application-dev.yml本地开发配置application-prod.yml服务器配置密码、日志级别、数据库地址都不同生产环境里有两个容易被忽略的细节千万不要用root账号直接启动jar包创建一个普通用户来跑避免安全问题。数据库密码不要明文写在项目配置里再打包用环境变量替代password: ${DB_PASSWORD}然后启动命令改成DB_PASSWORDyourpassword java -jar library.jar4.4 论文/设计文档怎么组织这个项目标题里特别提到“带论文文档1万字以上”可见写作文档和写代码同等重要。我整理了一份比较通用的论文大纲你可以参考绪论背景、意义、国内外研究现状、论文结构。相关技术介绍SpringBoot、MyBatis-Plus、Thymeleaf、MySQL。系统分析可行性分析、需求分析、用例分析、业务流程。系统设计总体架构、功能模块设计、数据库设计重点画ER图。系统实现按模块贴代码截图配合文字说明逻辑流程。系统测试功能测试用例表格、测试结果截图。总结与展望。写论文的时候每个功能的实现截图加上核心代码配合一到两段解释凑出来非常容易。重点是ER图和流程图一定要好好画这是老师最爱看的部分。小心踩坑图书管理项目里的六个隐性坑最后分享几个我在做类似项目时总结的隐性坑这些都是常规文档里不会写的。第一图书分类不能做成简单的一级分类。最好做成父子两级比如“文学”下面有“小说”“散文”“诗歌”这样检索和统计维度更丰富写论文的时候也能多画一张分类图。第二借阅记录的due_time字段必须手动设置。不要只在代码里写死30天要把天数做成可配置的万一老师要求不同书籍类型借期不同你改起来只动一个字段就能搞定。第三导出功能值得做。Excel导出借阅记录是很多管理系统的加分项论文里写“本系统支持借阅数据导出”也是一句实打实的功能描述。用EasyExcel或者POI都能实现。第四异常处理要统一。建议写一个全局异常处理器用RestControllerAdvice统一捕获业务异常和系统异常返回统一的JSON格式而不是让500错误页裸奔。这既是开发习惯问题也是论文里“系统健壮性”这一节的素材。第五前端的表单校验和后端的校验都要做。只做前端校验等于没做因为可以绕过前端直接调接口。后端校验用JSR 303NotNull、NotBlank这类注解就够了配置简单还能在论文里写一笔。第六上线之前一定要把日志级别调对。开发期用DEBUG没问题但生产环境用DEBUG会疯狂刷日志磁盘很快就爆了而且性能也会受到影响。建议生产环境设置为INFO只保留关键业务日志。我做这些课程设计类项目最大的体会是不用追求功能多炫而是把一个业务闭环做到位让代码结构干净、表设计合理、文档完整这比堆砌十几个半成品功能都管用。图书管理网站作为SpringBoot最常见的实战项目它背后涉及的CRUD、事务、状态流转、权限控制、分页查询几乎覆盖了后端开发日常80%的工作量。把这套流程吃透后面不管换什么管理系统接需求、建表、写逻辑、部署都会是同样的套路。
返回列表