首页/新闻资讯/正文详情

SSM+MySQL图书管理系统:从框架整合到答辩避坑全攻略

发布时间:2026/9/26 14:36:10 来源:云帆数科 栏目:资讯中心
SSM+MySQL图书管理系统:从框架整合到答辩避坑全攻略
简介这是一套基于SSM和MySQL的图书管理系统完整项目面向JavaWeb期末大作业、课程设计及毕业设计参考也适合刚接触SSM整合的开发者阅读。项目包含全部Java源码、SQL数据库脚本和实验报告代码注释较完整模块划分清晰涵盖图书分类与信息管理、借书还书、读者管理、逾期处理、管理员权限等常用功能下载后按说明简单配置即可运行。资源包共664个文件打包为zip体积约38.28MB包括Java后台业务类、JSP动态页面、CSS/JS前端样式与交互、数据库脚本以及png、gif截图与演示动图便于直观了解系统界面和操作流程目录层次清楚前端页面、后台代码、数据库脚本和文档分区存放检索方便。目前已有357人学习系统界面简洁、操作流畅项目结构规范既可作为课程设计或期末答辩的完整交付物也方便在此基础上扩展二次开发。1. 图书管理系统这个SSMMySQL大作业真正卡人的是这三件事「基于SSMMySQL的图书管理系统」几乎是Java课程设计里出镜率最高的题目配套的源码、数据库脚本和实验报告在答辩季也是被反复找的资源。可同样是这个题目有人一周交差拿满分有人临到答辩还在跟Tomcat报错较劲。区别不在题目本身而在三件事SSM三个框架怎么分工、MySQL表怎么设计、源码和实验报告能不能对得上。它表面是图书增删改查实际考的是Java Web全链路从Maven依赖到Spring容器从MyBatis映射到事务边界每一步都可能被老师追问。这篇笔记把从建库到答辩的完整路径和坑位一次讲清楚新手照着一路跑通有基础的也能在细节上补漏。2. SSMMySQL图书管理系统的技术底牌三层架构怎么分活、四张表怎么设计2.1 Spring、Spring MVC、MyBatis在图书管理系统里各自管什么先别急着写代码。这种题目老师一眼就能看出你是不是真懂SSM。SSM不是三个工具拼在一起而是有明确分工的三层Spring负责对象管理和事务Spring MVC负责接收HTTP请求并路由到ControllerMyBatis负责把Java对象映射成SQL操作。落到图书管理系统里一次「查询所有图书」的请求路径是这样的浏览器发起 /book/list 请求DispatcherServlet先拦下交给BookController的list方法Controller调BookService接口ServiceImpl里注入BookMapperMapper通过BookMapper.xml里的select语句把MySQL的book表查出来再逐层返回最终由视图解析器渲染成JSP页面。这里有个很多新手容易犯的错直接在Controller里写SQL或者调Mapper。SSM的分工里Controller只做参数接收和视图转发业务逻辑必须放在Service层。图书管理系统虽然简单但借书还书要扣库存、要在借阅表插记录这两步必须在一个事务里完成而事务只有放在Service层才能生效。Controller里做事务要么不生效要么事务边界烂成一锅粥。我一般会强调把Service层当成整个系统的核心接口设计好Controller和Mapper都只是它的左右手。也有人问为什么不直接用Spring Boot一句话回答课程设计指定SSM往往就是为了让你把这三个框架的边界讲清楚Spring Boot把这些细节都藏掉了答辩反而不好讲。2.2 四张核心表的MySQL建表设计字段、主外键与借阅状态图书管理系统最少需要四张表管理员表、图书表、读者表、借阅表。管理员表管登录图书表管库存读者表管借书人借阅表是中间表把图书和读者关联起来。少了任何一张系统逻辑就没法闭环。先看图书表和借阅表的建表SQLCREATE TABLE book ( id INT AUTO_INCREMENT PRIMARY KEY COMMENT 图书编号, book_name VARCHAR(100) NOT NULL COMMENT 书名, author VARCHAR(50) DEFAULT NULL COMMENT 作者, publisher VARCHAR(50) DEFAULT NULL COMMENT 出版社, isbn VARCHAR(20) DEFAULT NULL COMMENT ISBN号, total_count INT NOT NULL DEFAULT 1 COMMENT 馆藏总量, remain_count INT NOT NULL DEFAULT 1 COMMENT 当前可借数量, create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 入库时间 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT图书表; CREATE TABLE borrow ( id INT AUTO_INCREMENT PRIMARY KEY, book_id INT NOT NULL COMMENT 图书ID, reader_id INT NOT NULL COMMENT 读者ID, borrow_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 借出时间, return_time DATETIME DEFAULT NULL COMMENT 归还时间, status TINYINT NOT NULL DEFAULT 0 COMMENT 0-借出未还1-已归还, KEY idx_book_id (book_id), KEY idx_reader_id (reader_id), CONSTRAINT fk_borrow_book FOREIGN KEY (book_id) REFERENCES book (id), CONSTRAINT fk_borrow_reader FOREIGN KEY (reader_id) REFERENCES reader (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT借阅表;两个设计点值得说清楚。第一total_count和remain_count分开存而不是每次借还都去统计这样列表页显示剩余数量不用联表查询写起来简单借书时remain_count - 1还书时1。第二借阅状态用TINYINT而不是ENUM或字符串因为后面要扩展「预约中」「逾期」等状态时TINYINT加数字含义就行改ENUM要动表结构。外键约束建议加上虽然性能有损耗但课程设计的数据量感觉不到而老师看到正确的CONSTRAINT会认为你懂关系型数据库的范式设计。字符集统一用utf8mb4它比utf8多支持生僻字和EmojiMySQL 8.0里utf8本身已经是别名但写清楚更保险。2.3 MySQL 8.0和SSM整合的兼容性清单驱动类、时区与连接池标题里写了MySQL但没说版本。现在新装的基本都是MySQL 8.0及以上这也是一堆老教程失效的根源。MySQL 8.0把JDBC驱动类从com.mysql.jdbc.Driver改成了com.mysql.cj.jdbc.Driver老驱动包里的类被移除了。同时8.0对时区敏感连接串里不指定serverTimezone启动就会直接抛时区错误。连接URL建议按下面这个模板写三个参数一个都不能少jdbc:mysql://localhost:3306/library?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8useSSLfalse是因为本地开发不需要加密连接开了反而多一次握手还容易报证书警告serverTimezoneAsia/Shanghai告诉驱动用东八区时间characterEncodingutf8保证中文读写不乱码。连接池方面课程设计我推荐用阿里的Druid而不是c3p0或dbcp原因很实际Druid自带监控页面答辩时打开监控页给老师看连接池的活跃连接数比空口说「我配了连接池」有说服力得多。HikariCP性能确实好但Spring Boot风格的项目才更适合它SSM的XML配置环境下Druid的文档和中文资料都更全。核心参数先用一套保守配置就能跑initialSize5、minIdle5、maxActive20、maxWait10000为什么这么设避坑章节展开讲。3. 把SSM图书管理系统跑起来的完整操作Maven工程、web.xml与Spring配置3.1 创建Maven工程与pom.xml一张依赖清单看清SSM全家桶我见过不少同学用IDEA直接新建Web工程然后手动加jar包最后jar版本冲突到怀疑人生。SSM这种多框架整合项目正确打开方式是Maven依赖版本交给pom管理。前提是本地装好了MySQL 8.0按官方安装教程一路Next就行注意端口3306和root密码要记牢。新建工程时选Maven的webapp骨架然后打开pom.xml把下面这套依赖放进去properties spring.version5.3.9/spring.version mybatis.version3.5.9/mybatis.version /properties dependencies !-- Spring MVC与Spring核心 -- dependency groupIdorg.springframework/groupId artifactIdspring-webmvc/artifactId version${spring.version}/version /dependency !-- 声明式事务依赖 -- dependency groupIdorg.springframework/groupId artifactIdspring-jdbc/artifactId version${spring.version}/version /dependency !-- MyBatis核心与Spring桥接包 -- dependency groupIdorg.mybatis/groupId artifactIdmybatis/artifactId version${mybatis.version}/version /dependency dependency groupIdorg.mybatis/groupId artifactIdmybatis-spring/artifactId version2.1.3/version /dependency !-- MySQL 8.0驱动与Druid连接池 -- dependency groupIdcom.mysql/groupId artifactIdmysql-connector-java/artifactId version8.0.28/version /dependency dependency groupIdcom.alibaba/groupId artifactIddruid/artifactId version1.2.8/version /dependency !-- JSP、JSTL与Servlet API -- dependency groupIdjavax.servlet/groupId artifactIdjstl/artifactId version1.2/version /dependency dependency groupIdjavax.servlet/groupId artifactIdjavax.servlet-api/artifactId version4.0.1/version scopeprovided/scope /dependency /dependencies这套依赖里有几个关键点。mybatis-spring是必装的桥接包没有它Spring没法管理MyBatis的SqlSessionspring-jdbc提供事务管理器DataSourceTransactionManager声明式事务就靠它servlet-api的scope必须是provided因为Tomcat自带这个jar强行打包进去会和容器冲突。版本选择建议Spring 5.x配MyBatis 3.5.x这两个系列配合成熟网上能查到的报错也都集中在这套组合上。Spring 6要求JDK 17起步课程设计的机器未必装得起没必要给自己加难度。3.2 web.xml里的三件套DispatcherServlet、监听器与编码过滤器SSM整合最容易出问题的地方在web.xml因为要同时启动Spring根容器和Spring MVC子容器还得让字符编码过滤器在一开始就生效。下面是最小可用的配置web-app xmlnshttp://xmlns.jcp.org/xml/ns/javaee xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd version4.0 !-- 1. 字符编码过滤器必须放在所有过滤器最前面 -- filter filter-nameencodingFilter/filter-name filter-classorg.springframework.web.filter.CharacterEncodingFilter/filter-class init-param param-nameencoding/param-name param-valueUTF-8/param-value /init-param init-param param-nameforceEncoding/param-name param-valuetrue/param-value /init-param /filter filter-mapping filter-nameencodingFilter/filter-name url-pattern/*/url-pattern /filter-mapping !-- 2. Spring根容器监听器 -- listener listener-classorg.springframework.web.context.ContextLoaderListener/listener-class /listener context-param param-namecontextConfigLocation/param-name param-valueclasspath:spring/applicationContext.xml/param-value /context-param !-- 3. Spring MVC前端控制器 -- servlet servlet-namespringmvc/servlet-name servlet-classorg.springframework.web.servlet.DispatcherServlet/servlet-class init-param param-namecontextConfigLocation/param-name param-valueclasspath:spring/spring-mvc.xml/param-value /init-param load-on-startup1/load-on-startup /servlet servlet-mapping servlet-namespringmvc/servlet-name url-pattern//url-pattern /servlet-mapping /web-app三个点解释一下。ContextLoaderListener负责加载Spring根容器里面放数据源、事务管理器、MyBatis的SqlSessionFactory这些全局BeanDispatcherServlet的contextConfigLocation指向spring-mvc.xml只扫描Controller。这种父子容器分离的意义在于Controller能调用Service但根容器里的Bean不会反过来依赖MVC层的Bean边界清楚。forceEncoding设为true很关键它强制请求和响应都走UTF-8不然只设置encoding时如果请求头里带了其他charset就会覆盖失败。url-pattern用/而不是*.do这样REST风格路径直接可用JSP页面也能交给容器处理不会被DispatcherServlet拦掉。3.3 数据源与MyBatis整合Druid连接池参数和SqlSessionFactoryBeanapplicationContext.xml里要干三件事配数据源、配事务管理器、把MyBatis交给Spring管理。下面是核心片段?xml version1.0 encodingUTF-8? beans xmlnshttp://www.springframework.org/schema/beans xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xmlns:txhttp://www.springframework.org/schema/tx xsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd !-- 数据源Druid连接池 -- bean iddataSource classcom.alibaba.druid.pool.DruidDataSource property namedriverClassName valuecom.mysql.cj.jdbc.Driver/ property nameurl valuejdbc:mysql://localhost:3306/library?useSSLfalseamp;serverTimezoneAsia/Shanghaiamp;characterEncodingutf8/ property nameusername valueroot/ property namepassword value你的密码/ property nameinitialSize value5/ property nameminIdle value5/ property namemaxActive value20/ property namemaxWait value10000/ /bean !-- 事务管理器 -- bean idtransactionManager classorg.springframework.jdbc.datasource.DataSourceTransactionManager property namedataSource refdataSource/ /bean tx:annotation-driven transaction-managertransactionManager/ !-- MyBatis整合 -- bean idsqlSessionFactory classorg.mybatis.spring.SqlSessionFactoryBean property namedataSource refdataSource/ property namemapperLocations valueclasspath:mapper/*.xml/ property nametypeAliasesPackage valuecom.library.entity/ /bean bean classorg.mybatis.spring.mapper.MapperScannerConfigurer property namebasePackage valuecom.library.mapper/ /bean /beans注意URL里的在XML里必须写成amp;这是XML转义规则漏了直接报解析错误。mapperLocations告诉MyBatis去哪找SQL映射文件约定放classpath:mapper/目录下typeAliasesPackage设置后Mapper里写返回值类型可以简写成Book而不是全限定名com.library.entity.Book。MapperScannerConfigurer把com.library.mapper包下的接口全部扫描成Spring BeanService里直接Autowired就能注入Mapper省掉手写实现类。spring-mvc.xml这边相对薄核心是组件扫描加注解驱动加视图解析器放在classpath:spring/spring-mvc.xmlcontext:component-scan base-packagecom.library.controller/ mvc:annotation-driven/ bean classorg.springframework.web.servlet.view.InternalResourceViewResolver property nameprefix value/WEB-INF/jsp// property namesuffix value.jsp/ /bean组件扫描的base-package要指向com.library.controller千万别把com.library整个包扫了否则根容器和子容器扫描范围重叠会出现Bean定义冲突启动时不报错但运行期行为诡异这种黑匣子问题最折磨人。mvc:annotation-driven必须加它负责注册RequestMappingHandlerMapping和JSON转换器等一堆默认组件漏掉的话Controller和ResponseBody直接不生效。4. 图书管理系统的核心代码CRUD、借阅归还与登录拦截怎么落地4.1 图书模块的增删改查Mapper接口、XML映射与Service事务图书管理系统的核心就是图书的增删改查这也是「数据库增删改查」在Java Web里的标准形态Controller接收参数Service组织业务Mapper执行SQL。先写Mapper接口package com.library.mapper; public interface BookMapper { ListBook selectAll(); Book selectById(Param(id) Integer id); int insert(Book book); int update(Book book); int deleteById(Param(id) Integer id); ListBook searchByKeyword(Param(keyword) String keyword); }接口只是声明真正的SQL写在同名的XML映射文件里。注意Param(id)这个注解MyBatis传单个简单类型参数时XML里的#{id}必须和Param指定的名字一致不然运行期抛BindingException。对应的BookMapper.xml核心片段insert idinsert parameterTypecom.library.entity.Book useGeneratedKeystrue keyPropertyid INSERT INTO book (book_name, author, publisher, isbn, total_count, remain_count) VALUES (#{bookName}, #{author}, #{publisher}, #{isbn}, #{totalCount}, #{remainCount}) /insert update idupdate parameterTypecom.library.entity.Book UPDATE book SET book_name #{bookName}, author #{author}, publisher #{publisher}, isbn #{isbn}, total_count #{totalCount}, remain_count #{remainCount} WHERE id #{id} /update delete iddeleteById parameterTypeint DELETE FROM book WHERE id #{id} /delete select idsearchByKeyword resultTypecom.library.entity.Book SELECT * FROM book WHERE book_name LIKE CONCAT(%, #{keyword}, %) OR author LIKE CONCAT(%, #{keyword}, %) /selectuseGeneratedKeystrue配合keyPropertyid会在insert执行后把数据库的自增主键回填到Java对象的id字段。很多同学没写这两个属性插入后拿不到主键后面做借阅记录时外键就填不进去这是高频踩坑点避坑章节还会展开。搜索用LIKE CONCAT(%, #{keyword}, %)而不是%${keyword}%前者是预编译占位符防SQL注入后者是字符串拼接课程设计虽然风险小但这个习惯得从一开始就对。update语句里total_count改动的同时remain_count没有跟着处理会出现可借数量大于馆藏总量的脏数据我习惯在Service层强制校验修改总量时计算差值同步调整remain_count禁止把总量改得比已借出数量还小。Service层用接口加实现类的结构方法上加TransactionalService public class BookServiceImpl implements BookService { Autowired private BookMapper bookMapper; Override Transactional(rollbackFor Exception.class) public boolean addBook(Book book) { if (book.getTotalCount() null || book.getTotalCount() 0) { throw new IllegalArgumentException(馆藏总量必须大于0); } // 新书入库时可借数量等于总数量 book.setRemainCount(book.getTotalCount()); return bookMapper.insert(book) 0; } }rollbackFor Exception.class表示任何异常都回滚事务。不加的话Spring默认只在RuntimeException时回滚checked exception不会回滚图书入库中途出问题数据库就留半截数据。这里抛IllegalArgumentException属于非受检异常配合rollbackFor双保险。Autowired按类型注入如果同类型有多个实现Bean改成Qualifier(bookServiceImpl)指定名字。4.2 借阅归还的业务逻辑状态字段迁移与事务边界借书比单纯增删改查多一层业务规则校验库存、扣减库存、插入借阅记录三步必须在一个事务里完成否则会出现「库存扣了但借阅记录没插入」的事故。常见做法是把状态迁移放在Service层借阅表用status字段标记借出还是已还而不是物理删除记录这样借阅历史可追溯老师问「怎么查某本书谁借过」直接SQL按book_id过滤就行。Override Transactional(rollbackFor Exception.class) public boolean borrowBook(Integer bookId, Integer readerId) { // 1. 查图书校验库存 Book book bookMapper.selectById(bookId); if (book null || book.getRemainCount() 0) { throw new BusinessException(图书不存在或库存不足); } // 2. 扣减可借数量返回影响行数 int rows bookMapper.decreaseRemainCount(bookId); if (rows ! 1) { throw new BusinessException(扣减库存失败操作已回滚); } // 3. 插入借阅记录status0 表示借出未还 Borrow borrow new Borrow(); borrow.setBookId(bookId); borrow.setReaderId(readerId); borrow.setStatus(0); return borrowMapper.insert(borrow) 0; }为什么扣减库存要先查再更新而不是直接remain_count - 1因为直接减在库存为0时会造成负数。这里用了一个并发控制的小技巧decreaseRemainCount的SQL里带条件WHERE id #{id} AND remain_count 0如果影响行数不是1说明并发下有人抢先借走了最后一本事务直接回滚。这种「先检查再更新并用影响行数做并发判断」的方式是课程设计这个级别最可靠的做法也够你在答辩时讲清楚为什么不用select加锁。这个项目里业务逻辑放Service层用Java事务就够了不需要写MySQL存储过程但如果学有余力把借阅扣库存写成一个带事务的存储过程再在报告里对比两种实现的优劣很容易拿加分。update iddecreaseRemainCount UPDATE book SET remain_count remain_count - 1 WHERE id #{id} AND remain_count 0 /update还书就是对称操作把borrow记录的status置为1同时remain_count 1。注意归还SQL要带AND status 0条件防止同一本书被重复归还两次这是状态机设计里最常见的边界校验。归还时间的MySQL update语法这样写UPDATE borrow SET status 1, return_time NOW() WHERE id #{borrowId} AND status 0NOW()取数据库服务器当前时间比在Java里new Date()再传参更省事也避免应用服务器和数据库服务器时钟不一致。4.3 登录与权限拦截用Spring MVC拦截器做Session校验图书管理系统需要区分管理员和读者但课程设计级别的权限用Spring Security太重配置复杂还容易把新手绕晕。常见做法是登录后把用户对象放进Session再用Spring MVC的HandlerInterceptor统一拦截需要登录的路径一个拦截器加一个方法就能覆盖整个系统的权限控制。public class LoginInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 如果Handler是Controller方法才拦截静态资源放行 if (!(handler instanceof HandlerMethod)) { return true; } Object loginUser request.getSession().getAttribute(loginUser); if (loginUser null) { // 未登录重定向到登录页 response.sendRedirect(request.getContextPath() /login); return false; } return true; } }拦截器里先判断handler instanceof HandlerMethod是必要的因为url-pattern配成/后JSP、静态资源也会走DispatcherServlet不加这个判断会出现「登录页面的CSS和图片全被拦了」的怪现象。配置上只需要在spring-mvc.xml里注册拦截路径mvc:interceptors mvc:interceptor mvc:mapping path/book/**/ mvc:mapping path/borrow/**/ mvc:exclude-mapping path/login/ mvc:exclude-mapping path/css/**/ mvc:exclude-mapping path/js/**/ bean classcom.library.interceptor.LoginInterceptor/ /mvc:interceptor /mvc:interceptorsexclude-mapping把登录接口和静态资源放行其余与图书、借阅相关的路径全部走拦截。有同学会把判断逻辑写进每个Controller方法里反复复制代码又长又容易漏方法拦截器一处配置、全局生效属于这个项目里性价比最高的设计决策。区分管理员和读者时可以在Session里存一个role字段拦截器里按角色放行或拒绝比如/admin/**路径只允许role1访问比再造一套权限框架实在得多。5. SSM图书管理系统避坑指南5个让大作业当场翻车的实测问题这一章把SSMMySQL整合期间实际遇到、也看别人反复遇到的5个问题列全每条按现象、原因、解决展开照着排查能省下大半天。5.1 MySQL 8.0驱动类找不到或时区报错现象应用启动或第一次访问数据库时报java.lang.ClassNotFoundException: com.mysql.jdbc.Driver或者报The server timezone value Öйú±ê׼ʱ¼ä is unrecognizedJSP页面白屏Tomcat日志里全是SQLException。如果连接都建立不起来还会看到error 2002 (HY000): Cant connect to local MySQL server through socket /tmp/mysql.sock那是因为MySQL服务根本没启动跟代码没关系。原因MySQL Connector/J从8.0开始把驱动类改名为com.mysql.cj.jdbc.Driver老名字从驱动包里彻底移除同时8.0的驱动强制要求指定时区不指定就按服务器本地时区解析中文系统报出来的时区字符串驱动不认识直接抛异常。这也是大量老教程失效的根本原因属于典型的血泪经验。解决先确认MySQL服务在跑然后驱动类名换成com.mysql.cj.jdbc.DriverURL用下面的格式三个参数缺一不可jdbc:mysql://localhost:3306/library?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8改完在IDEA里点一下Maven面板的刷新按钮确认依赖树里只剩一个版本的mysql-connector-java避免5.x和8.x混着传。5.2 连接池参数拍脑袋配置并发一上来就报获取连接超时现象本地单机测试一切正常答辩演示时老师让你开两个浏览器同时操作或者同学一起访问页面卡住日志刷wait millis 10000, active 20, maxActive 20Druid报GetConnectionTimeoutException。原因连接池的maxActive配得太大或太小都有问题。太小了并发一高连接不够用太大了MySQL的max_connections默认只有151应用先占满连接MySQL自己反而不够分。另外最常见的隐藏原因是代码里查完数据没关连接Druid的活跃连接数只增不减最终池子被耗尽。Spring管理下的MyBatis连接是框架负责回收的但手写JDBC而不关Connection、Statement、ResultSet这些都会泄漏连接。解决课程设计级别用这套起步参数不会错initialSize5、minIdle5、maxActive20、maxWait10000超时10秒足够让用户感知到系统忙而不会无限卡死。MySQL那边用SHOW VARIABLES LIKE max_connections确认上限Druid的maxActive建议小于这个值的一半。排查泄漏可以启动项目后访问Druid监控页看「连接池-活跃连接数」是否随每次查询递增不降如果是优先查自己手写的JDBC代码。提示Druid监控页需要在web.xml注册StatViewServlet才能访问答辩时用完记得给监控页加上登录账号密码再演示。5.3 MyBatis插入后拿不到自增主键useGeneratedKeys没写对现象bookMapper.insert(book)执行成功数据库里id字段也正常自增了但代码里book.getId()返回null或者0紧接着拿这个id去插借阅表外键全是null。原因MyBatis默认不会把数据库生成的主键回填到实体对象必须显式开启useGeneratedKeys并指定keyProperty。最常见的错误是只写了useGeneratedKeystrue忘了keyProperty或者keyProperty写成了bookId这种不存在的属性名MyBatis按属性名反射回填失败后静默忽略看起来就像没生效。解决XML里insert标签补齐两个属性insert idinsert parameterTypecom.library.entity.Book useGeneratedKeystrue keyPropertyid INSERT INTO book (book_name, author, publisher, isbn, total_count, remain_count) VALUES (#{bookName}, #{author}, #{publisher}, #{isbn}, #{totalCount}, #{remainCount}) /insertkeyProperty对应Java实体里的字段名keyColumn对应数据库列名大多数情况写keyPropertyid就够了。另外确认实体类id字段有setterMyBatis是靠setter反射回填的Lombok的Data可以但别漏。改完重启项目insert后加一行日志打印book.getId()验证。5.4 中文乱码JSP、过滤器、JDBC URL三层的字符集不一致现象页面显示中文正常但从表单提交到数据库后存进去的是???或者反过来库里是好的、JSP页面上全是乱码。原因乱码是三层字符集不一致叠加出来的。第一层JSP页面本身的pageEncoding没设UTF-8第二层web.xml里缺少CharacterEncodingFilter请求体的编码不被强转第三层JDBC URL里没带characterEncodingutf8Connector/J用默认编码和MySQL通信。三个环节哪怕只有一个漏了中文就保不住。还要注意MySQL建库时不指定字符集会用服务器默认的latin1那是最坑的一种库里直接存不进中文。解决三层全部显式指定UTF-8。JSP页面顶部写% page contentTypetext/html;charsetUTF-8 pageEncodingUTF-8 %web.xml里配CharacterEncodingFilter并把forceEncoding设为trueJDBC URL带characterEncodingutf8。最后用下面的SQL确认库、表两级字符集SHOW CREATE DATABASE library; SHOW CREATE TABLE book;如果建库时没用utf8mb4执行ALTER DATABASE library DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;补救。已存在的???数据不会自动变回来只能删掉重新录入所以建库这步一定要一步到位别留后悔药。5.5 实验报告与源码对不上答辩被追问时的穿帮现场现象报告里的ER图画了3张表代码里实际有5张报告里的功能截图显示某本书库存1000现场打开系统同一本书只有3老师翻报告里的核心代码和你课堂演示的工程文件对不上。这种属于「大作业看起来很满一问就露」的典型翻车。原因实验报告是套用别人的模板改的改的时候只换了题目和截图没对照自己源码逐项核对。特别是数据字典、ER图、测试数据这三样套模板最容易对不齐。解决交报告前对照源码做一轮核对清单逐项打勾ER图实体数量必须等于数据库实际表数量数据字典的字段名、类型、默认值和SHOW CREATE TABLE输出一致报告的源码片段直接从自己工程复制而不是凭记忆手打测试数据在本地真实插入跑一遍功能再截图截到的库存数字、时间都是真实产生的。报告里附上可复现说明MySQL版本、JDK版本、Tomcat版本、导入数据库脚本的步骤老师按步骤能跑起来这比任何修饰都有用。还有个小习惯每次改完代码顺手把对应截图更新掉别攒到最后一天集中补那时候状态最差最容易对不上。6. 答辩前的最后一遍自查用一套演示路径给图书管理系统加分功能写完了报告交了答辩时怎么让自己的系统看起来比别人的完整我的习惯是不碰键盘纯靠一套固定路径演示每步都预设一个结果现场按这个顺序走基本不会翻车顺序操作预期结果1从登录页输入管理员账号跳转后台首页Session写入用户2新增一本图书填写完整字段列表页出现新书可借数量等于馆藏总量3用读者账号借阅这本书图书可借数量减1借阅记录生成4再次借阅同一本提示库存不足系统不产生脏数据5归还图书可借数量恢复借阅记录状态变为已归还6查询借阅历史能按读者或书名过滤出完整记录演示的同时两个加分细节值得做。第一打开Druid监控页放在旁边操作一步切过去看一眼连接池活跃连接数的变化这比嘴上说「我用了连接池」实在得多第二故意演示一次「借书时模拟库存不足」然后当众打开Navicat或MySQL Workbench查一下remain_count和borrow表证明事务回滚真的生效了——老师对能演示边界处理的学生的印象分极高。还有一个我踩过坑才养成的习惯所有数据库操作脚本、建表SQL、测试数据单独存一个sql文件放在项目根目录的sql/文件夹下。答辩老师如果要求现场还原环境你只需要导入这一个文件如果要求看数据库设计打开文件就能讲解字段含义。这份自觉比临时翻文件夹找脚本从容得多。图书管理系统的SSMMySQL路线就这么长环境、建库、框架整合、业务代码、报告核对每一步都不玄学卡住的地方基本都是上面这几个老问题。希望帮到你做作业时顺手把你自己的踩坑记录也写进报告那部分往往是老师最愿意看的东西。本文还有配套的精品资源点击获取

相关推荐

从AI安全审计到Skill工程化:打造可复用的代码审计工作流
从AI安全审计到Skill工程化:打造可复用的代码审计工作流

1. 为什么单独做一套安全审计Skill,核心需求拆解这几年跟AI编码工具打交道多了,我养成一个习惯:凡是重复性的技术活,先想能不能沉淀成一个skill。原因很简单,通用对话模型虽然有编程能力,但让它做一次像样的… · 2026/9/26 14:36:10

实测阿里开源AI代码评审工具:五个真实缺陷全检出
实测阿里开源AI代码评审工具:五个真实缺陷全检出

1. 为什么我会拿五个真实缺陷去试探这个评审工具代码评审这件事,做过团队协作的人都有体会:写得再仔细的 PR,也总有人能挑出你没想到的问题。但人不是机器,评审者会累、会走神、会因为"这个作者我熟"而放松标准。所以当… · 2026/9/26 14:36:10

LangGraph多智能体实战:角色分工与协作机制详解
LangGraph多智能体实战:角色分工与协作机制详解

1. 从"一个Agent打天下"到"一群Agent各司其职"的认知转变如果你最近在折腾Agent开发,大概率经历过这样一个阶段:一开始觉得单个Agent挺能打,给它一个系统提示词,挂上几个工具,就能完成搜索、总结、… · 2026/9/26 14:36:04

从TransUnet到SAM式交互:医学图像分割的提示引导改进实践
从TransUnet到SAM式交互:医学图像分割的提示引导改进实践

简介:面向医学图像分割场景,这份基于TransUnet架构的交互式分割系统,融合类似SAM的提示框引导机制,适用于医疗影像标注、病灶区域修正等需要人机协同的细分任务。代码按数据、训练、推理三模块组织:dataset.py通过bbox… · 2026/9/26 15:13:58

乱堆物料检测数据集VOC+YOLO双格式详解:从YOLOv8训练到避坑实战
乱堆物料检测数据集VOC+YOLO双格式详解:从YOLOv8训练到避坑实战

简介:乱堆物料检测数据集专为目标检测算法训练与评测设计,面向从事计算机视觉、智慧工地、港口堆场等场景的AI开发者和研究人员,有效解决了公共数据集中乱堆物料样本稀缺、标注格式不统一的问题。数据集采集了1143张真实场景图片,… · 2026/9/26 15:13:58

多Provider路由、RAG与Agent编排:AI应用三层架构设计实战
多Provider路由、RAG与Agent编排:AI应用三层架构设计实战

1. 从单点调用到多 Provider 路由:为什么一开始就要把口子留出来做 AI 应用最怕的一件事,就是第一版代码里把某一家模型服务商的 SDK 直接写死在业务逻辑里。我见过太多项目,最开始只是调一个对话接口,图省事,client.c… · 2026/9/26 15:13:58

旧系统零改造接入AI:MCP协议适配层实战指南
旧系统零改造接入AI:MCP协议适配层实战指南

1. 项目概述:为什么老系统不能“推倒重来”,而必须“带病上岗”AI?在银行核心账务系统还在跑 Windows Server 2016 SQL Server 2012 的机房里,在制造业 ERP 仍依赖 VB6 客户端 Oracle 9i 数据库的车间终端上,在政务审… · 2026/9/26 15:13:52

OpenClaw 安装手册:办公自动化工具报错统一处理方案(含安装包与 TaoToken 配置)
OpenClaw 安装手册:办公自动化工具报错统一处理方案(含安装包与 TaoToken 配置)

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 15:13:52

集装箱损伤检测数据集:工业质检落地的可信起点
集装箱损伤检测数据集:工业质检落地的可信起点

简介:本资源是面向物流智能化与工业视觉算法研发者的多类别目标检测数据集,聚焦货运箱体识别与表面损坏状态判别两大核心任务,适用于YOLO系列模型训练及实例分割算法验证。数据集共855张真实物流场景图像,配套855份YOLO格式标注文… · 2026/9/26 15:13:52

数据库课后习题答案别硬背:当测试用例集刷,效率翻倍
数据库课后习题答案别硬背:当测试用例集刷,效率翻倍

简介:万常选版《数据库原理与设计》课后习题答案资源,覆盖第2至6章及第9章,适合正在学习关系模型、数据库建模、关系数据理论与模式求精的本科生、自学者作为复习与自测材料。压缩包共7个文件,含3个doc参考答案、2个sql示例脚本、… · 2026/9/26 0:00:21

OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置
OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 0:00:40

向下兼容与向上兼容:接口设计中的兼容性策略与工程实践
向下兼容与向上兼容:接口设计中的兼容性策略与工程实践

一次版本升级事故,是很多团队绕不过去的坎。线上环境里,服务端明明已经上线了新版接口,老的移动端还在照着旧文档传参数。请求一到网关,校验直接拒绝,用户操作失败,客服群炸了锅,开发群里开始互… · 2026/9/26 0:00:46

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码