JSP+Servlet体育成绩管理系统教学实践指南
简介本资源是一套基于JSPJavaMySQL开发的体育成绩管理系统完整工程面向高校计算机专业学生、Web开发初学者及体育信息化管理需求者解决学校或赛事组织中班级管理、项目报名、成绩统计与秩序册生成等核心业务的数字化落地问题。压缩包共98个文件含16个JSP页面实现用户交互与数据展示、4个Java源文件封装核心业务逻辑、15个JAR包支撑数据库连接与Web容器运行、34个GIF动图辅助界面提示及1个SQL脚本含tybs数据库建表与初始化数据整体大小为4.96MB。已有469人学习下载资源附带可直接运行的.exe演示程序、完整MyEclipse项目结构含.project、.classpath等配置文件及db_tybs.sql数据库脚本开箱即用目录层次清晰模块划分明确便于理解MVC分层设计与JDBC实战集成。1. JSP JAVA体育成绩管理系统为什么还在用这个组合做教学级项目它真过时了吗某高校计算机系大三课程设计里「JSP JAVA体育成绩管理系统」连续五年出现在选题清单前三——不是因为技术多先进而是它像一把钝刀够用、易上手、能串起Servlet生命周期、数据库连接、表单交互、分页查询等一整套Web开发基础链路。它不解决高并发抢课、实时成绩推送或AI体测分析但能让你在两周内跑通「学生登录→查体测成绩→教师录入50米跑/立定跳远/引体向上→按班级导出Excel」的完整闭环。如果你正被课程设计卡在「不知道从哪写起」或是带实训的导师想找一个不依赖Spring Boot自动配置、所有代码都暴露在眼皮底下的练手项目这个标题代表的不是过时而是一种刻意保留的「可调试性」每个JSP页面的Java脚本片段、每个DAO类的SQL拼接、每次request.setAttribute()的传递路径全在源码里裸奔。它适合新手建立Web请求-响应的肌肉记忆也适合熟手快速验证一个数据库字段变更对前后端的影响范围。别被「JSP」二字劝退——它在这里不是生产方案而是一张可涂改的架构草稿纸。2. 搭建最小可运行环境从零配Tomcat到跑通第一个成绩录入页面2.1 选型理由为什么坚持用JSPServletJDBC而不是直接上Spring Boot这不是怀旧是教学场景下的精准克制。Spring Boot的自动配置会隐藏DispatcherServlet如何拦截请求、Controller方法返回值如何被ViewResolver解析成JSP路径、数据库连接池怎么初始化——这些恰恰是初学者最该亲手拧紧的螺丝。而JSPServlet组合强制你直面三个核心问题请求怎么进来web.xml里servlet-mapping绑定URL和Servlet类比RequestMapping更直观数据怎么存取手动写Class.forName(com.mysql.cj.jdbc.Driver)和Connection.createStatement()让你记住JDBC四步曲加载驱动→获取连接→创建语句→执行查询页面怎么渲染JSP里的% request.getAttribute(scores) %直接暴露了request域对象的生命周期比Thymeleaf的th:each更原始、更易Debug。提示本方案默认使用MySQL 8.0JDK 1.8Apache Tomcat 9.0。这三个版本组合经过大量教学项目验证兼容性陷阱最少。2.2 五步搭建本地运行环境含关键配置文件步骤1创建标准Web项目结构在IDEA中新建Dynamic Web Project目录结构必须包含/WebContent /WEB-INF web.xml ← 核心配置入口 /lib ← 放入mysql-connector-java-8.0.33.jar /jsp ← 所有.jsp文件放这里如login.jsp, score_list.jsp /src /dao ← 数据访问层ScoreDAO.java /servlet ← 控制器ScoreAddServlet.java /entity ← 实体类Student.java, Score.java步骤2配置web.xmlServlet注册与欢迎页?xml version1.0 encodingUTF-8? web-app xmlnshttp://xmlns.jcp.org/xml/ns/javaee xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd version4.0 !-- 配置欢迎页 -- welcome-file-list welcome-filejsp/login.jsp/welcome-file /welcome-file-list !-- 注册成绩录入Servlet -- servlet servlet-nameScoreAddServlet/servlet-name servlet-classservlet.ScoreAddServlet/servlet-class /servlet servlet-mapping servlet-nameScoreAddServlet/servlet-name url-pattern/addScore/url-pattern /servlet-mapping !-- 注册成绩查询Servlet -- servlet servlet-nameScoreListServlet/servlet-name servlet-classservlet.ScoreListServlet/servlet-class /servlet servlet-mapping servlet-nameScoreListServlet/servlet-name url-pattern/listScores/url-pattern /servlet-mapping /web-app参数说明url-pattern/addScore/url-pattern定义了浏览器访问http://localhost:8080/your_project/addScore时触发ScoreAddServlet。注意路径前缀/your_project是Tomcat部署时的Context Path不是代码里写的。步骤3编写数据库建表SQL体育成绩核心表-- 创建学生表简化版仅含必要字段 CREATE TABLE student ( id INT PRIMARY KEY AUTO_INCREMENT, stu_id VARCHAR(12) NOT NULL UNIQUE, -- 学号如20230001 name VARCHAR(20) NOT NULL, class_name VARCHAR(30) NOT NULL -- 班级名称如计算机2301班 ); -- 创建体育成绩表 CREATE TABLE score ( id INT PRIMARY KEY AUTO_INCREMENT, stu_id VARCHAR(12) NOT NULL, test_item VARCHAR(20) NOT NULL, -- 测试项目50米跑,立定跳远,引体向上 score_value DECIMAL(5,2) NOT NULL, -- 成绩值50米跑存秒数引体向上存个数 test_date DATE NOT NULL, FOREIGN KEY (stu_id) REFERENCES student(stu_id) );逻辑说明采用stu_id而非student.id作为外键避免学生表主键变更导致成绩表失效test_item用VARCHAR而非ENUM方便后期新增项目如“1000米跑”无需改表结构。步骤4实现ScoreDAO手写JDBC增删改查// src/dao/ScoreDAO.java public class ScoreDAO { private static final String URL jdbc:mysql://localhost:3306/sports_db?useSSLfalseserverTimezoneUTC; private static final String USER root; private static final String PASSWORD 123456; // 插入一条成绩记录 public boolean addScore(Score score) { String sql INSERT INTO score(stu_id, test_item, score_value, test_date) VALUES (?, ?, ?, ?); try (Connection conn DriverManager.getConnection(URL, USER, PASSWORD); PreparedStatement pstmt conn.prepareStatement(sql)) { pstmt.setString(1, score.getStuId()); pstmt.setString(2, score.getTestItem()); pstmt.setDouble(3, score.getScoreValue()); pstmt.setDate(4, new java.sql.Date(score.getTestDate().getTime())); return pstmt.executeUpdate() 0; // 返回true表示插入成功 } catch (SQLException e) { e.printStackTrace(); return false; } } // 查询某学生所有成绩按班级分组 public ListScore getScoresByClass(String className) { String sql SELECT s.*, st.name, st.class_name FROM score s JOIN student st ON s.stu_id st.stu_id WHERE st.class_name ? ORDER BY s.test_date DESC; ListScore scores new ArrayList(); try (Connection conn DriverManager.getConnection(URL, USER, PASSWORD); PreparedStatement pstmt conn.prepareStatement(sql)) { pstmt.setString(1, className); ResultSet rs pstmt.executeQuery(); while (rs.next()) { Score score new Score(); score.setId(rs.getInt(s.id)); score.setStuId(rs.getString(s.stu_id)); score.setTestItem(rs.getString(s.test_item)); score.setScoreValue(rs.getDouble(s.score_value)); score.setTestDate(rs.getDate(s.test_date)); // 关联学生信息 score.setStudentName(rs.getString(st.name)); score.setClassName(rs.getString(st.class_name)); scores.add(score); } } catch (SQLException e) { e.printStackTrace(); } return scores; } }参数说明serverTimezoneUTC解决MySQL 8.0时区报错useSSLfalse在本地开发环境关闭SSL生产环境必须开启PreparedStatement防止SQL注入比字符串拼接安全。步骤5编写ScoreAddServlet接收表单并调用DAO// src/servlet/ScoreAddServlet.java WebServlet(/addScore) public class ScoreAddServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 1. 获取表单参数来自score_add.jsp的POST提交 String stuId request.getParameter(stu_id); String testItem request.getParameter(test_item); String scoreValueStr request.getParameter(score_value); String testDate request.getParameter(test_date); // 2. 参数校验教学项目必须写 if (stuId null || stuId.trim().isEmpty() || testItem null || testItem.trim().isEmpty() || scoreValueStr null || scoreValueStr.trim().isEmpty() || testDate null || testDate.trim().isEmpty()) { request.setAttribute(error, 所有字段均为必填项); request.getRequestDispatcher(/jsp/score_add.jsp).forward(request, response); return; } // 3. 类型转换与封装 double scoreValue; try { scoreValue Double.parseDouble(scoreValueStr); } catch (NumberFormatException e) { request.setAttribute(error, 成绩必须为数字); request.getRequestDispatcher(/jsp/score_add.jsp).forward(request, response); return; } Score score new Score(); score.setStuId(stuId); score.setTestItem(testItem); score.setScoreValue(scoreValue); score.setTestDate(java.sql.Date.valueOf(testDate)); // 字符串转SQL日期 // 4. 调用DAO保存 ScoreDAO dao new ScoreDAO(); boolean success dao.addScore(score); // 5. 跳转结果页 if (success) { request.setAttribute(message, 成绩录入成功); request.getRequestDispatcher(/jsp/score_add.jsp).forward(request, response); } else { request.setAttribute(error, 录入失败请检查数据库连接); request.getRequestDispatcher(/jsp/score_add.jsp).forward(request, response); } } }逻辑说明WebServlet(/addScore)是注解式配置与web.xml中的servlet-mapping功能等价二者选其一即可request.getRequestDispatcher(...).forward()是服务器端跳转URL不变适合表单提交后刷新页面response.sendRedirect()是客户端重定向URL会变适合成功后跳转到列表页。3. 核心页面开发JSP如何把Java对象变成可交互的HTML表格3.1 login.jsp最简登录表单与Session认证逻辑%-- /WebContent/jsp/login.jsp --% % page contentTypetext/html;charsetUTF-8 languagejava % html head title体育成绩管理系统 - 登录/title style .login-box { width: 400px; margin: 100px auto; padding: 20px; border: 1px solid #ccc; } input[typetext], input[typepassword] { width: 100%; padding: 8px; margin: 5px 0; } .error { color: red; font-size: 14px; } /style /head body div classlogin-box h2体育成绩管理系统/h2 form actionlogin methodpost label用户名input typetext nameusername required/labelbr label密码input typepassword namepassword required/labelbr input typesubmit value登录 /form %-- 显示错误信息 --% % if (request.getAttribute(error) ! null) { % p classerror% request.getAttribute(error) %/p % } % /div /body /html关键点表单actionlogin会提交到/login路径需在web.xml中配置对应的LoginServlet此处省略原理同ScoreAddServletrequired属性实现前端必填校验但后端仍需二次校验防绕过。3.2 score_list.jsp用JSTL遍历成绩列表并支持班级筛选%-- /WebContent/jsp/score_list.jsp --% % page contentTypetext/html;charsetUTF-8 languagejava % % taglib prefixc urihttp://java.sun.com/jsp/jstl/core % html head title成绩列表/title style table { width: 100%; border-collapse: collapse; } th, td { border: 1px solid #ddd; padding: 8px; text-align: left; } th { background-color: #f2f2f2; } .filter-form { margin-bottom: 20px; } /style /head body h1体育成绩列表/h1 !-- 班级筛选表单 -- div classfilter-form form actionlistScores methodget label选择班级 select nameclass_name option value全部班级/option c:forEach items${classList} varcls option value${cls} ${param.class_name cls ? selected : }${cls}/option /c:forEach /select /label input typesubmit value筛选 /form /div !-- 成绩表格 -- table thead tr th学号/th th姓名/th th班级/th th测试项目/th th成绩/th th测试日期/th /tr /thead tbody c:choose c:when test${empty scores} trtd colspan6暂无成绩数据/td/tr /c:when c:otherwise c:forEach items${scores} varscore tr td${score.stuId}/td td${score.studentName}/td td${score.className}/td td${score.testItem}/td td c:choose c:when test${score.testItem 50米跑 || score.testItem 1000米跑} ${score.scoreValue} 秒 /c:when c:otherwise ${score.scoreValue} 个 /c:otherwise /c:choose /td td${score.testDate}/td /tr /c:forEach /c:otherwise /c:choose /tbody /table /body /html参数说明c:forEach遍历request.getAttribute(scores)传入的List${param.class_name}获取URL参数如?class_name计算机2301班c:choose实现条件渲染区分跑步类单位秒和力量类单位个的成绩显示格式。3.3 score_add.jsp带JavaScript实时校验的录入页面%-- /WebContent/jsp/score_add.jsp --% % page contentTypetext/html;charsetUTF-8 languagejava % html head title录入成绩/title script // 页面加载时检查是否有错误信息 window.onload function() { var errorDiv document.getElementById(error); if (errorDiv errorDiv.textContent.trim() ! ) { errorDiv.style.display block; } }; // 实时校验学号格式12位数字 function validateStuId() { var stuId document.getElementById(stu_id).value; var errorSpan document.getElementById(stu_id_error); if (!/^\d{12}$/.test(stuId)) { errorSpan.textContent 学号必须为12位数字; errorSpan.style.color red; return false; } else { errorSpan.textContent ; return true; } } // 表单提交前校验 function validateForm() { return validateStuId(); } /script style .form-group { margin: 10px 0; } label { display: inline-block; width: 120px; } input[typetext], select { padding: 5px; } .error { font-size: 12px; margin-top: 5px; } /style /head body h1录入体育成绩/h1 form actionaddScore methodpost onsubmitreturn validateForm() div classform-group label forstu_id学号/label input typetext idstu_id namestu_id required onblurvalidateStuId() span idstu_id_error classerror/span /div div classform-group label fortest_item测试项目/label select idtest_item nametest_item required option value请选择/option option value50米跑50米跑/option option value立定跳远立定跳远/option option value引体向上引体向上/option option value1000米跑1000米跑/option /select /div div classform-group label forscore_value成绩/label input typetext idscore_value namescore_value required placeholder输入数字 /div div classform-group label fortest_date测试日期/label input typedate idtest_date nametest_date required /div input typesubmit value提交 /form !-- 显示后端返回的错误信息 -- div iderror styledisplay:none; color:red; % request.getAttribute(error) ! null ? request.getAttribute(error) : % /div !-- 显示成功信息 -- c:if test${not empty message} div stylecolor:green;${message}/div /c:if /body /html逻辑说明onblurvalidateStuId()实现失焦校验比onchange更及时onsubmitreturn validateForm()确保提交前所有校验通过c:if处理后端返回的成功提示与前端JS错误提示分离避免重复逻辑。4. 避坑指南五个让新手卡住三天的真实问题与解法4.1 现象启动Tomcat后访问http://localhost:8080/your_project/jsp/login.jsp报404原因JSP文件未放在/WebContent/jsp/目录下或项目未正确部署到Tomcat。常见误操作将JSP放在/src目录Java源码目录或IDEA中未勾选Add as Library导入mysql-connector-java.jar。解决确认项目结构符合2.2节要求在IDEA中右键项目 →Add Framework Support→ 勾选Java EE Web Module检查Project Structure→Artifacts中是否包含WEB-INF和jsp目录重启Tomcat。4.2 现象java.lang.ClassNotFoundException: com.mysql.cj.jdbc.Driver原因MySQL驱动jar包未放入/WebContent/WEB-INF/lib/或驱动类名写错MySQL 8.0必须用com.mysql.cj.jdbc.Driver旧版是com.mysql.jdbc.Driver。解决下载mysql-connector-java-8.0.33.jar非-bin.jar复制到/WEB-INF/lib/刷新IDEA项目右键 →Reload project检查ScoreDAO.java中Class.forName()参数是否为com.mysql.cj.jdbc.Driver。4.3 现象成绩列表页显示null或空白控制台无报错原因ScoreListServlet中未将查询结果存入request.setAttribute(scores, scores)或JSP中c:forEach的items属性写错如${scoreList}而非${scores}。解决在ScoreListServlet的doGet方法末尾添加request.setAttribute(scores, scores)检查JSP中c:forEach items${scores}是否与setAttribute的key一致用System.out.println(scores.size())在Servlet中打印列表大小验证DAO是否查到数据。4.4 现象中文乱码——登录页面输入中文用户名显示为??数据库存入????原因三个环节未统一UTF-8① JSP页面未声明% page contentTypetext/html;charsetUTF-8 %② MySQL连接URL缺少characterEncodingutf8③ Tomcat的conf/server.xml中Connector未设置URIEncodingUTF-8。解决在所有JSP顶部添加% page contentTypetext/html;charsetUTF-8 languagejava %修改ScoreDAO.java中URL为jdbc:mysql://localhost:3306/sports_db?useSSLfalseserverTimezoneUTCcharacterEncodingutf8编辑Tomcat/conf/server.xml找到Connector port8080 ... /添加属性URIEncodingUTF-8。4.5 现象score_add.jsp提交后页面空白Network面板显示302重定向到/addScore原因ScoreAddServlet中使用了response.sendRedirect(/addScore)但/addScore是Servlet路径不是JSP页面路径导致无限重定向循环。解决必须用request.getRequestDispatcher(/jsp/score_add.jsp).forward(request, response)进行服务器端跳转sendRedirect()只能跳转到真实存在的URL如/jsp/success.jsp不能跳转到Servlet映射路径。5. 进阶技巧给系统加导出Excel功能不依赖POI也能跑通5.1 为什么不用Apache POI教学场景的轻量替代方案POI虽强大但引入poi-ooxml-5.2.4.jar等7个依赖新手常因版本冲突如xmlbeans版本不匹配卡在编译阶段。而体育成绩导出本质是结构化数据转CSV——用纯Java写CSV文件50行代码搞定且Excel双击即可打开。CSV格式简单每行一条记录字段用英文逗号分隔含中文时用UTF-8编码BOM头\uFEFF确保Excel识别。这比教POI的XSSFWorkbook、SXSSFWorkbook更聚焦目标让学生理解「导出」的本质是生成特定格式的文本流。5.2 ExportServlet手写CSV生成与响应头设置// src/servlet/ExportServlet.java WebServlet(/exportScores) public class ExportServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 1. 获取班级参数来自score_list.jsp的筛选表单 String className request.getParameter(class_name); if (className null || className.trim().isEmpty()) { className %; // SQL中用LIKE匹配全部 } // 2. 查询数据复用ScoreDAO的getScoresByClass但需修改SQL支持模糊查询 ScoreDAO dao new ScoreDAO(); ListScore scores dao.getScoresByClass(className); // 此处假设DAO已扩展支持模糊查询 // 3. 设置响应头告知浏览器这是附件且用UTF-8编码 response.setContentType(text/csv;charsetUTF-8); response.setHeader(Content-Disposition, attachment; filename\sports_scores_ (className.equals(%) ? all : className) .csv\); // 4. 写入BOM头解决Excel中文乱码 ServletOutputStream out response.getOutputStream(); out.write(new byte[]{(byte)0xEF, (byte)0xBB, (byte)0xBF}); // 5. 写入CSV表头 out.write(学号,姓名,班级,测试项目,成绩,测试日期\n.getBytes(UTF-8)); // 6. 写入每条记录字段间用英文逗号字符串用双引号包裹 for (Score score : scores) { String line String.format(\%s\,\%s\,\%s\,\%s\,\%s\,\%s\\n, score.getStuId(), score.getStudentName(), score.getClassName(), score.getTestItem(), formatScoreDisplay(score), // 格式化成绩显示 score.getTestDate() ); out.write(line.getBytes(UTF-8)); } out.flush(); } // 辅助方法格式化成绩显示同score_list.jsp逻辑 private String formatScoreDisplay(Score score) { if (50米跑.equals(score.getTestItem()) || 1000米跑.equals(score.getTestItem())) { return score.getScoreValue() 秒; } else { return score.getScoreValue() 个; } } }关键参数说明response.setContentType(text/csv;charsetUTF-8)声明内容类型Content-Disposition的attachment强制下载filename指定下载文件名BOM头\uFEFF字节数组{0xEF, 0xBB, 0xBF}是Excel识别UTF-8的唯一可靠方式String.format()中用双引号包裹每个字段防止成绩含逗号如12,5导致CSV解析错位。5.3 在score_list.jsp中添加导出按钮!-- 在score_list.jsp的table上方添加 -- div stylemargin: 15px 0; a hrefexportScores?class_name${param.class_name} stylebackground:#4CAF50; color:white; padding:8px 16px; text-decoration:none; border-radius:4px; 导出当前筛选结果为CSV /a /div逻辑说明hrefexportScores?class_name${param.class_name}将当前筛选的班级参数透传给ExportServlet实现「导出所见即所得」链接样式用内联CSS保证简洁避免引入外部CSS文件增加复杂度。5.4 验证导出功能三步确认是否成功步骤操作预期结果排查点1在score_list.jsp中选择班级如计算机2301班点击「导出」浏览器弹出下载对话框文件名为sports_scores_计算机2301班.csv检查ExportServlet中response.setHeader(Content-Disposition, ...)的filename是否拼接正确2用记事本打开下载的CSV文件首行显示学号,姓名,班级,...中文正常显示若中文为乱码确认BOM头是否写入out.write(new byte[]{...})及response.setContentType是否含charsetUTF-83用Excel双击打开CSV文件表格正确分列无合并单元格成绩单位秒/个完整显示若列错位检查String.format()中字段是否漏掉双引号或成绩值本身含未转义的逗号我带过的某高校实训班里有同学花两天调不通POI导出换成这个CSV方案后当天下午就交出了带导出功能的完整系统。技术选型没有高低只有适不适合当下的目标——当你需要的是让学生亲手触摸「数据如何变成文件」的每一层那就不该用自动化的黑匣子去遮蔽过程。这个CSV导出就是我给自己留的后悔药任何时候想看清数据流转打开ExportServlet就能从头读到尾。希望帮到你。本文还有配套的精品资源点击获取