ARTICLE DETAIL

资讯详情

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

Java Web学生成绩管理系统:从源码部署到二次开发实战解析

Java Web学生成绩管理系统:从源码部署到二次开发实战解析 简介这是一份面向有一定Java基础、正在学习JavaWeb开发的学生的实战项目资料包以典型的学生成绩管理业务为主线完整演示从登录鉴权、课程管理到学生与成绩维护的常见功能闭环。项目采用MVC设计模式与动态代理模式组织代码开发环境基于Java和MySQL适合用于课堂实训、课程设计或毕业设计前期的技术巩固。压缩包共271个文件约23.41MB以jar、java、class、jsp、xml等类型为主可清晰看到分层架构中的控制层、业务层、数据访问层及前端页面实现同时包含sql数据库脚本和doc文档便于直接导入数据库并对照阅读设计与部署思路。目前已有6977人学习下载对需要快速获得一套可运行、可改造的JavaWeb入门项目而言这是性价比较高的参考素材。1. 一份学生成绩管理系统源码的 .rar拿来之后先做什么很多人第一次接触这类压缩包时以为里面是一个可以双击运行的完整成品结果解压后只看到一堆 .java、.jsp、.sql 和 design.doc。学生成绩管理系统是 Java Web 课程设计里的常青树它的价值不在架构有多强而在于它把“页面 → Servlet → Service → DAO → MySQL”这条最基础的请求链路完整走了一遍。拿到手先别急着吐槽代码旧而是把三样东西拆开看数据库脚本定义了业务边界源码结构决定了你改哪里文档则告诉你设计者当时的思路。这篇内容适合两类人一类是刚学完 Java 基础想找一个能改、能跑、能应付答辩的练手项目另一类是工作中偶尔接手老系统需要快速从 .rar 里理出表结构、登录逻辑和部署步骤。我下面按一套可复现的流程来拆这个压缩包。2. 先读数据库脚本学生成绩系统最少需要几张表拿到 .sql 文件不要急着在 Navicat 里双击执行。数据库脚本是这套系统的地基先读它。一个规范的学生成绩管理系统核心不过学生、课程、成绩、用户四张表但字段设计和约束写法能看出设计者的水平也会直接影响你后面做统计查询。2.1 打开 .sql 文件后的第一件事先确认建库语句和字符集脚本开头通常会有一段CREATE DATABASE这里隐藏着第一个坑。不同版本的 MySQL 对字符集的处理不一样MySQL 5.7 默认是utf8MySQL 8.0 开始推荐utf8mb4。如果脚本里写的是DEFAULT CHARSETutf8而你本地是 MySQL 8.0成绩表里存了表情符号就会报Incorrect string value。先找到建库语句确认以下信息数据库名、字符集、排序规则。CREATE DATABASE IF NOT EXISTS student_score DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE student_score;这条语句指定了数据库名student_score字符集用utf8mb4排序规则用utf8mb4_general_ci。IF NOT EXISTS的作用是防止重复执行脚本时报错。后面所有建表语句都基于这个库所以脚本执行顺序必须是建库、选库、建表、插数据。2.2 student、course、score、user 四张核心表与典型字段设计课程设计级别的成绩管理系统四张表就够。student存学生基本信息course存课程score存成绩user存登录账号。下面这份是最简但完整的建表脚本注意看外键和联合唯一索引的写法。CREATE TABLE student ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT 学生ID, student_no VARCHAR(20) NOT NULL COMMENT 学号, name VARCHAR(50) NOT NULL COMMENT 姓名, class_name VARCHAR(50) COMMENT 班级, gender CHAR(1) DEFAULT 男 COMMENT 性别, created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, UNIQUE KEY uk_student_no (student_no) ) ENGINEInnoDB COMMENT 学生表; CREATE TABLE course ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT 课程ID, course_no VARCHAR(20) NOT NULL COMMENT 课程编号, course_name VARCHAR(100) NOT NULL COMMENT 课程名称, credit DECIMAL(3,1) DEFAULT 2.0 COMMENT 学分, UNIQUE KEY uk_course_no (course_no) ) ENGINEInnoDB COMMENT 课程表; CREATE TABLE score ( id INT PRIMARY KEY AUTO_INCREMENT, student_id INT NOT NULL, course_id INT NOT NULL, score DECIMAL(5,2) COMMENT 成绩, exam_date DATE COMMENT 考试日期, UNIQUE KEY uk_stu_course (student_id, course_id), CONSTRAINT fk_score_student FOREIGN KEY (student_id) REFERENCES student(id), CONSTRAINT fk_score_course FOREIGN KEY (course_id) REFERENCES course(id) ) ENGINEInnoDB COMMENT 成绩表;这段脚本里有几个值得注意的点。UNIQUE KEY uk_stu_course (student_id, course_id)是联合唯一索引它的作用是同一门课同一个学生只能有一条成绩记录从数据库层面防止重复录入。外键fk_score_student和fk_score_course保证成绩表里不能出现不存在的学生或课程。score DECIMAL(5,2)而不是FLOAT是为了避免浮点数精度问题导致 89.5 变成 89.4999。2.3 角色权限、初始账号与预置数据脚本的常见写法四张表里user表比较特殊它的设计决定登录逻辑怎么写。最常见的套路是加一个role字段0 表示管理员1 表示教师2 表示学生登录后根据角色跳转到不同页面。数据库脚本里通常会带一条初始管理员账号方便第一次登录。CREATE TABLE user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE COMMENT 登录名, password VARCHAR(100) NOT NULL COMMENT 密码, role TINYINT DEFAULT 2 COMMENT 0管理员 1教师 2学生, student_id INT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB COMMENT 用户表; INSERT INTO user (username, password, role) VALUES (admin, admin123, 0);初始密码用明文是课程设计项目的常态但这个点在答辩时经常被老师追问。如果你打算把项目再做得体面一点可以改成MD5或SHA-256加盐存储但要注意登录判断那一段 Java 代码也要同步改。user表里预留了student_id字段把登录账号和学生记录挂上钩这样学生登录后只能查自己的成绩这个设计比单独写一套权限判断要清爽很多。3. 把源码结构拆开Servlet 映射、JDBC 调用链和三处乱码源头看完数据库脚本再打开源码心里应该有了一张表关系图。Java Web 老项目的源码结构通常是 JSP Servlet DAO里面没有 Spring 的Autowired也没有 MyBatis 的Mapper.xml但只要能分清每个文件的职责你就能定位到“改成绩查询”要动哪个文件。3.1 先看 WEB-INFweb.xml 和 lib 目录决定技术坐标解压后如果你的目录里有WEB-INF/web.xml说明这是一个传统的 Servlet 项目需要打成 war 包丢给 Tomcat。如果根目录有pom.xml则说明这是 Maven 工程。最怕的是两种都有但互相矛盾这种情况一般是文档没同步更新以pom.xml为准。web.xml是这个项目的总入口主要看三处display-name项目名、welcome-file-list默认首页、servlet-mapping的 URL 映射。下面是一段典型的web.xml片段。servlet servlet-nameLoginServlet/servlet-name servlet-classcom.student.controller.LoginServlet/servlet-class /servlet servlet-mapping servlet-nameLoginServlet/servlet-name url-pattern/login/url-pattern /servlet-mappingservlet-class写的是类的完整限定名url-pattern定义浏览器访问路径。你访问http://localhost:8080/项目名/login时Tomcat 会找到LoginServlet里的doPost方法。从这里可以顺着com.student.controller包名找到所有请求入口比在 JSP 里满天飞找action要快得多。3.2 一个 Servlet 映射读清请求入口学生查询成绩的真实路径以学生查询成绩为例一般点是成绩查询菜单跳到一个 JSP 页面页面提交表单后请求转发到QueryScoreServlet。我见过大量新手项目把这段代码直接写在 JSP 里用% %包裹看起来能跑但完全没法维护。WebServlet(/queryScore) public class QueryScoreServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding(UTF-8); String studentId request.getParameter(studentId); ScoreService service new ScoreService(); ListScore list service.findByStudentId(Integer.parseInt(studentId)); request.setAttribute(scoreList, list); request.getRequestDispatcher(/score_list.jsp).forward(request, response); } }WebServlet(/queryScore)是注解方式配置映射比web.xml里写servlet-mapping少一步但两者不能对同一个类重复配置否则 Tomcat 启动时直接报Conflicting WebServlet异常。request.setCharacterEncoding(UTF-8)必须放在读取任何参数之前。这段代码把参数接收、业务调用、页面跳转三件事串起来是典型的“瘦 Servlet 胖 Service”写法。3.3 查询链路拆解从 JSP 表单到 JDBC 的 PreparedStatement 参数很多人追代码时被三层结构绕晕其实跟踪一个请求就全通了。JSP 页面里写form action${pageContext.request.contextPath}/queryScore methodpost表单里的studentId提交给QueryScoreServletServlet 调ScoreServiceService 再调ScoreDAODAO 里执行的就是下面这条 SQL。public ListScore findByStudentId(int studentId) { String sql SELECT c.course_name, s.score, s.exam_date FROM score s JOIN course c ON s.course_id c.id WHERE s.student_id ?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setInt(1, studentId); ResultSet rs ps.executeQuery(); ListScore list new ArrayList(); while (rs.next()) { Score sc new Score(); sc.setCourseName(rs.getString(course_name)); sc.setScore(rs.getBigDecimal(score)); sc.setExamDate(rs.getDate(exam_date)); list.add(sc); } return list; } catch (SQLException e) { throw new RuntimeException(查询成绩失败, e); } }写法上用了PreparedStatement的?占位符统一用ps.setInt(1, studentId)传参避免直接拼接字符串带来的 SQL 注入隐患。这种调用链在 Java 面试里会被当作最基础的八股文反复问答清楚三层各自职责比背十道面试题管用。JOIN关联查询把course_name直接带出来程序里就不用再查一次课程表。3.4 中文乱码的“三处位置”编码问题都出在这老项目最容易翻车的不是功能而是乱码。如果你的学生姓名在页面上显示成问号逐一定位这三个位置顺序不要乱。第一处是数据库连接字符串必须带useUnicodetruecharacterEncodingutf8第二处是 Servlet 里接收请求参数前调用request.setCharacterEncoding(UTF-8)第三处是 JSP 页面顶部的pageEncodingUTF-8。很多项目把前两处改了第三处忘了改结果页面标题正常从数据库读出来的中文全部乱掉。排错时有个规律如果页面标题和数据都是乱码问题几乎都在 JSP 的pageEncoding或contentType如果只有从数据库查出来的数据乱码问题在 JDBC URL 或表结构字符集如果表单提交后数据库里存的就是乱码那问题在request.setCharacterEncoding的执行顺序。用这个思路排查比反复重启 Tomcat 高效得多。4. 把数据库脚本导进 MySQL在 IDEATomcat 里跑起来源码读完接下来就是最考验耐心的环节把项目跑起来。下面这套流程我在不同机器上重复过很多次按顺序执行基本不会有偏差。这里面每一步都对应一个典型的失败场景我会把参数列出来。4.1 装环境前的版本匹配JDK 8、Tomcat 9、MySQL 5.7/8.0 怎么搭配拿到老项目先看 Java 文件里的语法如果全是List、ArrayList、for循环没有var和 lambda那大概率是 JDK 8 时代写的。不要一上来装 JDK 21老项目用新 JDK 编译容易遇到java.base模块问题。推荐组合是 JDK 8 Tomcat 9 MySQL 5.7 或 8.0。版本项推荐选择说明JDK1.88u202 或更后老项目编译兼容性最好Tomcat9.0.x支持 Servlet 4.0兼容老代码MySQL5.7 或 8.08.0 注意驱动版本项目结构war 或 explodedIDEA 里两种方式都行MySQL 8.0 要配套mysql-connector-java8.0.x 驱动装在 Tomcat 的lib或项目的WEB-INF/lib下。版本不匹配时启动项目可以但一执行 SQL 就报ClassNotFoundException: com.mysql.jdbc.Driver换成com.mysql.cj.jdbc.Driver才行。4.2 用 mysql 命令行执行数据库脚本别依赖 GUI 导入执行脚本这件事GUI 工具有时候反而帮倒忙。Navicat 导入大文件时如果中途报错不会明确告诉你哪行出了问题。用命令行最可靠直接看到每条 SQL 的执行结果。先确认 MySQL 已经启动然后把.rar里的.sql文件解压到磁盘上进入命令行执行。mysql -u root -p D:/sql/student_score.sql如果.sql文件里已经有CREATE DATABASE和USE语句上面这条命令就够了。执行成功后进入 MySQL 检查表是否建全。mysql -u root -p SHOW DATABASES; USE student_score; SHOW TABLES; SELECT COUNT(*) FROM student;SHOW TABLES应该看到至少四张表如果只有三张说明脚本执行到中间中断了检查中断位置附近的建表语句多半是字段类型不兼容或者外键引用顺序不对。还有一种常见情况source命令在 Windows 下路径分隔符要写成正斜杠source D:/sql/student_score.sql;在mysql提示符下同样可以执行。4.3 改 JDBC 连接参数必须确认的 4 个值脚本导成功下一步就是改 Java 代码里的数据库连接配置。老项目一般有一个db.properties或者DBUtil.java内容类似下面这样。jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/student_score?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai jdbc.usernameroot jdbc.password123456jdbc.url里有几个参数必须确认。localhost:3306是数据库地址和端口如果 MySQL 没跑在默认端口要改。student_score是库名必须和 2.1 节建库时一致。serverTimezoneAsia/Shanghai是给 MySQL 8.0 用的老项目里通常没有这个参数不加会报时间区异常。password改成你自己 MySQL 的 root 密码硬编码在这里虽然不优雅但课程设计项目都这样跑通了再考虑用环境变量覆盖。4.4 在 IDEA 里配置 Tomcat部署 exploded artifact环境变量配好、连接参数改好后在 IDEA 里打开项目。如果项目没有pom.xmlIDEA 会识别成普通 Java Web 项目需要手动导入WEB-INF/lib下的 jar 包。这一步要把所有mysql-connector、jstl、servlet-api的 jar 加入 Library漏掉任何servlet-api的 jarIDE 里能编译但启动时 Tomcat 会提示类冲突。部署方式选 exploded 比 war 包方便。war 包相当于打包后整体发布每次改 JSP 都要重新打包exploded 相当于直接把webapp目录映射给 Tomcat改完 JSP 刷新浏览器就生效。配置时在 IDEA 的Run/Debug Configurations里选 Tomcat Server LocalDeployment标签下加Artifact...选带 exploded 后缀的那一项。Application context默认写/项目名_war_exploded这里建议改成/stu访问 URL 短一点。4.5 启动后先验证登录和成绩查询两条链路Tomcat 启动成功不代表业务就通了你还需要做两次验证。第一次验证登录第二次验证数据能否正确展示。不要只打开首页看个样式就宣告成功。登录接口如果设计成表单提交用下面的 curl 命令可以验证服务端是否正常响应。curl -d usernameadminpasswordadmin123 \ http://localhost:8080/stu/login返回 302 跳转说明登录成功返回 200 且页面里有报错信息就看控制台里有没有SQLException或NullPointerException。SQL 异常大部分和 4.3 节的连接参数有关空指针多半是request.getParameter的 Key 和前端的name属性没对上。验证成绩查询时一定要选一个数据库里真实存在的学生 ID 来试否则列表为空你无法判断是代码错了还是数据没插进去。查不出问题时去 Tomcat 的logs/catalina.out里看完整堆栈别只盯着 IDEA 控制台的最后两行。5. 学生成绩管理系统的二次开发加筛选条件、导出 Excel 报表把项目跑通只能说明你有环境配置能力。答辩或实际使用中一定会遇到“能不能再加一个功能”的需求最常被提的两个是组合条件筛选和成绩表导出。这两个需求改动范围可控但能体现思路。5.1 给成绩查询页面加“按班级筛选”JSP 下拉框与安全的 SQL 拼接原始项目里成绩查询一般只能按学号或姓名查现在要支持班级。修改思路JSP 里加一个下拉框查出班级列表提交后 Servlet 把班级参数传给 ServiceService 再传到 DAO。SQL 部分不能直接拼WHERE class_name加参数要改成PreparedStatement动态拼接。String sql SELECT s.student_no, s.name, s.class_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 11; if (className ! null !className.isEmpty()) { sql AND s.class_name ?; }WHERE 11是动态 SQL 的常用写法它的作用不是没有条件而是让后面的AND s.class_name ?不用区分是不是第一个条件减少 if-else 拼接。className为空时查询所有班级有值时加上过滤条件注意PreparedStatement的参数下标从 1 开始如果前面还有别的条件下标要依次递增传参顺序不能乱。5.2 用 Apache POI 导出 Excel依赖、模板与数字格式的坑成绩表要导出最常见的做法是用 POI 写一个.xls。导出功能如果做在 Servlet 里注意两个细节第一响应头要设置Content-Disposition否则浏览器只会打开一个乱码页面第二成绩列必须设成数字格式否则 89.50 会被显示成文本Excel 里无法求和。HSSFWorkbook workbook new HSSFWorkbook(); HSSFSheet sheet workbook.createSheet(期末成绩); HSSFRow header sheet.createRow(0); header.createCell(0).setCellValue(学号); header.createCell(1).setCellValue(姓名); header.createCell(2).setCellValue(课程); header.createCell(3).setCellValue(成绩); response.setContentType(application/vnd.ms-excel); response.setHeader(Content-Disposition, attachment;filenamescores.xls); workbook.write(response.getOutputStream());response.getOutputStream()不能和PrintWriter混用Servlet 里一处写了response.getWriter()再写这段就会报IllegalStateException。.xls是 Excel 2003 格式旧版本 POI 不支持.xlsx如果导出后 Excel 提示文件格式与扩展名不匹配直接改成.xls后缀最省事。数字格式建议在生成 cell 时调用setCellValue(Double.parseDouble(score))让 Excel 自动识别为数值。5.3 改造后的验证清单回归测试要覆盖哪些入口改完功能别急着关 IDE先跑一遍验证清单。重点看三块原有查询是否被新增条件影响导出的文件能否正常打开班级参数为空时是否返回全量数据。验证场景操作预期结果全量查询不选班级直接查询返回全部成绩记录条件筛选选择计算机2301班只显示该班成绩空结果处理选一个没有学生的班级页面提示“无数据”导出文件点击导出按钮下载 xls 文件且数字可计算乱码回归导出后打开 Excel中文姓名正常显示导出文件出现中文乱码时检查response.setCharacterEncoding(UTF-8)是否在write方法之前调用导出文件打不开时检查workbook.close()是否在write之后关闭流。到这里这套学生成绩管理系统就具备了继续迭代的基础。本文还有配套的精品资源点击获取
返回列表