SSM在线考试系统实战:从架构设计到部署避坑全解析
2026/8/31 14:42:25 网站建设 项目流程

简介:本资源是面向计算机专业本科生的毕业设计实战项目——基于SSM框架(Spring+SpringMVC+MyBatis)开发的高中在线考试系统,聚焦电子化考试场景,覆盖试题管理、在线作答、自动评分、成绩反馈与考试监控等核心功能,适用于课程设计、毕设选题及Java Web技术进阶学习。压缩包共430个文件,含87个Java源码、87个编译后Class文件、76个JSP页面、43个XML配置与映射文件、77个Jar依赖库,以及SQL建库脚本、系统文档(docx)、配置说明(txt)和优化后的可部署程序包,整体22.76MB,结构清晰体现典型MVC分层设计。已有17人下载学习,读者可直接获取完整开发文档(含需求分析、数据库设计、模块实现细节)、可运行源码工程(含Controller层控制器类如ShijuanxinxiController、DuoxuantiController等)、MySQL初始化脚本及环境部署指引,快速掌握SSM整合开发全流程与教育类系统业务建模方法。 作为常年泡在Java Web项目里的老开发,看到“SSM在线考试系统”这个标题就觉得很亲切。SSM(Spring + SpringMVC + MyBatis)这套组合虽然现在被Spring Boot压了一头,但依然是无数计算机专业学生的毕业设计主战场,也是很多旧系统维护的必修课。这个项目的完整名称是“149ssm在线考试系统(文档+源码)_kaic.zip”,压缩包里既有可直接运行的源码,也有配套的说明文档,基本就是奔着“开箱即用”去的。这篇文章我就以这套在线考试系统为蓝本,把项目从需求拆解、数据库设计、核心功能实现,到环境部署和踩坑实录,完整地梳理一遍。无论你是准备拿它当毕设参考,还是想弄清楚SSM项目到底是怎么跑起来的,这篇文章都能给你一些实在的东西。

1. 项目整体设计与需求拆解

1.1 在线考试系统的核心业务场景

在线考试系统听起来简单,但真正拆开看,它要管的事情并不少。从用户侧看,至少分学生、教师、管理员三类角色。学生要能登录、参加考试、查看成绩;教师要能维护题库、创建试卷、批改主观题、统计成绩;管理员则要负责用户管理、考试安排和基础数据维护。这三类角色天然构成了不同的权限边界,也就决定了系统不可能只是一个“做卷子”的简单页面。

从业务流程来看,一场在线考试从创建到结束,至少要经历:教师录入题目、教师组卷并发布考试、学生在规定时间内作答、系统自动判分(或教师批改主观题)、成绩落库并展示。这套流程里藏着很多细节点,比如考试时间到没到、学生能不能提前交卷、答过的题断电了怎么办、主观题谁来评分等。好的在线考试系统,首先要把这些场景想清楚。

我见过太多毕设项目一开始就把功能表拉得特别大,什么在线课堂、论坛、错题本全都要做,最后代码堆了一万多行,核心的考试流程反而漏洞百出。真正合理的做法是先把“考、判、查”这三个核心动作闭环跑通,再做锦上添花的功能。这套SSM在线考试系统的定位就比较务实,功能集中在用户、题库、试卷、考试、成绩这几个核心模块上,没有过多铺开,这是我认为它适合作为学习样本的重要原因。

1.2 为什么是SSM而不是Spring Boot

很多同学拿到这个项目第一反应是:都什么年代了,还用SSM?直接上Spring Boot不香吗?这个问题我每次都要解释一下。SSM虽然配置繁琐,但它把Spring的核心思想暴露得更加彻底:Spring容器怎么管理Bean、SpringMVC怎么完成请求分发、MyBatis怎么和数据库打交道,这些在Spring Boot里往往被自动配置隐藏了,而在SSM里你必须亲手把每一块拼起来。

