每年到了毕设选题的季节,图书管理系统这个题目就会准时出现在各大高校的选题列表里,而且十有八九带着“SSM”这个标签。很多人第一反应是“这题目也太老了”,但作为带过不少实习生、也参与过毕业设计评审的人,我想说一句:SSM图书管理系统恰恰是Java方向最值得认真对待的入门级全栈项目之一。它技术栈经典、业务逻辑清晰、扩展空间大,更重要的是,它能帮你把Spring、SpringMVC、MyBatis这三座大山真正融会贯通,而不是停留在背面试题的层面。
这篇文章我就以“2026精选课题-基于SSM图书管理系统的设计与实现”为蓝本,从选题拆解、技术选型逻辑、数据库设计、核心功能实现到答辩准备,完整还原一个能拿优秀毕设的SSM图书管理系统应该怎么做。内容全部基于我实际开发和指导项目中用到的方案,不是教科书式的照搬,每一步我都会解释为什么这么做,踩过的坑也会一并交代。
1. 课题拆解:图书管理系统到底在考什么
1.1 课题来源与适用人群
先把这个题目的本质看清楚。图书管理系统属于典型的信息管理系统(MIS)范畴,它的核心不是算法难度,而是业务逻辑的完整性和工程规范的熟练度。这类题目在毕业设计里占据主流位置,是因为它覆盖了Web开发的基本功:用户登录认证、数据的增删改查(CRUD)、列表分页、条件检索、表单校验、关联表设计。
适用人群非常广:
- Java方向的大四学生,需要完成毕设
- 正在学习SSM框架、想找一个完整项目练手的人
- 准备进入Java后端开发岗位、需要项目经验支撑简历的求职者
如果你属于以上三类人,这个项目都是一块很好的“敲门砖”。它不要求你有多深的算法功底,但要求你踏踏实实把每个模块写清楚、跑通,并且能解释清楚每一行代码背后的设计意图。
1.2 评分维度决定了你要重点投入什么
经历过毕设答辩的人都知道,评委老师看重的不是你用了多新的技术,而是你对项目的掌控力。我根据实际评审经验,总结出这套评分权重供你参考:
| 评分维度 | 权重 | 核心考察点 |
|---|---|---|
| 系统功能完整性 | 30% | 登录、图书管理、借阅/归还流程是否闭环 |
| 数据库设计合理性 | 20% | 表结构是否规范、关联关系是否正确 |
| 代码规范与架构理解 | 20% | 三层架构是否清晰、命名是否规范 |
| 论文撰写质量 | 15% | 需求分析是否充分、流程图是否完整 |
| 答辩表现 | 15% | 能否讲清楚设计思路、应对提问 |
看到这个表你就明白了:功能跑通只是基础,数据库设计和架构理解才是拉开差距的地方。很多学生把精力全花在写页面上,结果被问到“为什么借阅记录表要单独建一张”就哑口无言,这就亏大了。
2. 技术选型解析:为什么是SSM而不是其他组合
2.1 SSM框架的生态地位与组合逻辑
SSM是Spring + SpringMVC + MyBatis的缩写。虽然现在Spring Boot已经大行其道,但SSM依然是国内高校Java教学的主流组合,而且企业内部大量遗留系统还在使用这套技术栈。从学习梯度上讲,SSM让你能看清Spring的IOC和AOP原理、SpringMVC的请求流转过程、MyBatis的SQL映射机制——这些底层认知在Spring Boot时代一样通用,只是被自动配置掩盖了。
为什么这三个框架组合在一起是合理的?
- Spring负责对象管理和事务控制,是整个应用的“骨架”
- SpringMVC负责HTTP请求的接收与分发,是Web层的“门卫”
- MyBatis负责数据库操作,通过Mapper接口和XML映射文件将SQL与Java方法绑定,是数据访问层的“管道”
三者各司其职,正好对应三层架构中的Service层、Controller层和Dao层。
2.2 为什么选了MyBatis而不是Hibernate
这里需要解释一下我的选择逻辑。Hibernate也是经典的ORM框架,但在图书管理系统这种SQL逻辑并不复杂的场景下,MyBatis的优势更明显:SQL由开发者自己控制,调试时直接看SQL语句就能定位问题,学习曲线也更平缓。
我见过不少学生用Hibernate时出现“懒加载异常”“N+1查询”这类经典问题,排查起来非常痛苦。MyBatis的一大好处是SQL和Java代码分离,出了问题先查看mapper/XML里的SQL,再排查Java代码,定位路径清晰得多。
如果你用的是MyBatis,还有一个隐藏加分项:你可以把MyBatis的<where>标签、<foreach>标签、动态SQL能力写进论文里,评委老师会认为你真正掌握了这个框架的精髓。
2.3 环境版本选择的一个关键建议
SSM框架的版本搭配是个容易让人头大的问题,我整理了一个经过验证的稳定组合:
| 组件 | 版本 | 说明 |
|---|---|---|
| JDK | 1.8 | 兼容性最好的版本,不建议用更高版本(某些框架兼容性有坑) |
| Maven | 3.6.x | 依赖管理和项目构建 |
| Spring | 5.2.x | 稳定版,与JDK8完美兼容 |
| SpringMVC | 5.2.x | 跟随Spring版本 |
| MyBatis | 3.5.x | 稳定版 |
| MyBatis-Spring | 2.0.x | 关键桥接组件,版本必须匹配 |
| MySQL | 5.7或8.0 | 5.7最安全,8.0注意驱动差异 |
| Tomcat | 8.5或9.0 | 9.0配合JDK8没有问题 |
| IDEA | 任意较新版本 | 建议使用Ultimate版,内置数据库工具 |
这里面最常出问题的就是MyBatis-Spring桥接包的版本,如果它跟你手里的Spring版本不匹配,启动时就会出现各种诡异的BeanCreationException。我的建议是:直接用Spring 5.2.x + MyBatis-Spring 2.0.x这个组合,我自己在多个项目里验证过,没有兼容性问题。
3. 系统设计思路与数据库建模
3.1 角色与功能模块梳理
图书管理系统的用户角色一般分两种:管理员和普通读者(读者)。如果还要继续扩展,可以增加图书管理员角色,与超级管理员区分开。但作为毕业设计,两类角色已经足够了,再多就属于给自己挖坑。
先列一下功能清单:
管理员端:
- 登录/注销
- 图书信息管理:新增、编辑、删除、查询
- 图书分类管理:分类的增删改查
- 读者管理:添加读者、禁用/启用账号、重置密码
- 借阅管理:办理借书、办理还书、查看借阅记录
- 统计看板:馆藏总数、借出数量、逾期记录
读者端:
- 注册与登录
- 浏览图书列表
- 按书名、作者、ISBN检索图书
- 查看个人借阅记录、当前借阅的图书
- 在线提交借阅申请(可选,看你想做到什么程度)
这里要提醒一点:不要把功能堆得太多。有些同学为了显得“工作量足”,硬加了在线支付、消息通知、实时聊天等功能,结果代码量翻倍,bug也翻倍,答辩时反而讲不清楚。把借还书闭环做好做透,深度比广度重要得多。
3.2 数据库表结构设计详解
数据库是这类项目最容易拿分也最容易翻车的地方。我见过很多学生的表设计,要么缺少主外键关系,要么字段类型乱用,要么没有索引。下面给出一个经过实际验证的表设计方案。
核心表一共5张:
用户表(sys_user)
| 字段名 | 类型 | 说明 |
|---|---|---|
| id | int | 主键,自增 |
| username | varchar(50) | 登录名,唯一 |
| password | varchar(100) | 存储加密后的密码,不要存明文 |
| real_name | varchar(50) | 真实姓名 |
| role | tinyint | 角色:1管理员,2读者 |
| status | tinyint | 状态:1启用,0禁用 |
| create_time | datetime | 创建时间 |
关于密码加密这一点,我见到太多学生直接把明文密码怼进数据库,这是让评委瞬间皱眉的地方。哪怕你用的是简单的MD5加盐,也说明你有安全意识。Base64编码不算加密,别闹。合理做法是在Java端用MD5或者SHA-256加盐哈希后再入库,答辩时讲出“即使数据库泄露,也不会直接暴露用户明文密码”这句话,能明显加分。
图书分类表(book_category)
| 字段名 | 类型 | 说明 |
|---|---|---|
| id | int | 主键,自增 |
| category_name | varchar(50) | 分类名称,如“计算机” |
| sort_order | int | 排序权重 |
| create_time | datetime | 创建时间 |
图书信息表(book_info)
| 字段名 | 类型 | 说明 |
|---|---|---|
| id | int | 主键,自增 |
| isbn | varchar(20) | ISBN编号 |
| book_name | varchar(100) | 书名 |
| author | varchar(50) | 作者 |
| publisher | varchar(100) | 出版社 |
| publish_date | date | 出版日期 |
| category_id | int | 外键,关联图书分类表 |
| total_stock | int | 总馆藏数量 |
| available_stock | int | 可借数量 |
| location | varchar(50) | 馆藏位置,如“A区3排” |
| status | tinyint | 状态:1上架,0下架 |
| create_time | datetime | 创建时间 |
这里有一个容易忽略的细节:total_stock和available_stock为什么要分开存?因为每次借书/还书时只需要维护available_stock,不需要去算总库存,性能和逻辑都更清晰。库存字段用int而非varchar,这个大家都懂但偶尔有人犯错。
借阅记录表(borrow_record)
| 字段名 | 类型 | 说明 |
|---|---|---|
| id | int | 主键,自增 |
| user_id | int | 外键,借阅人 |
| book_id | int | 外键,借阅的图书 |
| borrow_time | datetime | 借书时间 |
| due_time | datetime | 应还时间,一般借期30天 |
| return_time | datetime | 实际归还时间,NULL表示未还 |
| status | tinyint | 状态:1借出,2已还,3逾期 |
借阅记录表是整个系统的核心,它记录了一次完整的借还生命周期。注意这里不要冗余图书名称和用户名,通过外键关联去查询即可。表设计规范是:能通过关联查出来的信息,不要冗余存储。
3.3 关联关系设计的两个关键点
第一,book_info.category_id要设置外键约束,但删除分类时要先处理该类下的图书——要么限制删除,要么将其置为未分类。我的建议是限制删除,否则会出现“有书引用了不存在的分类”这种脏数据。
第二,borrow_record表在删除用户或删除图书时需要决策:是物理删除还是逻辑删除?我的方案是:用户和图书都做逻辑删除(用status字段标记),但借阅记录只增不改不删——保留历史痕迹。这样论文里可以写“保证操作的可追溯性”,又是个专业加分点。
4. 核心功能模块的实现要点
4.1 登录认证与拦截器设计
登录模块看起来简单,但里面有几个值得细做的点。首先,密码不能用明文比对,要用加密后的密文比对。其次,登录成功后把用户信息存入Session,然后通过SpringMVC的HandlerInterceptor实现请求拦截,未登录用户访问受保护资源时自动跳转到登录页。
拦截器实现的核心思路很清晰:新建一个拦截器类,重写preHandle方法,在其中判断Session里是否存在已登录用户;在SpringMVC配置文件中注册该拦截器,并设置放行路径(比如登录接口、静态资源、注册页面)。
这里有一个特别容易踩的坑:很多学生把拦截器配好了,结果发现静态资源(CSS、JS、图片)也被拦截了,页面样式全丢了。原因是SpringMVC拦截器默认拦截所有请求,必须在排除列表里加上/static/**这类静态资源路径。
4.2 图书管理模块与分页查询的实现
图书管理模块最核心的功能是分页查询+条件搜索。我强烈建议你手写一个简单的PageBean实体类,自己实现分页逻辑,然后再看MyBatis的分页插件PageHelper——很多学生喜欢直接用PageHelper,结果答辩时被问到“分页原理是什么”就答不上来。
手写分页的步骤:
- 前端传递
pageNum和pageSize参数 - Service层根据这两个参数计算出
startIndex = (pageNum - 1) * pageSize - MyBatis的Mapper中通过
LIMIT #{startIndex}, #{pageSize}实现分页 - 返回结果中封装当前页数据、总记录数、总页数
条件搜索的SQL是关键难点。比如你要支持按书名模糊查询、按ISBN精确查询、按分类查询,如果每个条件单独写一个查询方法,那组合条件时就会爆炸。MyBatis的动态SQL正好解决这个问题:
<select id="searchBooks" parameterType="map" resultType="BookInfo"> SELECT * FROM book_info <where> <if test="bookName != null and bookName != ''"> AND book_name LIKE CONCAT('%', #{bookName}, '%') </if> <if test="isbn != null and isbn != ''"> AND isbn = #{isbn} </if> <if test="categoryId != null"> AND category_id = #{categoryId} </if> </where> ORDER BY id DESC LIMIT #{startIndex}, #{pageSize} </select>这个<where>标签会自动帮你去掉第一个条件前面的AND,是MyBatis最实用的功能之一,答辩时值得重点讲。
4.3 借书与还书的事务处理
借书和还书是系统里唯一涉及多表更新的业务,也是事务控制的重点。借书时要做的事:
- 检查读者状态是否正常
- 检查图书是否处于可借状态,
available_stock > 0 - 扣减
available_stock - 插入一条
borrow_record记录,status置为1
还书时:
- 找到对应的未归还的借阅记录
- 更新
return_time为当前时间 - 将status改为2
- 增加
available_stock
这里的核心问题是:这四个步骤要么全成功,要么全失败,不允许出现“库存扣了但借阅记录没生成”这种数据不一致。Spring的@Transactional注解正是为此而生。在Service方法上加上这个注解,事务就由Spring统一管理,任何一步抛出RuntimeException都会触发回滚。
我指导过的项目里,真有同学漏掉了这一步,结果还书的同学反复出现库存对不上的问题,查了半天才找到原因:没有加事务管理。这个坑你在自己实现时一定要绕开。
另外,借书时要考虑一个并发问题:两个管理员同时给不同读者借同一本只剩一本库存的书,会不会出现超借?严谨的做法是用SELECT ... FOR UPDATE加行级锁,或者乐观锁加版本号字段。毕业论文里把这个点写进去,评委老师会认为你考虑问题很全面。不过如果是毕业设计,能讲清楚原理即可,实际的并发量也用不着这么复杂的处理。
5. 实操全流程:从零到可运行的完整步骤
5.1 环境准备与项目初始化
这一步看似简单,但决定成败。先确保你本地的环境是干净的:
- 安装JDK 1.8,配置JAVA_HOME环境变量
- 安装Maven 3.6.x,配置本地仓库镜像为阿里云镜像,否则依赖下载慢到怀疑人生
- 安装MySQL 5.7,设置好root密码
- 安装Tomcat 8.5,先不用急着和IDEA集成,单独启动一次确认没毛病
- 安装IDEA,Ultimate版自带Spring和MyBatis的插件提示
在IDEA里新建Maven项目时,选maven-archetype-webapp骨架。项目名建议用library-system这种规范名字,不要用什么test、demo1。包名用com.yourname.librarysystem分成controller、service、mapper、entity、common这些子包。
5.2 pom.xml依赖配置
在pom.xml里将下面这些依赖引入,版本号建议直接参考我列出的:
<properties> <spring.version>5.2.15.RELEASE</spring.version> </properties> <dependencies> <!-- Spring核心 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring.version}</version> </dependency> <!-- SpringMVC --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <!-- MyBatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.6</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.6</version> </dependency> <!-- 数据库驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.26</version> </dependency> <!-- 连接池 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.8</version> </dependency> <!-- Servlet和JSP --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency> <!-- Jackson,用于JSON序列化 --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.12.4</version> </dependency> </dependencies>如果你用的MySQL是5.7,把mysql-connector-java换成5.1.49也行,但更建议用8.0.26同时兼容两种数据库(只需注意8.0驱动要求url中配置serverTimezone=Asia/Shanghai,否则会报时区错误)。
5.3 三个核心配置文件逐个说明
Spring配置(applicationContext.xml):主要配置数据源、SqlSessionFactory、Mapper扫描和事务管理器。
<context:component-scan base-package="com.yourname.librarysystem"> <!-- 排除Controller,Controller由SpringMVC扫描 --> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/library_system?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8"/> <property name="username" value="root"/> <property name="password" value="你的密码"/> </bean> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="mapperLocations" value="classpath:mapper/*.xml"/> </bean> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.yourname.librarysystem.mapper"/> </bean>一个朋友的真实教训:在Spring配置里扫描了Controller,又在SpringMVC配置里也扫描了Controller,结果Bean实例化了两份,事务注解怎么加都不生效,排查了很久。这种问题就属于“原理没搞清导致的时间和心态双重消耗”。
SpringMVC配置(spring-mvc.xml):开启注解驱动,扫描Controller,配置视图解析器,配置拦截器和静态资源放行。
<mvc:annotation-driven/> <context:component-scan base-package="com.yourname.librarysystem.controller"/> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/views/"/> <property name="suffix" value=".jsp"/> </bean> <mvc:resources location="/static/" mapping="/static/**"/> <!-- 拦截器配置省略,要点是放行/login、/register、/static/** -->MyBatis映射文件:以上面那个searchBooks查询为例,所有Mapper的XML文件都放在src/main/resources/mapper目录下,对应每个Mapper接口。
5.4 前端页面开发策略
图书管理系统的前端用JSP+JSTL+EL表达式就足够,不需要上Vue或者React——那是给自己找麻烦。用JSP的好处是服务端渲染,Session和模型数据可以直接在页面上使用,不用额外写Ajax接口。
页面清单:
- login.jsp:登录页
- index.jsp:主框架页,用iframe嵌入子页面
- book_list.jsp:图书列表(分页)
- book_add.jsp:新增图书
- book_edit.jsp:编辑图书
- category_list.jsp:分类列表
- reader_list.jsp:读者列表
- borrow_list.jsp:借阅记录列表
- my_borrow.jsp:读者查看自己的借阅
- register.jsp:读者注册页
前端样式的优先级排最后,用Bootstrap写一套干净的后台模板就够看了。网上有很多免费的后台模板,下载后改造一下,重点保证表格展示清晰、表单布局合理、按钮位置统一。
6. 常见问题与排查技巧实录
6.1 典型问题速查表
根据我现场指导的经验,把学生遇到最多的问题按出现频率排个队:
| 症状 | 可能原因 | 解决方案 |
|---|---|---|
| 项目启动后报404 | 部署名称不对或web.xml配置问题 | 检查Artifact名称和url-pattern |
| 数据库连接失败 | 时区未设置/驱动版本不符 | url添加serverTimezone=Asia/Shanghai |
| Mapper接口报BindingException | 接口包名或XML namespace不对 | 核验mapper扫描包和namespace完整性 |
| 页面样式全丢了 | 拦截器挡住了静态资源 | 放行/static/** |
| 中文乱码 | 编码过滤器缺失 | web.xml配置CharacterEncodingFilter |
| 数据提交失败 | 表单字段名的驼峰映射冲突 | 开启mybatis的mapUnderscoreToCamelCase |
| 事务不生效 | Spring重复扫描了Controller | 检查Spring和SpringMVC扫描包范围 |
| 库存显示负值 | 借还书缺少事务管理 | Service方法加@Transactional |
6.2 三个高频坑的现场还原
第一个坑,MyBatis驼峰映射。数据库字段习惯用下划线(如real_name),Java属性用驼峰(realName),MyBatis默认不会自动映射,查询结果里这个字段就是null。解决方案有两种:SQL中写别名(SELECT real_name AS realName),或者在Spring配置文件里开启:
<configuration> <settings> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings> </configuration>第二种方案更优雅,全局生效,不用每句SQL都写别名。
第二个坑,IDEA的热部署不生效。修改Java代码后必须重启Tomcat才能生效,修改JSP页面倒是可以即时刷新。有些同学改了Controller代码,不重启就点浏览器,看到还是旧页面就以为代码没改对,白白浪费时间。在IDEA里开发调试时,把这个当默认操作习惯就好。
第三个坑,数据库初始化脚本一执行就报错。常见的触发点是写了DROP TABLE IF EXISTS但顺序不对,或者有外键约束的表删除顺序反了。我的习惯是:先写CREATE DATABASE IF NOT EXISTS library_system DEFAULT CHARACTER SET utf8mb4;,然后USE library_system;,建表时先建分类表、用户表,再建图书表,最后建借阅记录表,按依赖顺序来。
6.3 一个提高调试效率的实用技巧
把日志级别调成DEBUG,在MyBatis的映射文件中对每个执行的SQL进行日志输出,然后在控制台直接查看SQL语句。配置方法是在log4j.properties里加上:
log4j.logger.com.yourname.librarysystem.mapper=DEBUG这样每执行一次Mapper方法,控制台就会打印完整的SQL语句和参数值。凡是遇到“查不到数据”“更新不生效”这类问题,第一步永远是看控制台SQL,而不是瞎猜代码哪里错了。
7. 答辩准备、论文写作与加分技巧
7.1 论文结构怎么搭
论文结构不要直接照抄别人的模板,我建议按这个顺序组织:
- 绪论:选题背景与意义、国内外研究现状
- 需求分析:功能性需求与非功能性需求、用例图
- 系统设计:系统架构图、功能模块图、数据库ER图、表结构设计
- 系统实现:分模块截图+关键代码+实现思路说明
- 系统测试:测试用例表、测试结果分析
- 总结与展望
写论文的时候要注意:不要大段贴代码,评委反感。每段核心代码贴不超过10行,重点用文字把思路讲清楚,代码逻辑用流程描述即可。截图要有代表性,页面截图+数据变化前后对比,比光放页面好看多了。
7.2 答辩时评委常问的问题清单
这些是我做评审时经常问的,提前准备好,答辩表现会明显更好:
- 为什么SSM框架要整合成三层架构?每一层的职责是什么?
- 数据库表之间有哪些关联?删除图书时如何处理借阅记录?
- 库存字段为什么用两个字段而不是一个?
- 如果两个用户同时借最后一本书,怎么保证不超借?
- 登录状态是怎么维持的?Session失效后怎么办?
- MyBatis的
#{}和${}有什么区别? - 分页查询的原理是什么?
- 你的系统有哪些安全考虑?(SQL注入、密码加密)——这个问题很多学生答不上来,但只要认真做了上面提到的密码加密方案,就能从从容容回答。
7.3 让你的项目脱颖而出的小改进
在完成基本功能的基础上,有几个投入产出比很高的功能可以加:
- 读者借阅排行榜(按借阅次数排名)
- 图书过期自动标记(定时任务扫描due_time)
- 读者注册时密码强度校验
- 借阅记录的按日期区间统计图表
哪个都不复杂,但能体现你在需求分析上多花了心思。不过记住,加功能的前提是基础功能完全稳定,不要为了追求“花活”把主流程搞得不稳定。
8. 几点真实心得
做图书管理系统这个课题,说实话,难度并不高,但要做得“漂亮”也不容易。我带过的学生里,拿优秀的那些,往往是花时间把数据库设计反复打磨过的、能在白板上画出完整ER图的、把每个Mapper的SQL都能解释清楚的人。反而是那些一开始就追求华丽前端、各种炫技的同学,最后常在数据关系上栽跟头。
最后分享一个小技巧:整个项目完成后,自己从头到尾删除数据库再重新执行一遍初始化脚本,然后把Tomcat也删干净重新配置一遍,模拟从零到一的全过程。这个过程能帮你发现很多“当时能跑但换个环境就跑不起来”的问题,也是检验你对项目掌握程度的试金石。毕设是自己的,每一个细节都能讲明白,答辩的时候你自然会底气十足。