☰
JSP+Servlet+MySQL成绩管理系统:角色权限与课程设计实战
2026/10/9 3:58:14 网站建设 项目流程

简介:基于Jsp+Servlet实现的Web成绩管理系统,面向JavaWeb初学者及课程设计人群,覆盖学生、教师、管理员三类角色的核心业务,适合作为SSM/JSP阶段课设参考或毕设改造底稿。压缩包共870个文件,约39.64MB,包含93个Java源码、46个JSP页面、174个CSS样式、212个JS脚本及9个Excel数据文件,另有SQL脚本与说明文档,基本能支撑部署运行和二次开发。系统按学生、教师、管理员三模块组织,学生可查询个人信息与本人成绩,教师可完成成绩录入、修改、查看与分析,管理员可对学生、教师、班级、课程进行增删改查,并可安排课程、分配班级、管理系统用户。已有173人学习下载,适合需要完整可运行课设源码、模块化三层结构以及前后端资源对照参考的读者。

1. 基于 JSP + Servlet 的成绩管理系统:课程设计是怎么把三个角色塞进一个 Web 项目的

如果你是 Java Web 方向的学生,十有八九会被布置一个“学生成绩管理系统”的课设。这个题目看起来常规,但真正动手时才会发现:学生、教师、管理员三个角色要复用同一套登录入口,权限控制要区分页面级别和按钮级别,成绩录入要考虑分数边界和课程关联,单独写十个 JSP 页面也能交差,但答辩时导师一问“你的 Servlet 是怎么分发请求的”就可能卡壳。我拆的这个项目是编号 100010041 的 JSP+Servlet+MySQL 成绩管理系统,核心价值在于它把课程设计里最难的部分——学生模块、教师模块、管理员模块的三方协作,做成了可以直接照抄的工程结构。适合正在做课设、或者想弄懂 JSP 传统开发方式(非 Spring 系)的读者,拿来改改就能用自己的数据库跑起来。

2. 项目架构与运行环境:从三层结构到本地部署,先把骨架看清

2.1 技术选型:为什么 2025 年还要看 JSP + Servlet 项目

现在企业里新项目基本是 Spring Boot 的天下,但高校课设和部分老系统维护仍然大量使用 JSP + Servlet。这个项目恰好是一个标准的 Java Web 应用:JSP 负责视图层,Servlet 负责控制器层,DAO 负责数据访问层,MySQL 存数据。三层结构清晰,导师喜欢看这种分层,因为能讲清楚“请求怎么走”。

从源码包的静态资源可以看出,项目引入了 Bootstrap 和 Summernote:bootstrap.min.css 是前端样式框架,用于页面布局和表格美化;summernote-bs3.css 是一个富文本编辑器插件,对应版本是 Bootstrap 3 适配版。这说明系统不是纯 JSP 裸写页面,而是用前端框架把界面做了基础优化。常见的课设项目里,用 Bootstrap 3 搭配 JSP 是很经典的组合,因为不需要额外配置 Node 环境,直接把 CSS 文件丢进 webapp 目录下就能用。

项目本身是标准的 Maven 或 Dynamic Web Project 结构,源码包中应该包含 src(Java 源码)、webapp(JSP 页面和静态资源)、pom.xml(如果走 Maven)或者 .classpath 等 Eclipse 工程文件。核心的运行环境需求如下:

组件版本建议说明
JDK1.8 或 8+老项目兼容性最好,11 以上可能要调编译级别
Tomcat8.5 / 9.0对应 Servlet 3.1/4.0,适配 JSP 2.3
MySQL5.7 或 8.0注意驱动包版本,8.0 要用 mysql-connector-java 8.x
开发工具Eclipse / IDEA配置好 Tomcat 运行时即可

2.2 项目目录结构与请求流转:一个登录请求是怎么走完的

先看代码包里的核心目录结构,拿到源码包后第一件事不是急着跑,而是把目录对照一遍:

src/ ├── com/xxx/dao/ # 数据访问层,StudentDao、TeacherDao、AdminDao ├── com/xxx/servlet/ # 控制器层,LoginServlet、StudentServlet、ScoreServlet 等 ├── com/xxx/entity/ # 实体类,Student、Teacher、Course、Score ├── com/xxx/util/ # 工具类,DBUtil(数据库连接)、MD5Util(密码加密) └── com/xxx/filter/ # 过滤器,LoginFilter(登录状态校验) webapp/ ├── css/ # bootstrap.min.css、style.min.css 等 ├── js/ # 前端脚本 ├── jsp/ # 学生页面、教师页面、管理员页面 └── WEB-INF/web.xml # 核心配置,Servlet 映射和过滤器注册