这个“亲手拼装”的过程,恰恰是理解Java Web后端运行机制最有效的路径。你配置web.xml、配置Spring的applicationContext.xml、配置SpringMVC的dispatcherServlet-servlet.xml、配置MyBatis的mybatis-config.xml,每一份配置都在告诉你:一个请求从浏览器发出来之后,是怎么被DispatcherServlet接收、被HandlerMapping找到对应Controller、被Service层处理业务逻辑、被Mapper接口映射SQL、最后把结果渲染回页面的。

另外,很多学校的课程设计依然要求SSM框架,因为教学大纲还没更新。如果你直接交一个Spring Boot项目,可能连查重和答辩那关都过不了。所以不是SSM有多好,而是它在教学体系里依然有不可替代的位置。对于想真正吃透Java Web的人来说,花两三天把SSM整合一遍,比直接用IDEA生成一个Spring Boot项目收获大得多。

学习SSM的正确心态是:不排斥Spring Boot,但也别觉得SSM是过时技术。这套在线考试系统就是一个很好的练手样本——它麻雀虽小,但该有的分层、依赖注入、事务管理、MyBatis映射全都有,读完它,你再去看Spring Boot项目,会发现很多概念都是相通的。

1.3 系统功能模块全景

为了让你对这套系统有个整体认知,我把它的功能模块先铺一张全景图(这里用表格描述,不画图)。你可以拿这个表去对照源码里的包结构和页面文件,很快就能定位到每一块代码在哪里。

模块核心功能涉及角色
用户模块注册、登录、个人信息维护、密码修改学生、教师、管理员
题库模块题目的增删改查、按科目/题型分类教师
试卷模块创建试卷、选择题目、组卷策略、试卷管理教师
考试模块考试发布、在线答题、倒计时、交卷学生
判分模块客观题自动判分、主观题手动批改、分数汇总系统、教师
成绩模块成绩查看、成绩统计按科目、分数导出/打印学生、教师、管理员
管理模块用户管理、班级管理(如有)、系统基础配置管理员

从模块划分可以看出,这个系统的核心链路是“教师出题 → 教师组卷 → 学生考试 → 系统判分 → 成绩查看”。其他如用户管理、权限控制都是为这条主链路服务的。这套SSM在线考试系统的代码结构基本符合这个逻辑:controller包下按模块建类,service包下写业务接口和实现,mapper包下定义数据库操作,entity/model包下放实体类,resources目录下放Mapper XML和Spring配置文件,webapp目录下放JSP页面和静态资源。

如果你要在这个基础上扩展功能,最建议优先扩展的是“试题导入导出”(用Excel批量导入题库)和“考试防作弊”(切屏警告、随机题序),这两个功能在实际场景中几乎是刚需,也是答辩时很能加分的亮点。

2. 数据库设计与核心表结构

2.1 用户/角色/权限的建模思路

在线考试系统的数据库设计是整个项目的命根子。表建得好不好,直接决定后面写Mapper SQL的时候是轻松自如还是痛苦不堪。先从用户这块说起。

一般在线考试系统会设计user(用户表)、role(角色表)、user_role(用户角色关联表)三张表,这是典型的RBAC(Role-Based Access Control,基于角色的权限控制)简化模型。用户表必备字段包括:id(主键)、username(用户名)、password(密码,建议MD5或BCrypt加密存储)、real_name(真实姓名)、student_no/teacher_no(学号/工号)、email、phone等。角色表字段非常简单,id和role_code、role_name就够用,比如role_code分别是“STUDENT”“TEACHER”“ADMIN”。关联表user_role存两个外键字段。

但很多毕设项目为了降低复杂度,会直接在用户表上加一个role字段,用int类型区分:0代表管理员、1代表教师、2代表学生。这套SSM在线考试系统的做法更接近后者——它在用户表里带了一个角色标识字段,省去了关联查询。这种设计在数据量小、角色固定的时候完全够用,代码写起来也更直接。不过如果你想让“权限”这块在答辩时更有说服力,建议升级成三张表的RBAC模型,面试官或答辩老师问起来你能讲出个子丑寅卯。

