
简介这是一套基于JAVA与MYSQL开发的学生成绩管理系统项目文件面向正在学习Java GUI编程、数据库应用或准备课程设计的开发者。系统覆盖学生信息管理、成绩录入查询、选课管理、数据统计分析以及用户权限控制等模块能帮助理解一个完整管理系统的前后端交互流程。压缩包共104个文件包含29个java源文件、68个class编译文件、2个jar依赖库、2个project工程文件以及1个docx说明文档整体仅714KB目录结构清晰便于按模块查看目前已有600人学习下载从包内类分布可看出输入、查询、选课、信息修改等功能模块均有对应实现。通过阅读源码可以掌握Swing/JavaFX界面设计、JDBC连接MySQL实现增删改查、选课冲突处理及多角色权限校验等关键技巧docx文档和工程配置也有助于快速运行和二次开发适合作为Java课程设计或毕业设计的参考范例。1. 这个 Java 学生成绩管理系统包里到底有什么值得你下载前阵子帮朋友改一个课程设计他交的正是这种 Java MySQL 的学生成绩管理系统源码包。我原本以为又是一堆半成品代码打开 RAR 才发现里面把登录、学生管理、教师管理、课程管理和成绩统计都串起来了数据库脚本、配置文件、部署说明也齐全。如果你是要应付课程设计、毕业设计或者想快速看懂一个传统 JSP/Servlet 项目的完整链路这个包能让你省掉大量从零搭框架的时间。这份资源本质是一个可运行、可二次开发的 Java Web 项目核心技术栈是 JSP/Servlet JDBC MySQL不是现在流行的 Spring Boot但恰恰因为它足够传统反而更容易看清请求是怎么从浏览器一层层走到数据库的。下面我按自己拆包的习惯从解压到避坑把整个跑通流程和关键代码一次讲清楚。2. 拆开 RAR 先看结构目录、数据库表、权限控制拿到任何源码包我从来不会急着点运行先把压缩包里的目录结构、SQL 脚本和配置文件过一遍心里有数再动手能少翻车很多。2.1 解压后先认清目录结构别被文件名骗了RAR 包本身没什么好说Windows 下用 WinRAR 或 7-Zip 都能解开7-Zip 可以解压 RAR 文件吗完全可以而且免费。但要注意老 RAR 包里经常夹带中文文件名WinRAR 默认用本地字符集解压没问题换成 7-Zip 如果解出来一堆乱码去设置里把压缩包名和文件名编码改成 GBK 再解一次就行。解开后的典型结构是这样的Java学生成绩管理系统/ ├─ src/ │ ├─ com/edu/score/ │ │ ├─ bean/ // 实体类Student, Teacher, Course, Score, Admin │ │ ├─ dao/ // JDBC 数据访问层 │ │ ├─ servlet/ // 控制器处理登录和业务请求 │ │ ├─ util/ // DBUtil 数据库连接工具 │ │ └─ filter/ // 登录拦截器 ├─ WebRoot/ │ ├─ WEB-INF/ │ │ ├─ web.xml // Servlet 注册、欢迎页、过滤器 │ │ └─ lib/ // mysql-connector-java.jar 等依赖 │ ├─ css/ js/ jsp/ // 前端页面 ├─ sql/ │ └─ student_score.sql // 建库建表语句 初始化测试数据 └─ README.txt // 作者写的运行说明看清楚这个结构你就知道它的运行逻辑了JavaBean 是数据载体DAO 负责和 MySQL 打交道Servlet 接 HTTP 请求并调用 DAOJSP 只做页面展示。没有 Spring 容器管理 Bean也没有 MyBatis 自动映射一切靠手写 JDBC 连接和手动封装 ResultSet。这对初学者反而友好你能看到最原始的数据流。2.2 核心表设计五张表撑起一个成绩管理系统数据库脚本里一般是五张核心表我用表格把字段和关系列出来你自己打开 SQL 脚本对着看会更直观。表名关键字段作用t_adminid, username, password系统管理员登录t_studentid, student_no, student_name, class_name, password学生账号信息t_teacherid, teacher_no, teacher_name, title, password教师账号信息t_courseid, course_no, course_name, credit, teacher_id课程信息关联授课教师t_scoreid, student_id, course_id, score, exam_time学生某门课的成绩记录关系很简单t_score 里的 student_id 指向 t_student.idcourse_id 指向 t_course.idt_course 里的 teacher_id 指向 t_teacher.id。没有多余的多对多关系表成绩表本身就是学生和课程的关联表。这种设计在你写面试题答案里的“数据库三范式”时非常合适但真实场景下少了“班级表”如果后面要按班级统计平均分你可能会想加一张 t_class 表。这个思路后面第六章我会展开说。2.3 权限控制没有框架就用 Filter 拦截 Session传统 JSP/Servlet 项目里权限控制通常不是靠 Spring Security 做的而是在 web.xml 里注册一个 Filter拦截所有 /jsp/* 请求检查 session 里有没有登录用户。下面这段是典型的过滤器配置filter filter-nameAuthFilter/filter-name filter-classcom.edu.score.filter.AuthFilter/filter-class /filter filter-mapping filter-nameAuthFilter/filter-name url-pattern/jsp/*/url-pattern /filter-mapping这个配置的含义是只要浏览器访问项目里 /jsp 目录下的资源都会先经过 AuthFilter 做身份验证。对应 Java 代码里一般会这样实现// AuthFilter.java public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req (HttpServletRequest) request; HttpServletResponse resp (HttpServletResponse) response; HttpSession session req.getSession(false); if (session ! null session.getAttribute(loginUser) ! null) { chain.doFilter(request, response); // 已登录放行 } else { resp.sendRedirect(req.getContextPath() /login.jsp); } }这里的逻辑是session 为空或者里面没有 loginUser 属性就跳回登录页否则放行到目标 JSP。注意不少老系统会写成 session.getAttribute 后只登录用户而角色权限放在各个页面里再判断比如教师页面要求 session 里的 userType 是 teacher。这种方式不够精细但用来做课程设计已经够用。很多 java 面试题喜欢问过滤器、拦截器区别你从这个项目里能直接背出 Filter 的最原始用法。3. 本地跑通JDK、MySQL、Tomcat、配置文件的顺序很关键这一章是决定你能不能“双击就能跑”的关键。很多新手下载完源码后第一反应是用 IDEA 打开直接运行结果报错一堆。我建议你按下面这个顺序来每一步验证成功再进下一步。3.1 环境选型JDK 8 配 MySQL 5.7/8.0 是最稳的组合这类老项目基本是按 JDK 8 写的所以别用 JDK 17 去试除非你想当场体验 lambda 和反射的兼容性地狱。Tomcat 选 8.5 或 9.0这两个版本都支持 javax.servlet 命名空间和项目里的 Servlet API 对得上。MySQL 选 5.7 或 8.0 问题都不大区别在 JDBC 驱动和连接参数下一节细说。环境搭配参照下面组件推荐版本说明JDK1.8项目编译级别是 Java 8Tomcat8.5 / 9.0支持 javax.servletMySQL5.7 / 8.08.0 注意驱动版本要升级到 8.xIDEA2020 后来哪个都行保证能导入 lib 目录下的 jar记住这个组合你去查“java 环境配置”相关教程时不要看到新版本就往上冲老系统配老环境才是防翻车的第一原则。3.2 建库导数据用 source 命令把 SQL 脚本灌进 MySQL解压后先找到 sql/student_score.sql 这个文件它的作用是把数据库、表和初始化数据一次性建好。打开 MySQL 命令行执行# 先登录 MySQL-u 指定用户-p 表示要输密码 mysql -uroot -p # 登录成功后执行 source后面写绝对路径更稳妥 mysql source D:/JavaStudentScore/sql/student_score.sql # 查看是否成功 mysql show databases;执行 source 后如果脚本写得规范你会看到数据库里多了一个 student_score 库。这里有两个细节要注意一是文件路径建议用正斜杠Windows 下反斜杠会被转义二是 MySQL 8.0 下如果报权限错误确认你的 root 用户有 CREATE 权限。我看到有些脚本会写成 utf8 开头如果插入中文数据后 MySQL 客户端乱码执行一遍SET NAMES utf8mb4;再导。3.3 改配置文件URL 参数对账号名、密码、时区全在这里数据库导完接下来要让 Java 代码能连上 MySQL。老项目通常把连接参数放在 src 目录的 db.properties 文件里内容大概是这样的jdbc.drivercom.mysql.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/student_score?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai jdbc.usernameroot jdbc.password123456这里每行都能单独拿出来讲但挑最关键的三个参数说characterEncodingutf8保证 Java 和 MySQL 之间中文不乱码useSSLfalse关掉 SSL 握手本地开发不需要serverTimezoneAsia/Shanghai是 MySQL 8.0 的刚需不写会报时区错误。如果你把项目里 MySQL 驱动版本换成了 8.xdriver 类名要同步改成com.mysql.cj.jdbc.Driver。这是最常见的老驱动配新数据库报错点后面避坑章节我还会专门说。3.4 部署到 Tomcat把源码塞进 webapps 还是用 IDEA 管理部署方式取决于你从 RAR 里解出来的东西是什么形式。如果压缩包里已经有编译好的 war 包最简单的方式是把它复制到 Tomcat 的 webapps 目录下然后启动 Tomcat它会自动解压。命令窗口里执行# 进入 Tomcat 的 bin 目录 cd D:/apache-tomcat-8.5.99/bin # 启动Windows 下用 startup.bat startup.bat # 看到 started 之后访问项目 # 浏览器打开 http://localhost:8080/JavaStudentScore/如果拿到的是源码直接在 IDEA 里新建一个 Web 项目把 src 和 WebRoot 内容复制进去然后在 Project Structure 里添加 Tomcat 运行配置把 Artifact 设为 exploded warDeployment 填根路径 / 或 /JavaStudentScore。第一次启动验证三代先看 Tomcat 日志有没有 Exception再打开登录页能不能显示图片和 CSS最后用默认账号 admin/admin 登录。默认账号一般写在 README.txt 里建议先查那个文件别猜。3.5 初始化账号测试管理员、教师、学生各走一遍登录系统后先别急着点功能把三种角色各登录一次确认 session 切换没问题。管理员登录后通常可以管理教师和学生账号教师登录后能录入成绩、查看自己教的课程学生登录后只能查自己的成绩。如果发现学生登录后能看到老师的管理菜单说明 filter 只做了登录拦截没做角色判断。这种情况不算 bug是系统本身设计简陋真正到答辩时很容易被问住。第四章我看一下代码你就知道该怎么改。4. 核心代码走读从登录到成绩统计的完整链路很多读者下载这个系统不只是为了交差是想从里面学点 JDBC、Servlet 和 SQL 的写法。这部分我挑三个最值得读的代码片段讲每一段都是你以后写简历项目可以直接借鉴的。4.1 登录验证参数校验、Session 写入、密码安全登录是系统第一个入口LoginServlet 的 doPost 方法一般长这样// LoginServlet.java protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding(UTF-8); String username request.getParameter(username); String password request.getParameter(password); String role request.getParameter(role); // 区分 admin/teacher/student // 参数为空直接回登录页不查数据库 if (username null || password null || username.trim().isEmpty() || password.trim().isEmpty()) { request.setAttribute(msg, 用户名或密码不能为空); request.getRequestDispatcher(login.jsp).forward(request, response); return; } User user userDao.login(username.trim(), role); if (user ! null user.getPassword().equals(password)) { HttpSession session request.getSession(); session.setAttribute(loginUser, user); session.setAttribute(role, role); response.sendRedirect(index.jsp); // 成功后重定向避免刷新重复提交 } else { request.setAttribute(msg, 账号或密码错误); request.getRequestDispatcher(login.jsp).forward(request, response); } }这段代码的逻辑很清楚先做空值校验再调用 DAO 查用户比对密码后写入 Session。参数role决定了去哪张表查用户userDao.login 里其实是按 role 选择查 t_admin 还是 t_student 还是 t_teacher。这种做法简单直观但有两个坑一是密码是明文比对查出来的相当于明文存储拿到数据库就能看到所有人的密码二是用户名直接拼 SQL 有注入风险。我习惯把它改成 PreparedStatement 加参数绑定同时建议你用 MD5 或 BCrypt。课程设计里能提一句“我发现原项目密码是明文我改成了加盐哈希”这比整段代码背出来加分更多。4.2 成绩录入单条插入不叫事务和汇总更新在一起才叫事务成绩录入往往不是只 insert 到 t_score 就结束很多系统的学生页面会同步显示平均分或总分。数据一致性要求你把成绩插入和汇总更新放在同一个数据库事务里。看一段常见的 ScoreDao// ScoreDao.java public boolean insertScore(Score score) throws Exception { Connection conn null; PreparedStatement ps null; try { conn DBUtil.getConnection(); conn.setAutoCommit(false); // 关闭自动提交手动控制事务 String sql INSERT INTO t_score(student_id, course_id, score, exam_time) VALUES (?, ?, ?, ?); ps conn.prepareStatement(sql); ps.setInt(1, score.getStudentId()); ps.setInt(2, score.getCourseId()); ps.setDouble(3, score.getScore()); ps.setDate(4, new java.sql.Date(score.getExamTime().getTime())); ps.executeUpdate(); // 更新该学生成绩汇总注意这里要复用同一个 conn updateStudentAvg(conn, score.getStudentId()); conn.commit(); // 全部成功才提交 return true; } catch (Exception e) { if (conn ! null) { conn.rollback(); // 任何一个环节失败回滚 } throw e; } finally { DBUtil.close(ps); DBUtil.close(conn); } }这段代码最有学习价值的地方是事务边界单看 insert 一条成绩根本不需要事务但“先插成绩再更新平均分”如果两个 SQL 不包裹在同一个事务里就可能出现成绩加了、平均分没算的脏数据。conn.setAutoCommit(false)是关键开关之后所有 SQL 都等到commit()才算落库。4.3 成绩统计SQL 完成聚合别在 Java 里写循环加总教务系统里最常见的功能是按课程统计平均分、最高分、最低分。你下载这个包后可以看到原作者大概率是直接在 DAO 层执行一条 SQL 返回列表而不是把所有成绩查出来到 Java 里慢慢加因为后者既慢又显得不专业。统计 SQL 一般是这条SELECT c.course_name, COUNT(s.id) AS total_num, ROUND(AVG(s.score), 2) AS avg_score, MAX(s.score) AS max_score, MIN(s.score) AS min_score FROM t_course c LEFT JOIN t_score s ON c.id s.course_id GROUP BY c.id, c.course_name HAVING COUNT(s.id) 0 ORDER BY avg_score DESC;这里面ROUND(AVG(s.score), 2)让平均分保留两位小数HAVING COUNT(s.id) 0过滤掉没有学生选课的课程GROUP BY里包含了主键 c.id 和名称避免同名课程被错误合并。LEFT JOIN比内连接更能保住那些没人选修的课程记录再配合 HAVING 做剔除这种细节你在“java 八股文”里很难背到但写出来就知道是真调过 API 的。5. 避坑指南把 RAR 项目跑起来的路上最常见的五个坑我拆过不少同类型的 Java Web 管理系统几乎每个包里的坑都出在同样几个地方。下面按“现象 → 原因 → 解决”的方式记录挨个对照就能少折腾。问题 1Tomcat 能启动但浏览器访问项目报 404现象Tomcat 日志显示 started控制台没报错但打开 http://localhost:8080/项目名/ 一直 404。原因项目没有被打进 webapps 目录或者 IDEA 里部署的 Application context 和访问路径不一致。很多 RAR 包里的项目名和 web.xml 里的 context-path 对不上。解决先确认 Tomcat 的 webapps 下有没有生成项目目录。如果用 IDEA 部署打开 Run/Debug Configurations找到 Deployment 标签页看 Application context 是 / 还是 /JavaStudentScore_war_exploded访问地址必须和它完全一致。血泪经验直接把 context 改成 / 最省事然后访问 http://localhost:8080/login.jsp。问题 2MySQL 8 报 Public Key Retrieval is not allowed现象项目启动后登录页面抛异常Tomcat 日志里有 “Public Key Retrieval is not allowed”。原因MySQL 8.0 默认使用 caching_sha2_password 认证客户端在未 SSL 连接下获取公钥被限制。老项目里 mysql-connector-java 5.x 驱动不支持这个协议。解决换 MySQL 官方 8.x 驱动并在 jdbc.url 后面加上allowPublicKeyRetrievaltrueuseSSLfalse。完整 URL 片段是jdbc.urljdbc:mysql://localhost:3306/student_score?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue问题 3JSP 页面中文全乱码现象页面标题、公共导航中文显示成 “???”或者浏览器里一片菱形问号。原因三个地方编码不一致JSP 页面本身是 ISO-8859-1、数据库字符集是 utf8、连接 URL 没有加 characterEncoding。解决统一 UTF-8。每页 JSP 顶部保持% page contentTypetext/html; charsetUTF-8 %数据库连接 URL 加characterEncodingutf8数据库表 COLLATE 改成 utf8_general_ci最好把 filter 里加一个request.setCharacterEncoding(UTF-8)。这三处全对齐中文乱码基本绝迹。问题 4编译报错找不到 javax.servlet 包现象IDEA 打开源码后所有 import javax.servlet 的类全部爆红编译失败。原因源码包里的 lib 目录只有 mysql 驱动没有 servlet-api.jar而 Tomcat 的 lib 目录在编译时没有被 IDEA 自动识别到。解决在 IDEA 里找到 Tomcat 安装目录下的 lib/servlet-api.jar 和 jsp-api.jar添加到 Project Structure 的 Modules - Dependencies 里。或者如果 lib 目录里已经有这些 jar检查是否被 IDEA 标记为库。问题 5成绩为空时统计显示 NULL页面报空指针现象某门课没有学生选统计接口返回的 avg_score 是 nullJava 代码里直接把它转成 double 结果抛 NullPointerException。原因SQL 里 AVG 函数在没有行时返回 NULL没有用 IFNULL 或 COALESCE 做默认值处理。解决把聚合 SQL 改成IFNULL(ROUND(AVG(s.score), 2), 0)让返回结果永远是数字Java 侧再统一处理成字符串或 BigDecimal 就能避免空指针。这个坑建议你在答辩时主动说出来老师的印象分会好很多——因为这说明你踩过真实数据边界。6. 进阶玩法把课程设计工程化再加点简历上能写的功能跑通只是第一步更值得做的是把这个包改造出亮点。作为有几年经验的人我拿到这种传统项目不会重写而是在它原来的骨架上做三处低成本改造让学生成绩管理系统看起来像个“能上线”的工程。6.1 引入连接池和 MyBatis替换手写 JDBC原项目里 DBUtil 每个 DAO 都 getConnection性能差还难维护。你可以在 pom 里引入 Druid 和 MyBatis如果项目已经从 lib 转成 Maven或者先接 Druid 连接池试试水。Druid 的初始化代码// DBUtil.java 改造片段 static { Properties props new Properties(); try (InputStream in DBUtil.class.getClassLoader().getResourceAsStream(druid.properties)) { props.load(in); dataSource DruidDataSourceFactory.createDataSource(props); } catch (Exception e) { throw new RuntimeException(初始化连接池失败, e); } } public static Connection getConnection() throws SQLException { return dataSource.getConnection(); }对应的 druid.properties 里的关键参数driverClassNamecom.mysql.cj.jdbc.Driver urljdbc:mysql://localhost:3306/student_score?serverTimezoneAsia/Shanghai usernameroot password123456 initialSize5 maxActive20这些参数含义是初始连接 5 个最大活跃连接 20 个。改动量很小但项目从“每次请求新建连接”变成“复用连接”性能提升立竿见影。如果你想更贴近现在招聘市场的技术栈可以继续把 DAO 改成 MyBatis Mapper一张表一个注解就能跑面试时直接说我做过“手写 JDBC 到 MyBatis 的平滑迁移”。6.2 给成绩列表加分页避免一条 SQL 拖垮数据库原项目列表页大概率是SELECT * FROM t_score直接查出全部。真实课程数据几百条不算多但你要是把这段写进简历面试官一定会问“数据量大怎么办”。所以加分页是个很有性价比的改造SQL 改成-- 每页 10 条page 从 1 开始计数 SELECT s.id, st.student_name, c.course_name, s.score FROM t_score s JOIN t_student st ON s.student_id st.id JOIN t_course c ON s.course_id c.id ORDER BY s.id DESC LIMIT 10 OFFSET 0;Java 侧只需要接收参数 page然后计算offset (page - 1) * pageSize。再配合一个SELECT COUNT(*)查询获取总条数就能拼出分页按钮。这里注意一个坑MySQL 的LIMIT第一个参数是偏移量新手经常把 page 塞进去导致第一页和第二页数据重复。6.3 用 JUnit 验证统计逻辑彻底告别“我本地跑没问题”系统改完一定要有自动化验证尤其是成绩统计这种有聚合逻辑的功能。我一般会在测试包里写一个简单的 JUnit 测试往库里插入一条已知数据再断言结果// ScoreStatTest.java Test public void testAvgScore() { // 准备插入两个学生同一课程的成绩 80 和 90 insertScore(1, 1, 80); insertScore(2, 1, 90); // 执行调用统计 DAO MapString, Object stat scoreDao.getCourseStatById(1); // 断言平均分 85最大值 90 Assert.assertEquals(85.0, ((Number) stat.get(avg_score)).doubleValue()); Assert.assertEquals(90, ((Number) stat.get(max_score)).intValue()); // 清理删除测试数据避免影响其他用例 deleteAllTestData(); }这段测试的逻辑是“造已知数据 → 跑统计 SQL → 对比期望值 → 清理数据”。它不验证框架整合只验证 SQL 和 Java 转换逻辑。最直接的好处是你改完统计 SQL 后不用重启 Tomcat 再点页面跑一次测试就知道有没有把平均值算错。从那以后我每次拿到这类老系统第一件事不是看代码而是先建库导数据、跑通登录、再补一个最小测试用例确认核心链路是健康的才开始改业务。这样做的好处是任何一步翻车你都能判断是自己环境问题还是源码问题而不是在“黑匣子”里瞎猜。希望这篇笔记能帮你在下载这个 Java 学生成绩管理系统后少走一点弯路把时间花在真正该学的数据库设计和 Java Web 链路上。本文还有配套的精品资源点击获取