这里最值得看的是 web.xml 中的 Servlet 映射方式。老项目的常见写法是把所有请求按角色前缀分发,例如/student/*交给 StudentServlet,/teacher/*交给 TeacherServlet,/admin/*交给 AdminServlet。这样做的优点是权限校验集中在过滤器中做,每个角色只能访问自己前缀下的页面。

我一般拿到源码后会先跑一遍登录逻辑,搞清楚 Servlet 映射关系。以登录为例,流程是:用户提交表单 → LoginServlet 根据用户名和密码查询用户表 → 查出用户角色字段(role)→ 重定向到不同角色的首页。这个过程中,请求会先经过 LoginFilter,如果 session 里没有登录标记,直接拦截跳回登录页。

2.3 部署步骤:从源码包到浏览器跑起来

这是整个课设能否验收的第一步,本地跑不起来,后面全是空谈。我用 Eclipse(或者 IDEA)按下面的顺序操作:

第一步:导入项目

IDEA 选择 File → New → Project from Existing Sources,选中源码包根目录,如果是 Maven 项目会自动识别 pom.xml;如果是普通 Web 项目,选择 Web 目录即可。Eclipse 则用 Import → Existing Projects into Workspace,注意勾选“Copy projects into workspace”,避免源文件被外部改动影响。

第二步:配置 Tomcat

在 IDEA 里打开 Run → Edit Configurations,新增 Tomcat Server → Local。配置 Application server 指向本地 Tomcat 安装目录,然后在 Deployment 页签点 +,选择该项目的 Artifact,Application context 填/score(或源码包里 web.xml 定义的 context,注意看项目里有没有 context root 配置)。

第三步:初始化数据库

启动前必须把 SQL 脚本导入 MySQL。源码包里通常有一个database.sql或score.sql文件,里面包含建库和插入初始数据的语句。用命令行导入:

mysql -u root -p < database.sql

如果源码包没给 SQL 文件,就需要自己根据 DAO 里的 SQL 反推表结构。不过这个项目比较完整,一般在database或sql目录下能找到初始化脚本。导入后确认三件事:数据库名、用户名、密码是否和 DBUtil.java 里配置的一致。

第四步:修改数据库连接配置

DBUtil.java 里的连接串是这个项目的生命线,最常见的跑不起来就死在连接串上:

// com/xxx/util/DBUtil.java private static final String URL = "jdbc:mysql://localhost:3306/score_db?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai"; private static final String USER = "root"; private static final String PASSWORD = "123456";

注意 JDK 8 配 MySQL 8.0 时,serverTimezone参数必须加上,否则报时区错误;useSSL=false是避免 MySQL 8.0 默认 SSL 认证导致的连接警告。密码改为你自己的本地数据库密码。改完后重新编译,再启动 Tomcat,访问http://localhost:8080/score/login.jsp应该能看到登录页。

3. 数据库设计与初始化数据:三张核心表怎么支撑三个角色的权限体系

3.1 表结构拆解:用户-角色-业务数据的分离方式

打开 SQL 文件后,整个系统的数据库设计思路会清晰很多。大多数课设版本采用这样的核心表结构:用户表(user)、学生表(student)、教师表(teacher)、课程表(course)、成绩表(score)。其中 user 表存登录凭据和角色标识,而 student 表和 teacher 表存业务扩展信息。

我拆的这个版本,关键点在于用户表与业务表是分离的还是合一的。如果合一(比如 student 表直接带 password 字段),登录逻辑会比较简单;如果分离,则登录后还要根据角色去查对应业务表,获取姓名、班级等信息。建议拿到源码后先看 LoginServlet 的查询逻辑,这决定了你改密码逻辑的复杂程度。

-- 用户表:只负责登录和角色识别 CREATE TABLE `user` ( `uid` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(20) NOT NULL, `password` varchar(64) NOT NULL, `role` varchar(10) NOT NULL COMMENT 'admin / teacher / student', PRIMARY KEY (`uid`) ); -- 学生表:业务扩展信息 CREATE TABLE `student` ( `sid` int(11) NOT NULL, `name` varchar(20) NOT NULL, `class_id` int(11) NOT NULL, `gender` varchar(4) DEFAULT NULL, PRIMARY KEY (`sid`) );

3.2 初始化数据与测试账号:三分钟跑通全功能

导入 SQL 文件后,通常里面会带几个初始账号。管理员账号一般是 admin/admin123 或 admin/123456,教师账号类似 t001/123456,学生账号类似 s001/123456。但不同版本的源码初始账号可能不同,最稳妥的办法是直接查 user 表:

SELECT username, password, role FROM user;

如果发现密码字段是加密后的 MD5 字符串,自己又不知道原始密码,有个小技巧:先把已知的明文密码的 MD5 值算出来,然后 UPDATE 上去。

UPDATE user SET password = 'e10adc3949ba59abbe56e057f20f883e' WHERE username = 'admin';

这是123456的 MD5 值,很多课设版密码验证就是用 DigestUtils.md5Hex 或自己实现的 MD5 工具。改完后再用 admin/123456 登录,百试百灵。如果登录还是失败,那就要看 LoginServlet 里密码比对逻辑是纯字符串相等还是加密后比对。

我在拆这个项目时发现一个容易忽略的设计:教师账号除了在 user 表里有记录,还会在 teacher 表里对应一条业务记录,两边的 uid 需要一致。管理员在后台修改教师信息时,实际上要同步更新两张表,这是事务操作的典型场景。

3.3 成绩表的关键设计:课程与学生的多对多关系

成绩表在课设项目里是业务核心,通常这样建:

CREATE TABLE `score` ( `id` int(11) NOT NULL AUTO_INCREMENT, `student_id` int(11) NOT NULL, `course_id` int(11) NOT NULL, `score` decimal(5,2) DEFAULT NULL, `term` varchar(20) DEFAULT NULL COMMENT '学期,如 2024-2025-1', PRIMARY KEY (`id`), UNIQUE KEY `uk_stu_course` (`student_id`, `course_id`) );

这里最值得借鉴的是UNIQUE KEY约束——同一个学生同一门课只能有一条成绩记录,防止教师在录入时重复插入导致数据混乱。教师录入成绩时,前端会先按学生列表加载界面,提交到服务端后先判断是否已存在记录,存在则执行 UPDATE,不存在则 INSERT,这就是常见的 insert-or-update 逻辑。

4. 核心功能实现拆解:登录过滤器、成绩录入与角色分发

4.1 登录过滤器:权限控制的第一道防线

这个项目最值得看的功能点之一是过滤器。登录校验不放在每个 Servlet 里重复写,而是抽成一个 LoginFilter,对所有/jsp/*路径做拦截:

// com/xxx/filter/LoginFilter.java public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) resp; HttpSession session = request.getSession(false); // 判断是否已登录 if (session == null || session.getAttribute("user") == null) { // 未登录则跳转登录页,注意用绝对路径 response.sendRedirect(request.getContextPath() + "/login.jsp"); return; } chain.doFilter(request, response); }

这段代码逻辑很直白,但有几个细节容易翻车:getSession(false)表示不主动创建 session,如果用户根本没登录过,返回 null,直接拦截;request.getContextPath()必须拼上,否则在非根路径部署时跳转地址会 404;放行时调用chain.doFilter()不能丢。很多改版项目的登录失效问题都出在这三个细节上。

4.2 教师录入成绩:核心业务流的页面与 Servlet 协作

教师模块最核心的功能是成绩录入。前端页面通常是三联动下拉框:先选班级,再选课程,然后列出该班选该课的所有学生,最后逐个输入分数。这种联动可以用 AJAX 实现,也可以用最简单的表单回传方式实现。

老派但好用的做法是:页面 A(选择班级和课程)提交到 ScoreServlet,Servlet 查询出学生列表后,请求转发到录入页面 B,页面 B 在 JSP 中用 JSTL 循环渲染学生表格:

<!-- 录入页面中的核心片段 --> <form action="ScoreServlet?action=addScore" method="post"> <table> <c:forEach items="${studentList}" var="stu"> <tr> <td>${stu.name}</td> <td> <input type="hidden" name="studentId" value="${stu.sid}"> <input type="number" name="score" min="0" max="100" required> </td> </tr> </c:forEach> </table> <input type="hidden" name="courseId" value="${courseId}"> <button type="submit">批量提交</button> </form>

这里有一个坑:每个学生的 input 输入框用循环渲染后,name 属性重复了,服务端接收时要用request.getParameterValues("score")拿到数组,再按顺序取出和 studentId 一一对应。

// ScoreServlet 中的核心逻辑 String[] studentIds = request.getParameterValues("studentId"); String[] scores = request.getParameterValues("score"); for (int i = 0; i < studentIds.length; i++) { int sid = Integer.parseInt(studentIds[i]); double scoreVal = Double.parseDouble(scores[i]); // 先查是否已有记录,有则 update,无则 insert boolean exists = scoreDao.exists(sid, courseId); if (exists) { scoreDao.updateScore(sid, courseId, scoreVal); } else { scoreDao.addScore(sid, courseId, scoreVal); } }

参数说明:action=addScore是 Servlet 内部的分发参数,同一个 Servlet 用 action 区分“查询成绩列表”“录入成绩”“修改成绩”三种操作;min="0" max="100"是前端校验,但服务端必须再校验一次,因为绕过前端直接 POST 也能提交非法分数。我在实际改这个项目时习惯在 Service 层加一道分数范围判断,不满足则抛异常回滚。

4.3 管理员模块的增删改查:三张关联表的删除顺序

管理员的班级管理功能是课设答辩时导师喜欢追问的地方。删除一个班级,不只是删 class 表里的记录,还要处理班级下的学生。如果 student 表里 class_id 外键直接约束,DELETE 会失败或报外键错误。

常见的解决方案有两种:第一种是物理删除前先查询该班有多少学生,有学生则拒绝删除并提示“请先清空该班级学生”;第二种是逻辑删除,在班级表加一个 enabled 字段,默认 1,删除时改为 0,查询时过滤enabled=1。课设项目用第一种居多,因为逻辑简单好解释。看这个项目的源码时,可以看看班级删除逻辑里有没有处理这种关联数据,如果没有,答辩时正好可以当作一个改进点提出来。

5. 避坑指南与常见问题排查:课设运行中的五条血泪经验

5.1 Tomcat 启动报 404,但项目明明部署了

现象:访问http://localhost:8080/score/login.jsp直接 404,Tomcat 控制台没有报错。

原因:最常发生的是 Application context 配置不对。IDEA 部署时默认 context 可能是/score_war_exploded,而你访问的是/score,路径对不上必然 404。另一类情况是 JSP 文件没放在 webapp 根目录下,而是丢在了 src/main/webapp 的子目录里但路径写错。

解决:到 Run → Edit Configurations → Deployment 页签查看 Application context,把访问路径改成与之完全一致。或者统一都用/,再通过http://localhost:8080/login.jsp访问。

提示:改完 context 必须重启 Tomcat,热部署有时不生效。

5.2 数据库连接报 Access denied for user

现象:启动后页面能打开,但一查数据就报错,控制台输出Access denied for user 'root'@'localhost'。

原因:DBUtil.java 中的密码和你本地 MySQL 实际密码不一致。很多人用的是 MySQL 8.0,默认密码规则 caching_sha2_password 和 mysql-connector-java 5.x 驱动不兼容,也会报认证失败。

解决:如果是密码问题,改成自己的密码即可;如果确认密码没写错还是失败,看驱动包版本。mysql-connector-java 8.0.33 以上版本同时兼容 MySQL 5.7 和 8.0,建议直接用 8.x 驱动,然后把 URL 中的useSSL=false&serverTimezone=Asia/Shanghai补全。

5.3 页面中文乱码,表单提交后数据变成问号

现象:登录页中文正常,但提交数据后页面显示乱码,或者从数据库查出来的中文变成???。

原因:三处编码不一致——JSP 页面编码、Servlet 请求编码、数据库连接串编码。最常见的是一处漏了 UTF-8。

解决:按三层检查。JSP 文件顶部确保有<%@ page contentType="text/html;charset=UTF-8" language="java" %>;Servlet 里开始处加上request.setCharacterEncoding("UTF-8");数据库连接串确认带characterEncoding=utf8。改完重启后再测。

注意:Tomcat 8.5+ 默认请求编码是 UTF-8,高版本 Tomcat 一般不需要额外配置,但加了肯定没错。

5.4 成绩录入发现学生下拉框为空或数据错位

现象:教师选班级后,学生列表加载不出来,或者全班只有一个学生的数据重复显示。

原因:SQL 查询条件写错,常见的是只查了 student 表没关联 class 表,把全年级学生都查出来了;或者下拉框的 value 绑定的是学生名称而非学生 ID,导致提交后 ID 乱套。

解决:检查学生列表的 SQL,确认带WHERE class_id = ?条件;前端下拉框的 option value 绑定stu.sid,显示文本绑定stu.name。这里最容易翻车的确实是 ID 没有正确绑定,导致后端拿到的一串 ID 全是姓名。

5.5 管理员删课时把成绩也删没了

现象:删一门课程后,教师查看成绩分析时发现该课程历史成绩消失。

原因:score 表对 course_id 有外键级联删除,或者删除课程时手动执行了DELETE FROM score WHERE course_id=...。如果是课设验收阶段,这不算错,但答辩时导师会问“成绩有保留价值,为什么删课要连带删成绩”。

解决:建议改成逻辑删除,course 表加 enabled 字段,教师端查成绩只查enabled=1的课程;或者删除课程前弹窗提醒“该课程存在 N 条成绩记录,确认删除吗”,用户确认后在业务层先删成绩再删课程。这个改造不需要动表结构,只改 Service 层的删除逻辑即可。

6. 进阶用法:在课程设计基础上加一个成绩分析页,答辩时多一个亮点

如果时间充裕,我强烈建议在做完基本功能后,加一个“课程成绩分析”页面。这是导师最喜欢看到的设计点,因为它把 CRUD 变成了一个带有统计分析功能的模块,显示了你对 SELECT 聚合函数的掌握。

实现思路很简单:按课程统计最高分、最低分、平均分、及格率,用一条 SQL 搞定:

SELECT c.cname, MAX(s.score) AS max_score, MIN(s.score) AS min_score, AVG(s.score) AS avg_score, SUM(CASE WHEN s.score >= 60 THEN 1 ELSE 0 END) / COUNT(*) * 100 AS pass_rate FROM score s JOIN course c ON s.course_id = c.cid WHERE s.course_id = 2 GROUP BY c.cname;

这条 SQL 里有几个点可以在答辩时展开:CASE WHEN条件计数是统计及格率的标准写法,AVG()默认返回小数,如果需要四舍五入用ROUND(AVG(score), 1)。在 ScoreServlet 里加一个action=analyze的分支,把 query 结果放进 request 域,转发到新 JSP 页面展示:

// ScoreServlet 中新增的 analyze 分支 double avgScore = resultMap.get("avg_score") != null ? ((Number) resultMap.get("avg_score")).doubleValue() : 0.0; request.setAttribute("avgScore", Math.round(avgScore * 10) / 10.0); request.setAttribute("passRate", Math.round((Double) resultMap.get("pass_rate") * 100) / 100.0); request.getRequestDispatcher("/jsp/teacher/scoreAnalyze.jsp").forward(request, response);

页面端可以配合一个极简的柱状图——不用引入 ECharts,直接用 div 宽度模拟柱子,纯 CSS 输出排名前五的学生分数对比。这样做的好处是整个功能只依赖 JSP 和 CSS,不增加任何额外 jar 包,但视觉效果好,答辩时有话说:

<!-- 及格率可视化:一个简易横向进度条 --> <div class="progress"> <div class="progress-bar" style="width: ${passRate}%;"> 及格率 ${passRate}% </div> </div>

如果想把可视化做得更完整一些,对每门课的成绩分段统计(90-100、80-89、70-79、60-69、0-59)再配一个表格,基本就是一个完整的“学生学习质量分析”模块了。导师常见的追问是“这个分析有什么实际用处”,回答方向是:院系可以根据课程平均分评估教学效果,辅导员可以根据及格率锁定重点帮扶班级,比单纯看成绩列表直观得多。

从那以后我再做类似课设项目,都会先花 15 分钟把 SQL 里的聚合查询梳理一遍,确认字段名、关联键、空值处理,再动手写页面。这套项目的价值不完全在于代码本身,而在于它把 JSP + Servlet + MySQL 的完整链路演示清楚了——你拿到它之后,顺着登录过滤器往下追,整个请求流转就刻在脑子里了。希望帮到你。

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

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

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

立即咨询