密码字段这里多说一句。很多网上开源的SSM项目密码直接明文存储,这在大作业层面问题不大,但如果这个系统要真上线,必须加密。MD5加盐也好,BCrypt也罢,至少不能让密码裸奔。你拿到源码后可以顺手把注册和登录的逻辑改一下,这是简历上可以写的一个优化点。

2.2 题库与试卷的建模思路

题库表(question)是系统中的核心数据表。它至少要包含以下字段:id、subject_id(所属科目/课程)、type(题型:单选、多选、判断、填空、简答等)、content(题干)、option_a、option_b、option_c、option_d(选项,简答和填空题可以留空)、answer(标准答案)、score(单题分值)、difficulty(难度等级,可选)、create_time等。

设计题库表时最容易踩的坑是“题目类型不确定,导致字段不够用”。比如有的题目是判断题,只有对错两个选项;有的题目是填空题,需要多个空;有的题目是多选题,答案是“ACD”这样的组合。我的建议是:选项字段预留4个(A/B/C/D),判断题只填A和B表示“对/错”,多选题答案直接用字符串拼接(比如“A,C,D”),填空题则用特殊分隔符存多个空的答案。这种方式虽然不够“优雅”,但在一个课程设计级别的系统里完全够用,而且实现起来成本极低。

试卷表(exam_paper)的设计主要有两种思路。第一种是“试卷+试卷题目关联表”的两层结构:paper表存试卷的基本信息(名称、总分、时长、创建人、创建时间),paper_question表存这道试卷里包含了哪些题目、每题的分值、题目的顺序。第二种是直接把题目内容冗余到paper_question里,这样即使原题库里的题目被修改了,已经生成的试卷也不会受影响。

我强烈推荐采用第二种思路——在关联表里冗余一份题目的快照。原因很简单:假设教师3月10日出了一套试卷,3月15日修改了题库里某道题的选项,按第一种设计,考生在3月20日考试时看到的是被修改后的题目,这就不符合“试卷应保持发布时状态”的常理。冗余快照虽然占用了一点存储空间,但保证了试卷的稳定性和可回溯性,这也是真实系统中常用的做法。

2.3 考试记录与成绩表的关键字段

考试记录表(exam_record)记录的是“某位考生对某套试卷的一次考试过程”。关键字段包括:id、user_id(考生)、paper_id(试卷)、start_time(开始时间)、end_time(交卷/结束时间)、duration(实际用时)、status(考试状态:进行中/已交卷/超时)、total_score(总分)、create_time。

这里要注意start_time和end_time的设计。一场正式的在线考试需要设定考试开始时间和结束时间,学生只能在指定时间窗口内进入考试。在表设计上,paper表里要有exam_start_time和exam_end_time两个字段,而exam_record里的start_time是学生实际点击“开始考试”的时间。这样你就可以用SQL或Java逻辑判断:现在时间是否在考试窗口内?学生是否超时?

答题明细表(answer_record)是另一个容易被忽略的表。它记录的是学生对待每一道题的具体答案,字段包括:id、exam_record_id(关联考试记录)、question_id(题目)、student_answer(学生答案)、is_correct(是否正确)、score(该题得分)。这张表的作用有两个:一是系统判分后学生可以逐题回看自己的答题情况;二是教师批改主观题时,需要针对每一道简答题单独打分。

成绩统计通常可以直接在exam_record表上做聚合查询,比如按考试、按班级、按科目统计最高分、最低分、平均分、及格率。如果系统需要更复杂的成绩分析(比如各分数段人数分布),可以再建一个统计结果表,通过定时任务或手动触发的方式生成。课程设计级别的系统不需要做到这一步,直接把exam_record按paper_id分组查询就够了。

3. 核心功能实现与代码细节

3.1 SSM三层架构到底怎么分层

SSM项目的代码结构一般是标准的三层架构:表现层(Controller)、业务层(Service)、持久层(Mapper)。这套在线考试系统的包结构大致如下:

