简介:这是一套基于JSP、Servlet、JDBC与MySQL技术栈实现的学生成绩管理系统完整源码包,面向Java Web初学者、课程设计者及毕业设计开发者,帮助解决教务场景下学生、教师、管理员三类角色的信息管理需求。系统集成MD5加密算法,代码结构完整且注释详尽,涵盖考勤管理、请假审批、选课、成绩查询与修改个人信息等学生功能,教师端在此基础上增加选课信息管理、班级与课程管理、成绩导入导出及可视化统计,管理员端则额外提供教师管理模块。压缩包共93个文件,约1.7MB,包含24个java源文件、24个class编译文件、21个jsp页面、6个jar依赖包,以及css样式、properties配置、xml映射、sql建库脚本和doc说明文档,便于快速部署与二次开发。目前已有1487人学习下载,适合对照源码理解MVC分层与数据库交互流程,也可作为课程设计或毕业设计的参考模板。
1. 一个 .rar 包丢过来:JavaEE 学生成绩管理系统到底能不能直接跑起来
拿到「javaEE学生成绩管理系统(源码+数据库+文档).rar」这种压缩包,第一反应通常不是兴奋,而是警惕——里面到底是能跑的项目,还是一堆截图和半成品。我见过太多学生课设包,解压后源码缺 jar、数据库只有 .mdf 没有 .sql、文档写的是「系统采用 B/S 架构」这种废话。但这类项目本身是有价值的:它覆盖了 JavaEE 最核心的一整套链路——Servlet 容器、JDBC 连接池、事务控制、前后端数据流转,以及数据库增删改查的完整闭环。如果你正在做课设、准备面试项目复盘,或者想找一个结构完整的小型管理系统练手,这个方向值得投入。问题在于,你得先判断它能不能跑,再决定要不要改。下面按「先跑通、再拆解、后改造」的顺序,把这类项目从解压到二次开发的路径讲清楚。
2. 解压后先别急着点运行:判断这个包值不值得投入
2.1 目录结构里藏着项目质量的信号
把 .rar 解压后,先看根目录。一个结构清晰的 JavaEE 学生成绩管理系统,通常长这样:
student-score-system/ ├── src/ # Java 源码 │ └── com/xxx/ │ ├── controller/ # Servlet 或 SpringMVC Controller │ ├── service/ # 业务逻辑 │ ├── dao/ # 数据库访问层 │ ├── entity/ # 实体类 │ └── util/ # 工具类(DBUtil、MD5 等) ├── WebContent/ 或 web/ # 网页资源 │ ├── WEB-INF/ │ │ ├── web.xml # 部署描述符 │ │ └── lib/ # 第三方 jar │ ├── css/ js/ images/ │ └── *.jsp ├── sql/ # 数据库脚本 │ └── student_score.sql ├── doc/ # 文档 │ └── 需求分析.docx └── .classpath / .project # Eclipse 工程文件如果解压后只有src和几个.java文件,没有lib目录、没有.sql、没有web.xml,那基本可以判定是「半成品」——作者可能只提交了源码,依赖和数据库需要你自己补。这种情况不是不能救,但工作量会翻倍。
判断优先级:有 .sql 脚本 > 有 lib 目录 > 有 web.xml > 有文档。四样齐全,说明作者至少跑通过一次;缺两样以上,建议先评估补全成本再动手。
2.2 数据库脚本先看三件事:建库语句、字符集、初始数据
打开sql目录下的.sql文件,不要直接导入,先扫一遍:
- 有没有
CREATE DATABASE:如果只有CREATE TABLE,说明作者假设你已经建好库了,你得自己补CREATE DATABASE student_score DEFAULT CHARACTER SET utf8mb4; - 字符集是不是 utf8 或 utf8mb4:如果是
latin1,中文成绩单会变乱码,导入前必须全局替换 - 有没有
INSERT初始数据:没有初始数据的话,登录账号密码你得从代码里反推,或者自己往user表插一条
常见做法是先用文本编辑器打开 .sql,搜索CHARSET和INSERT INTO,确认这两处再导入。如果脚本里用了utf8但你的 MySQL 是 8.0 默认utf8mb4,一般兼容,但排序规则可能报错,导入时加--default-character-set=utf8mb4更稳。
2.3 技术栈版本对不上,是这类包最大的坑
课设包最常见的技术栈组合是:JDK 8 + Tomcat 8/9 + MySQL 5.7 + Servlet 3.1。如果你本地装的是 JDK 17 + Tomcat 10 + MySQL 8.0,直接跑大概率翻车。原因:
- Tomcat 10 把
javax.servlet换成了jakarta.servlet,老代码里的import javax.servlet.*全部编译不过 - MySQL 8.0 的 JDBC 驱动类名从
com.mysql.jdbc.Driver变成了com.mysql.cj.jdbc.Driver,连接 URL 还要加时区参数 - JDK 17 移除了
javax.xml.bind等模块,如果项目用了 JAXB 相关工具类,会报ClassNotFoundException
所以第一步不是改代码,而是对齐版本。最省事的做法:装一个 JDK 8 和 Tomcat 9,用独立环境跑通,再考虑升级。如果你非要用新版本,那就得做好改包名、换驱动、补依赖的准备。
提示:在 IDE 里先看
.classpath或pom.xml(如果有),确认项目原本用的 JDK 版本和 Servlet 版本,再决定本地环境怎么配。
3. 把项目跑起来:从导入 IDE 到登录成功的完整链路
3.1 用 Eclipse 或 IDEA 导入非 Maven 老项目
这类课设包大多不是 Maven 项目,没有pom.xml,依赖全在WEB-INF/lib下。导入步骤:
Eclipse 路线:
File → Import → Existing Projects into Workspace- 选择解压后的根目录,如果识别不到项目,说明缺少
.project文件,需要New → Project → Dynamic Web Project手动建,再把src和WebContent拷进去 - 右键项目 →
Properties → Project Facets,确认Dynamic Web Module版本和Java版本与代码匹配 Properties → Java Build Path → Libraries,检查WEB-INF/lib下的 jar 是否全部加入 classpath
IDEA 路线:
File → New → Project from Existing Sources,选根目录- 如果提示不是 Maven/Gradle,选
Import project from external model → Eclipse,让 IDEA 读.classpath File → Project Structure → Modules → Dependencies,把WEB-INF/lib下的 jar 全部加进去Project Structure → Artifacts,新建一个Web Application: Exploded,输出目录指向out/artifacts/xxx_war_exploded
导入后先别急着配 Tomcat,先看src下有没有红色报错。如果有The import javax.servlet cannot be resolved,说明 Servlet API 没加进 classpath——Tomcat 的lib/servlet-api.jar需要手动加,或者用 IDE 的 Server Runtime 自动关联。
3.2 配置数据库连接:DBUtil 里的四个参数怎么改
找到util包下的DBUtil.java或BaseDao.java,里面通常硬编码了连接信息:
public class DBUtil { private static final String DRIVER = "com.mysql.jdbc.Driver"; private static final String URL = "jdbc:mysql://localhost:3306/student_score?useUnicode=true&characterEncoding=utf8"; private static final String USER = "root"; private static final String PASSWORD = "123456"; public static Connection getConnection() throws Exception { Class.forName(DRIVER); return DriverManager.getConnection(URL, USER, PASSWORD); } }你需要改三处:
- DRIVER:MySQL 5.7 用
com.mysql.jdbc.Driver,MySQL 8.0 用com.mysql.cj.jdbc.Driver - URL:数据库名要和 .sql 脚本里建库名一致;MySQL 8.0 还要加
&serverTimezone=Asia/Shanghai&useSSL=false - USER / PASSWORD:改成你本地 MySQL 的账号密码
改完后写一个main方法测连接:
public static void main(String[] args) { try (Connection conn = DBUtil.getConnection()) { System.out.println("数据库连接成功:" + conn.getMetaData().getURL()); } catch (Exception e) { e.printStackTrace(); } }如果报Access denied for user,检查密码;如果报Unknown database,说明库没建;如果报Communications link failure,检查 MySQL 服务是否启动、端口是否 3306。
3.3 部署到 Tomcat 并验证登录流程
数据库通了之后,配置 Tomcat:
- 在 IDE 里新建 Server,指向本地 Tomcat 安装目录
- 把项目添加到 Server 的 Deployment 中,设置 context path(比如
/score) - 启动 Server,浏览器访问
http://localhost:8080/score/login.jsp
如果页面能打开但登录报 500,看 Tomcat 控制台堆栈。常见原因:
ClassNotFoundException: com.mysql.jdbc.Driver:驱动 jar 没放进WEB-INF/libNoSuchMethodError:jar 版本冲突,比如同时存在两个不同版本的commons-dbutilsSQLException: Table 'student_score.user' doesn't exist:表名不对,或者 .sql 没导入完整
登录成功后,先别急着改功能,把「学生列表 → 新增成绩 → 修改成绩 → 删除成绩」这条增删改查链路走一遍。哪一步报错,就定位到对应的 Servlet 和 SQL 语句。这一步走通,说明项目骨架是完整的,后面二次开发才有意义。
4. 拆解源码:成绩管理系统的三层架构与数据库设计
4.1 Servlet + Service + DAO 的调用链怎么读
这类项目通常采用经典三层架构,以「查询学生成绩」为例,调用链是:
浏览器请求 /score/list → ScoreServlet.doGet() → ScoreService.queryAll() → ScoreDao.findAll() → DBUtil.getConnection() → SQL: SELECT * FROM score读代码的顺序建议从后往前:先看entity包有哪些实体类(Student、Score、Course、User),再看dao包的 SQL 语句,最后看controller包的请求映射。这样你能快速知道系统支持哪些功能,而不是被 JSP 页面绕晕。
web.xml里配置了 Servlet 的映射关系,比如:
<servlet> <servlet-name>ScoreServlet</servlet-name> <servlet-class>com.xxx.controller.ScoreServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>ScoreServlet</servlet-name> <url-pattern>/score/*</url-pattern> </servlet-mapping>看到/score/*,就知道所有以/score开头的请求都会进这个 Servlet。然后在doGet或doPost里根据request.getParameter("method")或request.getRequestURI()分发到不同方法。
4.2 成绩表设计的三个关键字段
打开 .sql 里的score表,核心字段通常是:
| 字段名 | 类型 | 说明 |
|---|---|---|
| id | int / bigint | 主键,自增 |
| student_id | varchar / int | 学号,关联 student 表 |
| course_id | int | 课程 ID,关联 course 表 |
| score | decimal(5,2) | 成绩,保留两位小数 |
| exam_time | datetime | 考试时间 |
这里有两个容易出问题的地方:
- student_id 用 varchar 还是 int:如果学号是
2021001这种,用 varchar 更安全,避免前导零丢失;如果用 int,插入001会变成1 - score 用 decimal 还是 float:成绩涉及计算(平均分、总分),用 decimal 避免浮点误差,
decimal(5,2)表示最大 999.99,够用
如果 .sql 里score表没有外键约束,别急着加。课设项目数据量小,外键反而增加插入顺序的麻烦。用JOIN查询时手动关联即可。
4.3 用一条 SQL 验证数据完整性
导入数据后,跑一条关联查询,确认学生、课程、成绩三张表能对上:
SELECT s.student_no, s.name AS student_name, c.course_name, sc.score FROM score sc JOIN student s ON sc.student_id = s.id JOIN course c ON sc.course_id = c.id WHERE sc.score < 60 ORDER BY sc.score ASC;这条 SQL 查所有不及格记录。如果返回空但你知道有不及格数据,说明关联字段对不上——可能是student_id存的是学号而不是主键 id。这种情况在课设包里很常见,作者图省事直接用学号关联,导致JOIN条件写错。解决办法:要么改 SQL 用s.student_no = sc.student_id,要么统一改成主键关联。
5. 避坑与排查:这类课设包最容易翻车的五个地方
5.1 中文乱码:从数据库到 JSP 的完整链路
现象:页面显示??????或æˆç»©,数据库里存的中文也乱。
原因:字符集不统一。常见断点有四处——MySQL 建库时用了 latin1、JDBC URL 没加characterEncoding=utf8、JSP 页面pageEncoding写错、Tomcat 的server.xml里 Connector 没加URIEncoding="UTF-8"。
解决:按链路逐段排查。先SHOW CREATE DATABASE student_score;确认库字符集;再检查 DBUtil 的 URL;然后看 JSP 头部<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" %>;最后在 Tomcatconf/server.xml的<Connector>标签加URIEncoding="UTF-8"。四处都对了,乱码基本消失。
5.2 驱动版本不匹配:ClassNotFoundException 的两种面孔
现象:启动报ClassNotFoundException: com.mysql.jdbc.Driver,或者No suitable driver found for jdbc:mysql://...。
原因:第一种是 jar 没放进WEB-INF/lib;第二种是 jar 放了但版本和 URL 写法不匹配——MySQL 8.0 的驱动不认com.mysql.jdbc.Driver,必须用com.mysql.cj.jdbc.Driver。
解决:先确认WEB-INF/lib下有mysql-connector-java-x.x.x.jar;再看驱动类名和 URL 是否配套。如果用的是 MySQL 8.0,URL 还要加?serverTimezone=Asia/Shanghai,否则报时区错误。
5.3 事务没提交:插入成功但查不到数据
现象:执行新增成绩后提示成功,但列表页看不到新记录,重启 Tomcat 后又出现了。
原因:DAO 层用了Connection.setAutoCommit(false)开启事务,但忘了commit()。连接关闭时未提交的事务被回滚,数据没落库。
解决:检查 DAO 里涉及setAutoCommit(false)的方法,确保try块最后有conn.commit(),catch块里有conn.rollback()。如果项目用了连接池(如 Druid),还要注意连接归还前是否自动提交。
5.4 JSP 页面 404:路径写错还是 web.xml 配错
现象:访问login.jsp正常,但提交表单后 404。
原因:表单action路径写的是相对路径,比如action="login",但实际 Servlet 映射是/user/login。或者web.xml里<url-pattern>配的是/login,但表单提交到了/score/login。
解决:在浏览器 F12 看 Network 里请求的实际 URL,对比web.xml的映射。建议表单action用绝对路径${pageContext.request.contextPath}/user/login,避免相对路径歧义。
5.5 修改功能报错:主键丢失或类型不匹配
现象:点击「修改」按钮,页面报NumberFormatException或更新后数据错乱。
原因:修改页面通常通过 URL 传参?id=1,如果 id 是字符串但 DAO 里用Integer.parseInt转换,传空值或非数字就崩。另一种情况是更新 SQL 的WHERE id=?用了错误的参数,导致更新了多条记录。
解决:在 Servlet 里先判空再转换,String id = request.getParameter("id"); if(id == null || id.isEmpty()) { response.sendRedirect("list"); return; }。更新 SQL 执行前打印参数日志,确认WHERE条件唯一。
6. 二次开发进阶:把课设包改成能写进简历的项目
6.1 从 JDBC 裸连升级到 Druid 连接池
原项目用DriverManager.getConnection()每次新建连接,性能差且容易耗尽。改成 Druid 连接池,只需三步:
第一步,在WEB-INF/lib下加入druid-1.2.x.jar。
第二步,新建druid.properties:
driverClassName=com.mysql.cj.jdbc.Driver url=jdbc:mysql://localhost:3306/student_score?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username=root password=123456 initialSize=5 maxActive=20 maxWait=3000第三步,改写 DBUtil:
public class DBUtil { private static DataSource dataSource; static { try { Properties props = new Properties(); props.load(DBUtil.class.getClassLoader().getResourceAsStream("druid.properties")); dataSource = DruidDataSourceFactory.createDataSource(props); } catch (Exception e) { throw new ExceptionInInitializerError(e); } } public static Connection getConnection() throws SQLException { return dataSource.getConnection(); } }改完后,DAO 层代码不用动,因为getConnection()返回的仍然是Connection对象。但连接关闭时,close()会归还到池里而不是真正断开。这一步改完,项目在面试时就能聊「连接池参数调优」了。
6.2 加一个分页查询:从 SQL 到 JSP 的完整实现
原项目的成绩列表通常是一次性SELECT *,数据一多就卡。加分页需要改三处:
SQL 层,MySQL 用LIMIT:
SELECT * FROM score ORDER BY id DESC LIMIT ?, ?;第一个?是偏移量(pageNum - 1) * pageSize,第二个?是每页条数。
DAO 层,新增两个方法:
public List<Score> findByPage(int pageNum, int pageSize) throws SQLException { String sql = "SELECT * FROM score ORDER BY id DESC LIMIT ?, ?"; try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { ps.setInt(1, (pageNum - 1) * pageSize); ps.setInt(2, pageSize); ResultSet rs = ps.executeQuery(); List<Score> list = new ArrayList<>(); while (rs.next()) { Score s = new Score(); s.setId(rs.getInt("id")); s.setScore(rs.getBigDecimal("score")); list.add(s); } return list; } } public int countAll() throws SQLException { String sql = "SELECT COUNT(*) FROM score"; try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql); ResultSet rs = ps.executeQuery()) { rs.next(); return rs.getInt(1); } }Servlet 层,接收pageNum参数,计算总页数,把list和totalPages塞进request,转发到 JSP。JSP 底部渲染上一页/下一页链接。
分页做完,项目就有了「处理大数据量」的亮点,比单纯增删改查更有说服力。
6.3 用 Postman 验证接口,而不是只靠浏览器
课设项目通常没有接口文档,调试全靠点页面。建议用 Postman 直接测 Servlet:
- 方法选
POST - URL 填
http://localhost:8080/score/score?method=add - Body 选
x-www-form-urlencoded,填studentId=1&courseId=2&score=85
这样能绕过 JSP 表单,直接验证后端逻辑。如果 Postman 返回 JSON 但页面报错,说明问题在前端;如果 Postman 也报错,问题在后端。定位效率比反复刷新页面高得多。
6.4 一个我踩过的坑:别在课设包上直接改包名
最后说一个血泪教训。我曾经接手一个课设包,觉得原包名com.xxx太随意,想改成com.myname.score。结果 IDE 重构后,web.xml里的<servlet-class>没同步更新,JSP 里<%@ page import="com.xxx.entity.*" %>也漏改了几处,启动直接 500。排查了半小时才发现是包名不一致。
后来我的习惯是:先跑通原包名,确认功能正常,再决定要不要重构。如果非要改,用 IDE 的Refactor → Rename全局替换,改完搜索一遍web.xml、.jsp、.properties里有没有残留旧包名。这个习惯帮我省了很多后悔药。
希望帮到你。
本文还有配套的精品资源,点击获取