ARTICLE DETAIL

资讯详情

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

高校毕业生去向跟踪系统SSM毕业设计实战解析

高校毕业生去向跟踪系统SSM毕业设计实战解析 简介基于HTML5与Bootstrap前端技术、SSM后端架构的高校毕业生信息去向跟踪系统是一份完整的Java毕业设计源码面向计算机相关专业学生及需要搭建毕业生管理平台的开发者。系统提供毕业生就业状态就业、未就业、创业等的录入、查询与跟踪功能管理员可定期更新应届生与往届生职位变动信息并将统计结果用于辅助学校调整培养计划。资源包共1106个文件压缩包约18.3MB主要包含51个Java源文件、31个JSP页面以及253个HTML、222个CSS、183个JS等前端资源同时附带数据库脚本与相关配置可配合MySQL数据库直接部署运行管理员也可按属性查询毕业生去向。后台采用SSM架构开发环境支持MyEclipse、Eclipse或IDEA。目前已有162人学习浏览项目目录结构清晰模块分层明确适合快速理解业务逻辑。下载后可直接运行也可作为毕业设计二次开发的基础节省大量从零搭建与数据建模的时间。1. 高校毕业生去向跟踪系统到底在跟踪什么先看清SSM毕业设计的真实工作量每年到了毕业设计季我都会被问到类似的需求用 HTML5 Bootstrap SSM 做一个高校毕业生信息去向跟踪系统要 Java 源码、要数据库脚本。这类系统的业务其实很直白——辅导员要在毕业季批量录入学生的就业、升学、待业状态定时更新最后导出统计数据给学校就业办。技术上也不复杂但它恰恰是 SSM 技术栈最典型的落地场景前端页面用 Bootstrap 铺出来后端用 SpringMVC 收请求MyBatis 读写 MySQL。真正让人翻车的不是框架调用而是数据库表设计和状态流转。下面从一个可直接复用的视角把这套系统的表结构、代码骨架、部署步骤和常见坑拆开讲。2. 从HTML5Bootstrap到SSM这套技术栈为什么是毕业设计的标准答案2.1 前端层HTML5新增表单标签与Bootstrap栅格在去向登记页里的用法先说页面。Bootstrap 最核心的价值是栅格系统和现成的表单样式。与其去翻 Bootstrap 官方文档从头读不如直接拿去向登记页面做例子。一个登记表单需要学生姓名、学号、去向类型就业/升学/待业、单位名称、单位性质、联系电话、更新时间。HTML5 给提供了几个新表单标签比如 input typetel、typeemail、typedate、select 配 option、required 校验这些都能让浏览器在前端就拦一道非法数据减轻后端压力。form classform-horizontal action${pageContext.request.contextPath}/track/save methodpost div classform-group label classcol-sm-2 control-label学号/label div classcol-sm-6 input typetext classform-control namestudentNo required /div /div div classform-group label classcol-sm-2 control-label去向类型/label div classcol-sm-6 select nametrackType classform-control required option value请选择/option option value1 selected就业/option option value2升学/option option value3待业/option /select /div /div div classform-group label classcol-sm-2 control-label入职时间/label div classcol-sm-6 input typedate classform-control nameentryDate /div /div button typesubmit classbtn btn-primary保存去向/button /form这段代码的关键点在于form 的 action 指向后端 Controller 的映射地址method 必须是 post因为登记属于写操作不能用 get 带到 URL 上。input 的 required 属性是 HTML5 自带的非空校验学号和去向类型必填后端还要再校验一遍前端校验只是提升体验。typedate 会让 Chrome 弹出日期选择器但老版本浏览器可能不支持所以后端收到的字符串格式要做兼容我一般会在 Controller 里用 DateTimeFormat 注解声明格式否则容易出 400 错误。Bootstrap 的引用方法也很简单把下载好的 css 和 js 文件放进 static 目录在 JSP 里用 ${pageContext.request.contextPath} 拼出绝对路径引入不要用 CDN以免演示时断网打不开页面。form-horizontal 加 form-group 只是让标签和输入框对齐真正布局靠的是 col-sm-2 和 col-sm-6 这两个栅格类名栅格总和是 12剩下 4 份留给右侧留白。为什么用 HTML5 Bootstrap 而不是写原生 CSS因为毕业设计的评分重点在业务功能完整和代码规范页面只要干净整齐就能拿到印象分。Bootstrap 把按钮、表格、弹窗、分页都包好了你要做的是拼装而不是从零设计一套 UI 规范。还有一个隐蔽的收益Bootstrap 的样式文件和字体文件如果用本地路径引入答辩时断网也能正常打开页面这对演示很重要。能在静态资源这一层规避掉网络依赖后面排查样式丢失问题时能少花一半时间。2.2 SSM三层架构Spring容器、SpringMVC控制器、MyBatis映射的职责切分SSM 是 Spring、SpringMVC、MyBatis 的组合。Spring 负责管理对象Service、Mapper 的实例SpringMVC 负责接收 HTTP 请求并分发到 ControllerMyBatis 负责把 Java 对象和数据库行记录做映射。理解这三层的职责切分比背十个注解更重要。一个请求的完整路径是浏览器发出 POST → DispatcherServlet 拦截 → HandlerMapping 找到 Controller 方法 → Controller 调 Service → Service 调 Mapper 接口 → MyBatis 执行 SQL → 返回结果逐层封装 → 跳转或返回 JSON。Controller RequestMapping(/track) public class TrackController { Autowired private TrackService trackService; PostMapping(/save) public String save(Track track, Model model) { // 后端二次校验前端 required 只能挡鼠标键盘操作不能挡恶意请求 if (track.getStudentNo() null || track.getTrackType() null) { model.addAttribute(error, 学号和去向类型不能为空); return track/form; } trackService.saveTrack(track); return redirect:/track/list; } }Controller 上的 RequestMapping(/track) 是类级别的路径前缀方法上的 PostMapping(/save) 组合了 RequestMapping(method POST)。Autowired 是 Spring 的依赖注入它按类型自动装配 TrackService 实例这个实例是 Spring 容器在启动时创建的你不需要 new。需要注意 save 方法的参数 Track trackSpringMVC 会把表单里的 studentNo、trackType 等字段按属性名自动绑定到 Track 对象上前提是 Track 的属性名和 input 的 name 完全一致少一个字母就绑不上。绑定成功后返回 redirect:/track/list这是一个重定向用来避免表单重复提交F5 刷新也不会再触发一次 save。再看 Service 和 Mapper 的写法Service public class TrackServiceImpl implements TrackService { Autowired private TrackMapper trackMapper; Override Transactional public void saveTrack(Track track) { // 更新时记录当前时间业务表中一般不使用数据库now()便于单元测试 track.setUpdateTime(new Date()); trackMapper.insertSelective(track); } }Service 注解告诉 Spring 这是业务层组件Transactional 表示这个方法跑在事务里中间只要抛出 RuntimeException 就会回滚。这里用 insertSelective 而不是 insert是 MyBatis 生成的方法它只插入非 null 字段这样如果某个可选字段没填就不会把 null 写进表里减少数据库层面的意外。这里用到的注解都是 SSM 常用注解也是 Java 面试题里的高频考点比如 Autowired 和 Resource 的区别、Transactional 的回滚条件这些答辩前值得专门准备一下。理解事务边界也很重要saveTrack 里如果后续要同时写 track_record 和学生状态表必须放在同一个事务方法里否则一边成功一边失败数据就对不上了。2.3 MyBatis映射文件里resultMap与动态SQL的取舍Mapper 接口只定义方法真正的 SQL 写在 XML 映射文件里。毕业设计的去向列表一般带查询条件按姓名、按去向类型、按学院筛选。用动态 SQL 可以拼出灵活的 where 条件避免为每种组合写一个方法。select idselectByCondition resultMapTrackResultMap SELECT * FROM track_info where if teststudentName ! null and studentName ! AND student_name LIKE CONCAT(%, #{studentName}, %) /if if testtrackType ! null and trackType ! AND track_type #{trackType} /if /where ORDER BY update_time DESC /select标签会自动去掉第一个条件前的 AND这是 MyBatis 动态 SQL 最常用的技巧。 里的参数名对应传入查询对象的属性名属性名写错不会在编译期报错而是运行时抛出 OGNL 异常排查时先看这里。LIKE 查询用 CONCAT 拼接百分号而不是直接写 %${studentName}%因为 ${} 是字符串替换存在 SQL 注入风险#{} 才是预编译参数。resultMap 的作用是把数据库列名比如 student_no映射到 Java 属性studentNo如果你在 mybatis-config.xml 里开了 mapUnderscoreToCamelCasetrue那么常见的下划线转驼峰就自动完成可以少写很多 resultMap。从上面的代码和注解能看出一个判断这套技术栈之所以成为毕业设计的标准答案不是因为它最新而是因为它的每一层都有明确的交付物前端页面、Controller、Service、Mapper 各有各的文件老师检查代码结构时一目了然。对于一个去向跟踪系统这样的中小型项目SSM 的开发效率和学习成本是最平衡的。如果你的项目用的是 Spring Boot MyBatis那只是把配置方式换成了自动装配分层思路完全一样答辩时把这个关系说清楚也很加分。3. 把数据库脚本跑起来毕业生去向库表设计与初始化数据的落地步骤3.1 核心表结构学生表、去向表、用户表的关系与外键约束去向跟踪系统的数据模型不复杂但有一个容易被忽略的设计点去向记录不应该只保存学生的当前状态而是要保存历史变更。比如一个学生先登记就业三个月后换工作又登记一次如果只有一行记录就无法追溯。常见做法是拆成 student_info 和 track_record 两张表去向表用 student_id 做外键每次变更插一条新记录查当前状态时取最新一条。CREATE TABLE student_info ( student_id INT NOT NULL AUTO_INCREMENT COMMENT 学生ID, student_no VARCHAR(20) NOT NULL COMMENT 学号, student_name VARCHAR(50) NOT NULL COMMENT 姓名, college VARCHAR(50) DEFAULT NULL COMMENT 学院, major VARCHAR(50) DEFAULT NULL COMMENT 专业, phone VARCHAR(20) DEFAULT NULL COMMENT 联系电话, PRIMARY KEY (student_id), UNIQUE KEY uk_student_no (student_no) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT毕业生信息表; CREATE TABLE track_record ( record_id INT NOT NULL AUTO_INCREMENT COMMENT 记录ID, student_id INT NOT NULL COMMENT 学生ID, track_type TINYINT NOT NULL COMMENT 去向类型:1就业 2升学 3待业, unit_name VARCHAR(100) DEFAULT NULL COMMENT 单位或学校名称, entry_date DATE DEFAULT NULL COMMENT 入职/入学时间, update_time DATETIME DEFAULT NULL COMMENT 登记时间, PRIMARY KEY (record_id), KEY idx_student (student_id), CONSTRAINT fk_track_student FOREIGN KEY (student_id) REFERENCES student_info (student_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT去向跟踪记录表;表的设计有几个参数值得留意。第一字符集统一用 utf8mb4 而不是 utf8因为 utf8 在 MySQL 里最多存 3 字节像 emoji 和某些生僻字会存不进去毕业生姓名里出现生僻字的概率不低。第二track_type 用 TINYINT 存数字而不是直接存中文这是为了后续加类型时不用改表结构代码里或页面上再做字典翻译。第三外键约束用 CONSTRAINT 命名这样后续要删外键也能按名找到。实际开发中有人为了性能不建外键只在代码层保证但毕业设计里建外键能向老师展示你懂关系型数据库的完整性应该建。另外还需要一张用户表存登录账号最小化设计就三个字段user_id、username、password。password 一定要存加密后的值常见做法用 MD5 加盐或者用 BCrypt。毕业设计里很多人直接存明文答辩时被问到安全问题会很被动。用户表不要和 student_info 混在一起因为登录用户是系统使用者学生是被跟踪对象两者不是一回事混表会让权限控制变得很别扭。如果你打算做角色区分比如管理员和辅导员可以再加一个 role 字段用 TINYINT 存 1 和 2 就行。3.2 用IDEA或命令行导入SQL脚本字符集与自增主键的两个参数细节拿到项目附带的数据库脚本后常见的导入方式有两种。一种是在 Navicat 里右键数据库选择运行 SQL 文件另一种是用命令行导入。我更倾向命令行因为它不依赖图形客户端的字符集设置出错时报错信息也更直接。mysql -uroot -p --default-character-setutf8mb4 -e CREATE DATABASE IF NOT EXISTS graduate_track DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; mysql -uroot -p graduate_track --default-character-setutf8mb4 /path/to/graduate_track.sql第一条命令先建库第二条命令把脚本导入到指定库。--default-character-setutf8mb4 这个参数一定要加否则客户端默认用系统编码去解析脚本文件Windows 下很容易把中文注释和 INSERT 语句里的中文数据导成乱码。脚本文件本身也要确认是 UTF-8 编码保存如果用户改过且存成了 ANSI那就会出现导入成功但内容全是问号的经典翻车现象这属于文本编辑器编码问题不仔细看根本发现不了。自增主键的一个隐蔽坑在于初始值。如果脚本里有 DELETE FROM student_info 而没有 TRUNCATE那么 AUTO_INCREMENT 计数器不会重置插入新数据时主键会从之前的最大值继续。如果你想让演示数据的 id 从 1 开始导入后需要执行 ALTER TABLE student_info AUTO_INCREMENT 1。这个细节在验收时很明显评分老师打开表看到主键跳到几百会觉得数据不干净。如果你是在 IDEA 里用 Database 面板导出脚本注意勾选 include CREATE DATABASE 或者在脚本头部手动加一句 USE graduate_track不然导入时会报 No database selected。注意数据库脚本导入完成后一定要用查询语句复查一遍数据行数比如 SELECT COUNT(*) FROM student_info确认有数据再启动项目避免后端页面打开了但列表空白误以为是代码问题。4. 在IDEA里跑通SSM项目从Maven配置到Tomcat部署的完整命令链4.1 用IDEA打开源码后的Maven依赖同步与JDK版本对齐项目源码拿到手第一件事不是点运行而是确认三个环境变量JDK 版本、Maven 版本、Tomcat 版本。SSM 项目大多数用 JDK 8 编写如果机器上装的是 JDK 17直接运行会报一堆模块访问错误。正确做法是在 IDEA 的 Project Structure 里把 Project SDK 和 Module language level 都设成 8同时确认 Maven 的 settings.xml 里配的 jdk 也是 8。还有一个常被忽略的地方pom.xml 里的 spring 版本和本地 Maven 仓库里的依赖版本是否匹配如果私服镜像没配对下载会卡在某个 jar 上。properties maven.compiler.source1.8/maven.compiler.source maven.compiler.target1.8/maven.compiler.target project.build.sourceEncodingUTF-8/project.build.sourceEncoding /properties这段 pom 配置的作用是锁死编译级别和源文件编码。很多启动失败的根因不是代码错了而是编译级别默认用了当前 JDK 的版本导致一些依赖二进制不兼容。source 和 target 都设成 1.8 后即使本机有多个 JDKMaven 也会按 1.8 的语法规则编译。另外注意 project.build.sourceEncodingUTF-8/...这是为了让 Java 文件里的中文注释和字符串在编译后不乱码Windows 下系统默认 GBK有时候你在 IDEA 里看代码是正常的mvn 打包后运行就乱了问题就在这。JDK 环境变量配置不正确的表现也类似命令行里 java -version 能打出版本但 Tomcat 启动脚本找不到 JAVA_HOME这种通常是系统环境变量和用户环境变量冲突。Maven 依赖同步的时候要留意 IDEA 右下角的进度条如果长时间卡在 Resolving dependencies多半是中央仓库网络连接出了问题。这时我会用 mvn dependency:tree 在终端里看依赖冲突或者直接把 settings.xml 里的镜像换成国内公共仓库。但毕业设计项目用的依赖都比较常规spring-webmvc、mybatis、druid、jstl 这几个镜像配对就能下全。还有一个容易卡住的地方是 IDEA 的 Maven 面板里显示了项目但 dependencies 列表是空的这时要先点一下刷新按钮让 IDEA 重新读取 pom.xml不要反复重启 IDEA。4.2 修改jdbc.properties数据库连接参数后启动Tomcat的最小步骤数据库导完脚本接下来配数据源。SSM 项目通常有一个 jdbc.properties 文件放在 src/main/resources 下里面是数据库连接的四个关键参数。改这个文件的时候最容易错的是 url 里的参数拼写比如 characterEncodingUTF-8 和 useSSLfalse多一个或少一个问号都会让连接失败的报错信息看起来像是账号密码错误。jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/graduate_track?useUnicodetruecharacterEncodingUTF-8serverTimezoneAsia/ShanghaiuseSSLfalseallowPublicKeyRetrievaltrue jdbc.usernameroot jdbc.password123456driver 用 com.mysql.cj.jdbc.Driver 还是 com.mysql.jdbc.Driver取决于 pom 里 mysql-connector-java 的版本。8.x 版本用 cj5.x 用老的。url 里 serverTimezone 必须指定否则 MySQL 8.x 会报 The server time zone value 错误。allowPublicKeyRetrievaltrue 是为了解决连接时的公钥检索问题本地开发可以加生产环境建议关掉。改完这个文件后一定要在 IDEA 的 Maven 面板里执行一次 clean再重新打包因为 resources 目录的文件有时候不会被热更新进 target。如果你用的是 druid 连接池还可以在配置里加一个 initialSize5 这样的初始化连接数参数它会在应用启动时就建立连接提前暴露数据库配置错误而不是等到第一次查询才报错。注意serverTimezone 和 allowPublicKeyRetrieval 是 MySQL 8.x 连接的两个高频错误点排查时优先检查这两个参数。接下来配 Tomcat。常见做法是在 IDEA 的 Run Configuration 里新增一个 Tomcat Server LocalDeployment 里选择 war explodedApplication context 填项目名比如 /graduate。启动前检查一下 Tomcat 的 VM options 里有没有 -Dfile.encodingUTF-8这个参数决定了控制台打印和请求参数的默认编码。启动后看日志里有没有 Connected to server 和 Deployment is available这两行出来才算部署成功。启动失败是毕业设计答辩前最常见的崩溃场景。见过很多人报 Port 8080 is already in use这时不要盲目改端口先看是谁占用了 8080cmd 里执行 netstat -ano | findstr :8080找到 PID 后在任务管理器结束进程。如果是自己上一次启动没关干净那就是 Tomcat 的工作目录锁死了把 CATALINA_BASE 下的 work 目录删掉再启。还有一种情况是 context.xml 里配了数据源数据库密码改了但这个文件没改Tomcat 会在启动时初始化数据源并直接报错。遇到这种情况按报错里的 Caused by 往下找不要只盯着第一行通常真正的错误原因在最后几行。4.3 用Maven命令手动打包与部署脱离IDEA也能跑的验证方式真正检验一个毕业设计是否可交付建议脱离 IDEA 打一次 war 包放到独立 Tomcat 的 webapps 目录下启动。这能暴露很多 IDEA 里看不到的问题比如依赖是否全部进包、静态资源路径是否绝对化、数据库连接是否硬编码在代码里。mvn clean package -DskipTests cp target/graduate.war /path/to/tomcat/webapps/ROOT.war /path/to/tomcat/bin/startup.sh tail -f /path/to/tomcat/logs/catalina.out第一条命令打出 war 包-DskipTests 表示跳过单元测试毕业设计项目里的测试类很多是依赖数据库的本地没配好容易把整个打包流程卡死。第二条把 war 包拷到 Tomcat 的 webapps 下并命名为 ROOT.war这样访问时就不用带项目名直接 http://localhost:8080。第三条启动后用 tail 盯 catalina.out 日志。如果页面能打开但样式全丢了问题基本出在页面里引用的 css 路径是相对路径部署到 ROOT 下时路径前缀变了需要用 ${pageContext.request.contextPath} 重写静态资源引用。这种独立部署的验证方式能让你在答辩现场的笔记本上也能一键跑起来。很多老师会要求现场从零搭建环境如果你只会点 IDEA 里的绿色按钮换一台电脑就废了。能用命令行打包部署是区分跑通了和真懂了的分界线。最后还要检查一下 war 包里的 WEB-INF/classes 目录下是否真的有 jdbc.properties 文件有时候 IDE 的资源过滤规则会把 xml 和 properties 排除掉导致打包后找不到配置文件这种问题在 IDEA 里看不出来独立部署时立刻暴露。5. SSM系统最常见的5个翻车点从404到中文乱码的排查手册下面这五个问题是我帮人调毕业设计时遇到频率最高的。它们有一个共同特点报错信息看起来五花八门但根子都在配置或编码上一旦看穿五分钟内就能解决。5.1 前端页面打不开静态资源被拦截与404排查现象启动成功后访问 http://localhost:8080/login.jsp结果报 404或者页面打开了但 css、js 全部加载失败F12 里一片红色的 404 状态。原因SpringMVC 的 DispatcherServlet 在 web.xml 里配了 url-pattern 为 /它会拦截所有请求包括 .jsp 和静态资源。如果项目里没有配置 mvc:resources 映射Tomcat 的 DefaultServlet 不会处理这些请求于是全部 404。而 /login.jsp 报 404 则经常是因为文件根本没有放在 webapp 根目录比如被放进了 WEB-INF 下面浏览器无法直接访问 WEB-INF 里的资源。解决在 spring-mvc.xml 里加一段静态资源释放同时用转发而不是重定向访问 WEB-INF 下的页面。如果你的 Controller 返回 login 而配置了 InternalResourceViewResolver 的前缀是 /WEB-INF/jsp/那访问时间接访问 /WEB-INF/jsp/login.jsp 就对了。mvc:resources mapping/static/** location/static// mvc:annotation-driven/这里 mapping 的 /static/** 是 URL 里的前缀location 的 /static/ 是实际文件目录。页面里引用 css 时要写 ${pageContext.request.contextPath}/static/css/bootstrap.min.css这样才能在不同部署上下文里都找得到。排查时先在浏览器直接输入静态资源的完整 URL能打开就说明拦截已经放行问题在页面引用路径打不开就回去看映射配置。还有一种情况是 mvc:resources 的 location 路径写成了 /static 而没有末尾斜杠这会导致资源匹配失败Spring 的路径匹配规则对结尾斜杠很敏感。5.2 数据库中文乱码连接URL、表字符集、页面编码三处不一致现象登记页面填入的中文姓名保存到数据库后变成了 ??或者页面显示的时候是乱码但数据库里是正常的。原因这是三层编码不一致。第一层是表的字符集如果是 latin1 或 utf8存中文就会有问题。第二层是连接 URL 里的 characterEncoding如果没有指定 UTF-8JDBC 会用默认编码解析请求参数。第三层是页面的 response 编码JSP 里没加 % page contentTypetext/html;charsetUTF-8 % 时Tomcat 默认用 ISO-8859-1字节被转换后就彻底乱了。解决先统一表字符集执行 ALTER TABLE track_record CONVERT TO CHARACTER SET utf8mb4再确认 jdbc.url 里有 characterEncodingUTF-8最后在 JSP 页面头部加编码声明并在 web.xml 里加 CharacterEncodingFilter 强制设置请求编码。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 /filter filter-mapping filter-nameencodingFilter/filter-name url-pattern/*/url-pattern /filter-mapping这个 filter 会把所有请求的 encoding 设成 UTF-8前提是它在 web.xml 里的位置要排在其他 filter 前面。如果项目里还有 Shiro 或 Spring Security编码 filter 一定要在它们之前注册否则认证过程里的用户名密码就是乱码登录永远失败。排查乱码最快的方法是在 Controller 里接收参数后打印一行日志看是入场就乱了还是出场才乱。还有一个小细节POST 请求的乱码靠 CharacterEncodingFilter 解决但 GET 请求的乱码要走 Tomcat 的 URIEncoding 配置在 server.xml 的 Connector 上加 URIEncodingUTF-8否则 URL 上的中文参数照样乱。5.3 登录状态丢失Session与拦截器配置顺序的坑现象登录成功跳转到主页点任何一个链接再回来又被拦截器踢回登录页好像 Session 根本没存住。原因三种可能。一是拦截器配置了拦截所有路径但放行的路径写错比如 /login 的 Controller 实际映射是 /user/login只放行了 /login。二是 Session 存在了 request 作用域而不是 session比如用了 request.setAttribute 而不是 session.setAttribute。三是 Cookie 的 path 问题项目部署在 /graduate 上下文里Cookie 的 path 是 /这个一般没问题但如果项目移动过上下文名旧 Cookie 还留在浏览器里就会出现奇怪的时好时坏。解决先在拦截器的 preHandle 里打印 session.getAttribute(user) 看是否为 null再看拦截器和登录 Controller 的路径是不是一致。Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Object user request.getSession().getAttribute(loginUser); if (user null) { response.sendRedirect(request.getContextPath() /login); return false; } return true; }这里的关键是判断后要 return false否则即使重定向了后续的 Controller 还会执行。排查时还要注意拦截器的排序SpringMVC 的拦截器按配置顺序依次执行 preHandlepostHandle 则相反。如果项目里有多个拦截器比如一个统计用时、一个校验登录校验登录的必须放在最前面不然统计拦截器会把未登录的请求也统计进去数据看起来很奇怪。另外一个容易踩的坑是登录逻辑里用了 session.invalidate() 来清理旧会话但紧接着又往同一个 session 里放新用户这时候实际上又创建了一个新会话前端的 Cookie 还是旧的 sessionId就会造成反复踢出。正确做法是先 invalidate 再获取新 session。5.4 列表页数据出不来MyBatis resultType与驼峰映射报错现象表里有数据Controller 返回的 Model 里也有数据但页面上表格是空的或者部分字段为 null。原因MyBatis 查询出来的结果集映射到 Java 对象时列名 student_no 和属性 studentNo 对不上。如果没有配置 mapUnderscoreToCamelCaseMyBatis 默认不会做下划线转驼峰结果就是 studentName、trackType 这些属性全是 null。还有一种情况是查询的列名和实体类属性名完全不匹配比如 SQL 里写了 t.unit_name 而实体类里的属性叫 companyName这时候必须用别名或 resultMap 手动映射。解决在 mybatis-config.xml 里开启驼峰映射同时规范 SQL 别名。settings setting namemapUnderscoreToCamelCase valuetrue/ setting namelogImpl valueSTDOUT_LOGGING/ /settingsmapUnderscoreToCamelCase 开了之后student_no 就会自动映射到 studentNo。这个设置只对单词之间的下划线生效如果你的列名是全大写或者有数字前缀比如 class_2024_no映射规则会变得难以预测这种情况下用明确的 resultMap 更稳。logImpl 设成 STDOUT_LOGGING 后MyBatis 会把执行的 SQL 和参数打印到控制台这是排查列表页数据不对的第一工具。看到打印的 SQL 后复制到 Navicat 里手动执行一遍眼见为实。如果 SQL 执行结果没问题再去检查实体类的字段有没有写 getter/setterMyBatis 底层用的是反射即使属性名对上了没有对应 getter 也会返回 null而且不报错。5.5 启动直接失败jar包版本冲突与Tomcat类加载机制现象启动 Tomcat 时报 java.lang.NoClassDefFoundError 或 NoSuchMethodError有时候是一个类在多个 jar 里同时存在版本不同。原因SSM 项目的依赖链比较长spring-webmvc 和 spring-context 版本不一致或者 mybatis-spring 和 mybatis 版本不匹配。常见的一个坑是项目里同时引了两个版本的旧依赖比如某个从网上下载的二手项目把 spring 3.x 和 4.x 的 jar 混在一起。另外还有一类是依赖扫描到了自己写的类比如在项目里建了一个包叫 org.apache.commons里面放了一个同名类运行时就会很怪异。解决先用 Maven 的依赖树查冲突。mvn dependency:tree -Dverbose这条命令会打印整棵依赖树-Dverbose 能看到每个依赖是通过哪条路径引入的。找到冲突后在 pom.xml 里用 exclusions 排除旧版本或者在 properties 里统一版本号。很多毕业设计项目的 pom 是从网上抄的里面会有很多用不上的依赖比如一整套 dubbo 或者什么 maven 插件这时干脆重建一个只包含 spring-webmvc、mybatis、mybatis-spring、druid、mysql-connector-java、jstl 的 pom反而比在原有基础上修修补补更清爽。还有一个经验Tomcat 的 lib 下如果放过其他项目的 jar比如旧版本的 mysql 驱动会和你项目 WEB-INF/lib 里的新版本冲突排查时把 Tomcat lib 下的无关 jar 清干净。注意如果你同时引了多个版本的 spring-jdbc 和 spring-tx经常会在启动时出现 AbstractMethodError这种错误不会在编译期暴露只在运行时炸出来必须用 dependency:tree 查清楚。6. 把毕业设计做成能答辩的作品追加去向统计报表与验证数据正确性到这一步基本的 CRUD 已经能跑但答辩现场老师最爱问的是你这个系统比别人多做了什么。一个很稳的加分项是去向统计报表用一张柱状图或饼图展示各学院的就业率、升学率。Bootstrap 自身不带图表组件常见做法是再引入 ECharts它的引入方式很轻一个 JS 文件就够不会打破原有的 SSM 结构。后端写一个 COUNT 分组查询返回 JSON前端用 AJAX 接收后喂给图表。SELECT student_info.college, track_record.track_type, COUNT(*) AS cnt FROM track_record JOIN student_info ON track_record.student_id student_info.student_id GROUP BY student_info.college, track_record.track_type ORDER BY student_info.college;这类分组查询的结果需要用三个字段组合成一个可用的图表数据结构。在 Controller 里建一个 MapString, Object 或者一个简单的 VO 类把 college、trackType、cnt 三个值放进去用 ResponseBody 返回 JSON 数据。前端的 AJAX 成功回调里按照 ECharts 的 option 结构填入 series这个东西很多人第一次写会卡在数据格式上所以我的建议是先用浏览器直接访问那个返回 JSON 的 URL确认数据结构是类似 [{ college: 计算机, trackType: 1, cnt: 23 }] 的数组再去写前端渲染代码。不要一上来就堆图表配置先把数据链路打通图表只是数据的展示形式。验证数据正确性也是答辩时值得展示的素养。我的习惯是在 Service 层写一个单元测试用 Spring 的 JUnit 测试框架加载上下文直接调用 Service 方法并断言查询结果。比如构造一个学生数据调 saveTrack 后再查出来断言 trackType 是否等于刚刚设的值。这种测试不要连真实数据库用 H2 或者在 test 配置里指向一个临时库跑完自动回滚。答辩时你可以说这个系统的数据正确性不是靠眼睛看而是有自动化测试跑过这比我手动测了很多遍有说服力得多。ECharts 的图表如果时间紧可以先做一个简单的柱状图别贪多做出来之后要当场演示数据变化带来的图表联动老师最喜欢看这个。最后提醒一句所有的前端校验都能被绕过关键的后端校验和事务回滚一定要写这是做了很多个类似项目之后吃过亏才养成的习惯。希望帮到你。本文还有配套的精品资源点击获取
返回列表