com.kaic.exam ├── controller # 表现层,处理HTTP请求,返回页面或JSON │ ├── UserController │ ├── QuestionController │ ├── PaperController │ ├── ExamController │ └── ScoreController ├── service # 业务层接口 │ ├── UserService │ ├── QuestionService │ ├── PaperService │ ├── ExamService │ └── ScoreService ├── service.impl # 业务层实现 │ └── ... ├── mapper # MyBatis Mapper接口 │ ├── UserMapper │ ├── QuestionMapper │ ├── PaperMapper │ ├── ExamRecordMapper │ └── AnswerRecordMapper ├── entity # 实体类,对应数据库表 │ ├── User │ ├── Question │ ├── Paper │ ├── ExamRecord │ └── AnswerRecord └── common/util # 工具类、常量类、统一返回结果封装

三层架构的核心规则是“上层依赖下层,但不能跨层调用”。Controller层只负责接收参数、调用Service、把结果传到页面;Service层写业务逻辑,比如判断考试时间是否有效、计算总分;Mapper层只做数据库的增删改查。很多初学者会犯一个错误:在Controller里直接注入Mapper,把业务逻辑写在Controller里。这在很小的demo里没问题,但一旦业务复杂起来,Controller会越来越臃肿,根本没法维护。

拿“学生交卷”这个操作举例,正确的业务层逻辑应该是:ExamService.submitExam(recordId, answers)方法里,先根据recordId查出考试记录,判断考试状态是否已经是“已交卷”(防止重复交卷),再遍历学生提交的答案,对客观题进行自动判分,累加总分,更新考试记录状态,最后批量插入答题明细。这一串逻辑放在Service里,Controller只需要接收前端传来的参数,调用submitExam方法,然后返回“交卷成功”就行。

3.2 自动组卷的两种实现方式

在线考试系统里最有技术含量的功能之一就是组卷。组卷分两种方式:手动选题和自动组卷。

手动选题实现起来简单:教师创建试卷时,先选好科目,然后在题目列表里勾选要加入试卷的题目,点击添加,系统把题目快照写入paper_question表。这种方式适合小规模题库,操作直观,但教师得一道一道选,效率很低。

自动组卷则要设计策略。最简单的策略是“按题型和数量随机选题”:教师输入单选题要10道、多选题要5道、判断题要5道、简答题要2道,系统从题库中对应题型里随机抽取。对应的Mapper SQL可以这样写:

SELECT * FROM question WHERE subject_id = #{subjectId} AND type = 'SINGLE' ORDER BY RAND() LIMIT #{count}

ORDER BY RAND()在数据量小的场景下完全没问题,但如果题库有几万道题,这种方式会全表扫描加随机排序,性能会比较差。更专业的做法是先统计题型总数,然后算出一个随机偏移量,再用LIMIT offset, count取出随机记录。课程设计级别用RAND()就够了,答辩时你只要能把这个方案的优缺点说出来,就已经很加分了。

还有一种进阶的组卷策略是“按知识点和难度分布组卷”:教师设定每个难度等级(易、中、难)的题目占比,系统从对应知识点中按比例抽取题目。这种方案需要题库表里有difficulty字段,而且组卷算法要复杂得多。如果时间有限,我建议不要贪多,手动选题+随机抽题就能覆盖大部分需求。

3.3 定时交卷与防作弊的落地做法

考试过程中的“时间控制”是最容易出Bug的地方。前端虽然可以用JavaScript写倒计时,但倒计时到0就自动交卷的逻辑绝对不能只信任前端——用户可以修改浏览器时间、刷新页面甚至直接调接口。因此,后端必须做两层校验。

第一层是前端控制:进入考试页面时,后端返回考试的截止时间(endTime),前端根据这个时间戳计算倒计时,倒计时归零后触发交卷请求,并禁用答题按钮。第二层是后端兜底:学生提交答案时,后端要检查当前时间是否已经超过考试截止时间;如果服务器时间已经过了截止时间且考试记录状态还是“进行中”,系统应该强制置为“已交卷”,并对未提交的题目不给分。

实现后端兜底的一种常见做法是:在交卷接口里加一个时间判断,如果当前时间大于paper.examEndTime,则只处理已经收到的答案,并更新考试记录状态为“超时交卷”。还可以写一个定时任务(Quartz或Spring Task),每分钟扫描一次考试记录表,把超过截止时间仍未交卷的记录自动标记为“已交卷”,总分按已作答题目计算。

防作弊这块,简单可落地的方案是在考试页面记录“切屏次数”。前端监听window的blur事件,每次页面失去焦点(比如切到其他标签页)就计数,页面重新聚焦时把切屏次数上报到后端,存在exam_record表里。教师查看考试详情时,能看到这位考生切了几次屏。至于要不要因为切屏直接取消成绩,需要靠制度规则去定义,系统层面只负责记录数据。这个功能实现成本极低,但很能体现细节,建议加上。

3.4 判分逻辑与成绩统计

判分是整个系统流程的收尾环节。客观题(单选、多选、判断)的判分完全可以用代码自动完成,核心逻辑就是字符串比对。这里容易踩坑的是多选题——学生答案可能是“A,C,D”这样乱序的,标准答案也是“A,C,D”。如果直接字符串equals比较,顺序不对就判错。稳妥做法是把答案字符串按逗号拆分,排序或转Set之后再做比较,这样“A,D,C”和“A,C,D”都能判定为正确。

主观题(简答、论述)不能自动判分,需要教师去后台“待批改列表”里找到对应试卷,逐题查看学生答案,输入分数,最后系统汇总。这里为了效率,通常做成分步提交:教师先批量批改,改完一道保存一道,最后统一提交。当所有主观题批改完毕,系统根据“客观题得分 + 主观题得分”计算出总分,更新exam_record表。

成绩统计基本就是SQL聚合了。统计某场考试的平均分、最高分、最低分、及格率,一条SQL就能搞定:

SELECT COUNT(*) AS totalCount, AVG(total_score) AS avgScore, MAX(total_score) AS maxScore, MIN(total_score) AS minScore, SUM(CASE WHEN total_score >= 60 THEN 1 ELSE 0 END) / COUNT(*) * 100 AS passRate FROM exam_record WHERE paper_id = #{paperId} AND status = 'SUBMITTED'

需要注意的细节是:考试记录状态里会混着“进行中”“已交卷”“超时交卷”等多种状态,统计成绩时一定要过滤掉那些还没有交卷的记录,否则平均分会被拉低。

4. 环境搭建与项目部署实操

4.1 本机环境准备清单

无论你是要在自己电脑上把这个SSM项目跑起来,还是想在此基础上二次开发,环境搭建是避不开的第一步。如果是从零开始,建议按下面的清单准备工具。

工具版本建议说明
JDK1.8SSM项目最常见运行版本,JDK 11+可能出现兼容问题
Maven3.6.x项目依赖管理,版本过高或过低都可能出问题
Tomcat8.5 或 9.0部署运行容器,不要用Tomcat 10(涉及Jakarta命名空间迁移)
MySQL5.7 或 8.0数据库,注意8.0驱动名和时区设置
Navicat / SQLyog任意数据库客户端,也可用IDEA自带Database工具
IDEA2021+开发工具,Community版也够用

JDK和Maven的安装配置这里不展开,但要说一个高频问题:Maven仓库镜像。如果你在中国大陆,默认中央仓库下载依赖非常慢,强烈建议在Maven的settings.xml里配置阿里云镜像。很多同学卡在“IDEA构建项目一直失败”上,就是依赖没下载完,换成镜像后几分钟就好了。

4.2 导入项目与参数配置

拿到压缩包之后,解压出来一般会是这样的目录结构:

db/ exam.sql # 数据库初始化脚本 src/ main/ java/ # Java源码 resources/ # 配置文件和Mapper XML webapp/ # JSP页面、静态资源、web.xml pom.xml # Maven依赖配置 README.md # 项目说明文档

第一步是导入数据库。用Navicat新建一个数据库,字符集选utf8mb4,然后在“运行SQL文件”里选择db/exam.sql,执行完成后就能看到所有表。如果有初始化数据,一般是默认账号,比如admin/admin123这样的,以README里的说明为准。

第二步是修改数据库连接配置。在src/main/resources目录下一般会有一个jdbc.properties或db.properties文件,里面写着数据库连接信息。你需要把url、username、password改成你自己本机的MySQL配置。如果是MySQL 8.0,驱动类要写成com.mysql.cj.jdbc.Driver,url里还要加时区参数serverTimezone=Asia/Shanghai,不然会报时区错误。

第三步是配置Tomcat。在IDEA里打开项目后,点“Run → Edit Configurations”,添加一个Tomcat Server/Local,在Deployment页签里把项目部署上去,Application context一般设成/exam或/,具体看代码里URL的写法。这一步很多新手会漏,直接Run Application主类,发现根本跑不起来——因为SSM项目必须在Web容器里运行,不能像Spring Boot那样自带Tomcat。

4.3 从Tomcat启动到首次登录

配置完成后,启动Tomcat,控制台出现类似“started on port 8080”或“Server startup in xxx ms”的日志,就说明项目启动成功了。打开浏览器输入http://localhost:8080/exam(取决于你设置的Application context),应该能看到登录页面。

登录页面通常会提供“学生登录”“教师登录”“管理员登录”三个入口,或者就是同一个入口靠账号角色区分。用数据库初始化脚本里的默认账号登录一次,跑通“登录 → 查看考试列表 → 参加考试 → 交卷 → 查看成绩”这条主链路,就说明系统在你的机器上已经正常运行了。

这里有个实用小技巧:第一次启动如果控制台报错一大片,先别慌,把第一个Exception(带有Caused by字样除外,要看最顶部的错误)复制到搜索框里查,90%的问题都能在常见错误列表里找到答案。剩下的10%,基本都是下面这几种:数据库连不上、端口被占用、依赖没下载全。

5. 常见问题与排查实录

5.1 Maven依赖下载失败或IDEA不识别项目

这类问题在导入SSM项目时出现频率最高。症状通常有两种:第一,IDEA打开项目后,右侧Maven面板里一片空白,或者依赖列表里有红色波浪线;第二,编译时报错找不到org.springframework或org.mybatis下的类。

排查顺序是这样的:先看IDEA是否把项目正确识别为Maven项目,如果pom.xml文件没有被标记为Maven Project,在pom.xml上右键选择“Add as Maven Project”就好。其次,看Maven配置是否指向了正确的settings.xml,确认本地仓库路径和镜像已经配置好。如果依赖已经在本地仓库但IDEA识别不到,执行一次“Maven面板 → Lifecycle → clean → install”重启IDEA即可。

如果依赖下载总是中途失败,大概率还是网络问题。把settings.xml里的阿里云镜像配好,或者把IDEA的Maven导入选项里的“Always update snapshots”设置为不勾选,可以避免反复拉取快照包失败的问题。

5.2 数据库连接失败和中文乱码

数据库连接失败可以说是SSM项目里最常见的报错。错误信息通常是“Access denied for user ‘root’@‘localhost’ (using password: YES)”或者“Communications link failure”。

“Access denied”就是账号或密码不对,检查jdbc.properties里的配置。“Communications link failure”多半是MySQL服务没启动,或者端口不是默认的3306。还有一个非常烦人的坑:MySQL 8.0以上版本的密码加密方式默认是caching_sha2_password,而老版本的连接驱动不支持,解决办法是升级drvier到mysql-connector-java 8.0.22及以上,或者在MySQL里把root账号的密码加密方式改成mysql_native_password。

中文乱码则分两层。第一层是数据库层面的:建库时字符集没选对,导致存到表里的中文显示成问号或乱码。解决方法是建库时指定字符集:CREATE DATABASE exam DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci。第二层是页面显示层面的:JSP页面头部没有加charset=utf-8,或者过滤器没设置编码。在web.xml里配置一个CharacterEncodingFilter,设置encoding为UTF-8,可以解决大部分请求和响应的乱码问题。

5.3 前端页面资源加载异常

启动成功后,打开页面发现布局全乱了,CSS样式丢失,头像加载不出来,f12控制台一大堆404。这个问题几乎每个SSM新手都遇到过,根源在于静态资源的访问路径没有处理对。

SSM项目里,如果使用JSP,通常会在页面里用<c:url>标签或${pageContext.request.contextPath}拼basePath,确保资源路径以项目上下文名称开头。如果你发现路径硬编码成了“/css/style.css”,而项目部署上下文是“/exam”,那么浏览器实际请求的地址就是http://localhost:8080/css/style.css,根本不包含项目名,自然404。

另外还要检查SpringMVC配置里是否放行了静态资源。因为DispatcherServlet会拦截所有请求,如果没在spring-mvc.xml里配置<mvc:resources mapping=“/css/**” location=“/css/”/>,浏览器请求CSS文件也会被当成Controller接管,返回404。

这个403/404的问题排查起来不复杂,但很折腾。我的经验是:打开浏览器F12,Network面板看请求的URL,把URL和项目上下文对比一下,两分钟就能定位。

5.4 事务不生效和Mapper绑定异常

这两个问题是后端开发里比较隐蔽的Bug。事务不生效的表现是:比如学生交卷后,考试成绩计算成功,但答题明细插入失败,最终成绩也没能保存。这种“部分成功部分失败”的现象,多半是事务没开启。

在SSM里开启事务有两种常见方式:一是在spring.xml里配置<tx:annotation-driven transaction-manager=“transactionManager”/>,然后在Service实现类上加@Transactional注解;二是在spring.xml里配置事务增强。如果你发现@Transactional加上了但不生效,第一件事是检查spring.xml有没有配 tx:annotation-driven ,因为注解本身不会自动生效,没有这个配置,你加多少个注解都没用。

Mapper绑定异常则是启动时或首次调用Mapper方法时,报类似“BindingException: Invalid bound statement (not found)”的错。一般是Mapper接口和Mapper XML文件没有正确关联,排查三个点:Mapper XML文件的namespace是否等于接口的全限定名;XML文件里每条SQL的id是否等于接口方法名;定义Mapper接口的包路径和Mapper XML扫描路径是否一致。这三个点对上了,这种报错的基本就消失了。

5.5 时间控制与并发问题

最后聊一个比较进阶的问题:考试时间判断和并发提交。如果学生同时打开多个标签页,或者用脚本同时发送多个交卷请求,后端会因为缺少幂等校验而重复交卷、重复保存成绩。

一个简单的解决方案是在exam_record表里增加一个status字段,交卷前先执行“UPDATE exam_record SET status = ‘SUBMITTED’ WHERE id = ? AND status = ‘EXAMING’”,如果受影响行数为0,说明这条记录已经不是考试中状态,就拒绝再次交卷。这个用乐观锁的思路,比“先查询再判断再更新”的写法安全得多。

时间判断上也一样,判断条件要放在数据库更新语句里:UPDATE exam_record SET total_score = #{score}, status = ‘SUBMITTED’, end_time = NOW() WHERE id = #{id} AND status = ‘EXAMING’ AND NOW() < #{deadline}。如果更新行数为0,交卷就视为失败,重新查询一下原因即可。这种方式可以防住大部分并发问题。

做这套SSM在线考试系统的部署和排查,我最大的感受是:SSM项目的问题绝大多数都出在“配置环节”,而不是业务逻辑本身。Spring容器有没有起来、DispatcherServlet有没有拦截对路径、Mapper XML的模式有没有拼对,这些排查经验一旦积累下来,后面再碰Spring Boot、碰微服务,处理思路都是一脉相承的。

最后再分享一个我个人的使用习惯:拿到这类带文档的源码项目,我一定会先把README通读一遍,再按数据库脚本、配置文件、启动步骤这个顺序去梳理项目,不要一上来就点Run。文档里几句话可能就省下你半天排错的时间。这个项目后续如果想继续扩展,我建议优先考虑把前端页面从JSP升级到Vue3加后端API分离,利用热词里大家常提到的“vue3连接ssm框架”思路,把前后端彻底解耦,一方面页面体验会好很多,另一方面在简历上写“SSM架构升级为前后端分离”也更有分量。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询