资讯详情 Servlet+JSP+MySQL学生成绩系统实战教程
📅 2026/11/6 2:08:19
简介这是一套基于ServletJSPMySQL实现的JavaWeb学生成绩管理系统升级版面向Java初学者与Web开发入门学习者聚焦教学管理场景下的成绩录入、查询、统计与可视化需求。资源包共344个文件含86个Java源码如ScoreDaoImpl、StudentDaoImpl等核心DAO与Service类、86个编译后class文件、62个JavaScript脚本含ECharts图表初始化与数据渲染逻辑、37个JSP页面涵盖登录、课程管理、成绩录入等完整业务视图以及28个CSS样式文件整体压缩包仅3.59MB轻量易部署。已有6725人学习下载说明其结构清晰、功能完整、适合作为课程设计或实训项目参考。读者可直接运行获得包含学生/教师/课程/成绩四大模块的完整系统尤其能通过新增的ECharts动态统计图直观展示各科成绩分布、班级平均分趋势等教学分析维度同时掌握MVC分层开发、数据库连接池配置及前后端数据交互等关键实践技能。1. 一个跑在 Tomcat 里的“老派但能打”的学生成绩系统不靠 Spring Boot纯 Servlet JSP MySQL 就把成绩录入、查询、统计全链路跑通了你可能刚学完 JavaWeb 基础手头有 Tomcat、MySQL、Eclipse/IDEA但面对“学生成绩管理系统”这个经典课题却卡在——到底该从哪写起DAO 层怎么分JSP 表单提交后怎么跳转又不丢数据echarts 图表怎么把数据库里查出来的成绩分布塞进去别急这个项目就是专为这种“刚脱离 HelloWorld但还没摸到 Spring 框架门把手”的阶段量身定制的。它没用任何现代框架Spring、MyBatis、Thymeleaf全程基于 Servlet 处理请求流转、JSP 渲染页面、MySQL 存储数据外加 echarts.js 做前端可视化。所有代码都在一个标准 WebApp 目录结构里没有 Maven 依赖地狱没有 XML 配置迷宫连数据库建表语句都给你写好了。适合两类人一是想真正吃透 JavaWeb 请求-响应生命周期的初学者二是需要快速交付一个可演示、可修改、无外部服务依赖的课程设计或实训项目的开发者。它不是炫技的 Demo而是一个“能跑、能改、能讲清楚每一行为什么这么写”的完整闭环。2. 项目结构与技术栈拆解从 web.xml 到 WEB-INF/lib看清这个“轻量级但自洽”的系统骨架这个项目不是靠 IDE 自动生成的模板而是手动搭出来的标准 JavaWeb 工程结构。它没有隐藏黑匣子所有路径、配置、依赖都明明白白摆在你眼皮底下。理解它的目录结构等于拿到了整个系统的导航图。2.1 标准 WebApp 目录树每个文件夹都承担明确职责项目根目录下是典型的 JavaWeb 结构StudentScoreSystem/ ├── src/ # Java 源码Servlet、DAO、Bean │ ├── dao/ # 数据访问层StudentDAO.java、ScoreDAO.java │ ├── entity/ # 实体类Student.java、Score.java、ChartVO.java专为 echarts 封装 │ ├── servlet/ # 控制层LoginServlet.java、AddScoreServlet.java、ShowChartServlet.java 等 │ └── util/ # 工具类DBUtil.java封装 JDBC 连接、DateUtil.java日期格式化 ├── WebContent/ # Web 资源根目录Eclipse 下或 src/main/webappIDEA 推荐 │ ├── css/ # style.css基础样式含表格、按钮、图表容器 │ ├── js/ # echarts.min.jsv4.9.0已压缩、chart-init.js初始化逻辑 │ ├── images/ # logo.png、bg.jpg可选 │ ├── WEB-INF/ │ │ ├── web.xml # 核心配置Servlet 映射、欢迎页、过滤器本项目暂未用过滤器 │ │ └── lib/ # mysql-connector-java-5.1.47.jarJDBC 驱动必须放这里 │ ├── index.jsp # 登录入口页 │ ├── main.jsp # 主功能页含左侧菜单 右侧 iframe 内容区 │ ├── student/ # 学生管理相关 JSPlistStudent.jsp、addStudent.jsp、editStudent.jsp │ ├── score/ # 成绩管理相关 JSPlistScore.jsp、addScore.jsp、searchScore.jsp │ └── chart/ # 统计图表页scoreChart.jspecharts 容器 初始化脚本 └── README.md # 项目说明含数据库建表 SQL、Tomcat 部署步骤提示WEB-INF是安全边界其下的web.xml和lib不会被浏览器直接访问这是 JavaWeb 的强制规范。lib文件夹里只放了一个.jar说明这个项目对第三方库的依赖极简——这正是它易上手、易排查的核心优势。2.2 技术栈选择逻辑为什么是 Servlet JSP MySQL echartsServlet作为控制器处理所有 HTTP 请求GET/POST。比如/login由LoginServlet接收验证账号密码后决定跳转到main.jsp或返回登录页。它不负责渲染只做业务判断和流程调度。JSP作为视图层嵌入 Java 代码片段% %和 EL 表达式${student.name}来动态生成 HTML。例如listStudent.jsp中用c:forEach需引入 JSTL遍历request.getAttribute(studentList)并生成表格行。注意本项目未强制要求 JSTL但推荐添加jstl-1.2.jar和standard-1.1.2.jar到lib否则c:forEach会报错。MySQL存储学生基本信息学号、姓名、班级、课程信息课程号、课程名、成绩记录学号、课程号、分数、考试时间。三张表通过外键关联符合第二范式。echarts纯前端 JS 库不依赖后端框架。后端 Servlet如ShowChartServlet只负责查出数据组装成 JSON 格式如{categories:[数学,英语],scores:[85,92]}再通过request.setAttribute()传给scoreChart.jsp由chart-init.js读取并初始化图表。这是前后端解耦最干净的做法。2.3 web.xml 配置详解Servlet 映射是请求路由的“交通指挥中心”web.xml是整个 Web 应用的总开关它的配置直接决定了 URL 如何找到对应的 Servlet?xml version1.0 encodingUTF-8? web-app xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xmlnshttp://xmlns.jcp.org/xml/ns/javaee xsi:schemaLocationhttp://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd idWebApp_ID version3.1 display-nameStudentScoreSystem/display-name !-- 欢迎页 -- welcome-file-list welcome-fileindex.jsp/welcome-file /welcome-file-list !-- LoginServlet 配置 -- servlet servlet-nameLoginServlet/servlet-name servlet-classservlet.LoginServlet/servlet-class /servlet servlet-mapping servlet-nameLoginServlet/servlet-name url-pattern/login/url-pattern /servlet-mapping !-- AddScoreServlet 配置 -- servlet servlet-nameAddScoreServlet/servlet-name servlet-classservlet.AddScoreServlet/servlet-class /servlet servlet-mapping servlet-nameAddScoreServlet/servlet-name url-pattern/addScore/url-pattern /servlet-mapping !-- ShowChartServlet 配置关键echarts 数据源 -- servlet servlet-nameShowChartServlet/servlet-name servlet-classservlet.ShowChartServlet/servlet-class /servlet servlet-mapping servlet-nameShowChartServlet/servlet-name url-pattern/showChart/url-pattern /servlet-mapping /web-app这段配置的核心逻辑是当浏览器访问http://localhost:8080/StudentScoreSystem/login时Tomcat 会根据url-pattern/login/url-pattern找到servlet-nameLoginServlet/servlet-name再根据servlet-class定位到src/servlet/LoginServlet.class类并执行其doPost()方法。URL 路径不是随意写的它和servlet-mapping严格一一对应。如果你在 JSP 表单里写form action/login而web.xml里没配/login就会 404如果配了但servlet-class写错包名比如写成servlet.loginServlet少了个大写 L就会 500。这是新手最常翻车的第一关。2.4 数据库设计三张表搞定核心业务字段命名直白不绕弯MySQL 数据库名为studentdb字符集建议用utf8mb4兼容中文和 emoji。建表语句在README.md中给出核心三张表如下表名字段类型说明studentidINT PK AI主键自增stu_idVARCHAR(20) UNIQUE NOT NULL学号业务主键nameVARCHAR(50) NOT NULL姓名class_nameVARCHAR(50)班级名称genderVARCHAR(10)性别男/女courseidINT PK AI主键course_idVARCHAR(20) UNIQUE NOT NULL课程号course_nameVARCHAR(100) NOT NULL课程名scoreidINT PK AI主键stu_idVARCHAR(20) NOT NULL外键关联student.stu_idcourse_idVARCHAR(20) NOT NULL外键关联course.course_idscoreDECIMAL(5,2) CHECK(score0 AND score100)分数带两位小数exam_dateDATE考试日期注意score表没有用复合主键stu_id course_id而是用了独立id。这是为了后续扩展留余地比如同一学生同一课程多次考试。外键约束在开发阶段可以先不启用MySQL 默认 InnoDB 支持但需显式ADD CONSTRAINT避免因数据顺序问题导致建表失败。等数据跑通后再加更稳妥。3. 核心模块代码逐行解析从登录验证到 echarts 图表渲染看懂每一行“为什么这么写”光有结构不够得知道代码怎么联动。下面挑三个最具代表性的模块登录认证控制层入口、成绩新增事务与重定向、图表数据组装前后端数据桥接逐行拆解。3.1 LoginServlet一次 POST 请求背后的完整生命周期LoginServlet是用户进入系统的第一个 Servlet它展示了如何接收表单数据、调用 DAO 查询、判断结果、设置 Session、最后重定向。// src/servlet/LoginServlet.java package servlet; import dao.UserDAO; // 假设有一个 UserDAO 处理账号密码 import entity.User; import util.DBUtil; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.IOException; public class LoginServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 1. 获取表单参数index.jsp 中 input nameusername 和 input namepassword String username request.getParameter(username); String password request.getParameter(password); // 2. 参数非空校验防御性编程防止空指针 if (username null || username.trim().isEmpty() || password null || password.trim().isEmpty()) { request.setAttribute(errorMsg, 用户名和密码不能为空); request.getRequestDispatcher(/index.jsp).forward(request, response); return; // 提前退出避免后续执行 } // 3. 调用 DAO 查询用户实际项目中密码应加密存储此处为简化用明文比对 UserDAO userDAO new UserDAO(); User user userDAO.findByUsername(username); // 4. 验证逻辑用户存在且密码匹配 if (user ! null user.getPassword().equals(password)) { // 5. 登录成功将用户信息存入 Session供后续页面获取当前登录人 HttpSession session request.getSession(); session.setAttribute(currentUser, user); session.setAttribute(username, username); // 也可只存 username // 6. 重定向到主页面使用 sendRedirect不是 forward // 为什么用 redirect避免用户刷新 main.jsp 时重复提交登录请求F5 会重发 POST response.sendRedirect(request.getContextPath() /main.jsp); } else { // 7. 登录失败设置错误信息回到登录页 request.setAttribute(errorMsg, 用户名或密码错误); request.getRequestDispatcher(/index.jsp).forward(request, response); } } // GET 请求也处理避免直接访问 /login 出错 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.sendRedirect(request.getContextPath() /index.jsp); } }关键点说明request.getParameter()是获取表单数据的唯一正道名字必须和 JSP 中input namexxx完全一致区分大小写。request.getRequestDispatcher().forward()是服务器端跳转URL 不变适合传递错误信息response.sendRedirect()是客户端重定向URL 会变适合登录成功后跳转防止重复提交。HttpSession是跨请求保持用户状态的核心机制。session.setAttribute(currentUser, user)后在main.jsp里就能用${sessionScope.currentUser.name}取出姓名。密码明文比较仅用于教学演示。真实项目必须用 BCrypt 或 SHA-256 加盐哈希DAO 层查出的是哈希值比对时对输入密码做同样哈希。3.2 AddScoreServlet一次成绩录入背后的事务控制与页面跳转成绩新增涉及多表操作检查学生是否存在、课程是否存在、插入成绩记录必须保证原子性。本项目用 JDBC 的Connection.setAutoCommit(false)手动管理事务。// src/servlet/AddScoreServlet.java package servlet; import dao.ScoreDAO; import dao.StudentDAO; import dao.CourseDAO; import entity.Score; import util.DBUtil; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.sql.Connection; import java.sql.SQLException; public class AddScoreServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 1. 获取表单参数来自 addScore.jsp String stuId request.getParameter(stuId); String courseId request.getParameter(courseId); String scoreStr request.getParameter(score); String examDate request.getParameter(examDate); // 格式2023-10-01 // 2. 基础校验 if (stuId null || courseId null || scoreStr null || examDate null) { request.setAttribute(errorMsg, 请填写完整信息); request.getRequestDispatcher(/score/addScore.jsp).forward(request, response); return; } // 3. 类型转换分数转 double日期转 java.sql.Date double scoreValue; try { scoreValue Double.parseDouble(scoreStr); if (scoreValue 0 || scoreValue 100) { throw new NumberFormatException(分数必须在 0-100 之间); } } catch (NumberFormatException e) { request.setAttribute(errorMsg, 分数格式错误); request.getRequestDispatcher(/score/addScore.jsp).forward(request, response); return; } java.sql.Date sqlDate; try { sqlDate java.sql.Date.valueOf(examDate); // 自动解析 yyyy-MM-dd } catch (IllegalArgumentException e) { request.setAttribute(errorMsg, 日期格式错误请使用 YYYY-MM-DD 格式); request.getRequestDispatcher(/score/addScore.jsp).forward(request, response); return; } // 4. 开启数据库事务关键 Connection conn null; try { conn DBUtil.getConnection(); // 从工具类获取连接 conn.setAutoCommit(false); // 关闭自动提交 // 5. 检查学生和课程是否存在业务规则 StudentDAO stuDAO new StudentDAO(conn); CourseDAO courseDAO new CourseDAO(conn); if (!stuDAO.existsByStuId(stuId)) { throw new RuntimeException(学生学号 stuId 不存在); } if (!courseDAO.existsByCourseId(courseId)) { throw new RuntimeException(课程号 courseId 不存在); } // 6. 插入成绩记录 Score score new Score(); score.setStuId(stuId); score.setCourseId(courseId); score.setScore(scoreValue); score.setExamDate(sqlDate); ScoreDAO scoreDAO new ScoreDAO(conn); scoreDAO.insert(score); // 7. 提交事务 conn.commit(); // 8. 成功后跳转并传递成功消息用 request.setAttribute 在 forward 时有效 request.setAttribute(successMsg, 成绩录入成功); request.getRequestDispatcher(/score/listScore.jsp).forward(request, response); } catch (SQLException | RuntimeException e) { // 9. 出错回滚事务 if (conn ! null) { try { conn.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } } // 10. 错误信息返回给用户 request.setAttribute(errorMsg, 录入失败 e.getMessage()); request.getRequestDispatcher(/score/addScore.jsp).forward(request, response); } finally { // 11. 关闭连接无论成功失败都要关 if (conn ! null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } } }血泪经验这个 Servlet 是新手最容易写错的地方。常见错误包括忘记conn.setAutoCommit(false)导致部分插入成功、部分失败数据不一致rollback()写在catch里但没判空connconn为 null 时抛NullPointerExceptionforward()时用了response.sendRedirect()导致request.setAttribute()的消息丢失redirect 是新请求旧 request 已销毁日期解析用SimpleDateFormat而不是java.sql.Date.valueOf()后者更简洁且线程安全。3.3 ShowChartServlet scoreChart.jspecharts 图表的数据组装与渲染这是本项目“新增 echarts 统计图”的核心实现。后端只管提供干净的 JSON 数据结构前端负责渲染职责分明。// src/servlet/ShowChartServlet.java package servlet; import dao.ScoreDAO; import entity.ChartVO; // 自定义 VO封装图表所需数据 import util.DBUtil; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.List; public class ShowChartServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 1. 调用 DAO 获取统计所需数据例如各科平均分 ScoreDAO scoreDAO new ScoreDAO(); ListChartVO chartData scoreDAO.getAverageScoreByCourse(); // 2. 将 ListChartVO 存入 request供 JSP 读取 request.setAttribute(chartData, chartData); // 3. 转发到图表页面 request.getRequestDispatcher(/chart/scoreChart.jsp).forward(request, response); } }ChartVO.java定义非常简单只为 echarts 服务// src/entity/ChartVO.java package entity; public class ChartVO { private String courseName; // 课程名对应 echarts 的 xAxis.data private Double avgScore; // 平均分对应 series.data // getter/setter 省略... }scoreChart.jsp是前端渲染的舞台% page contentTypetext/html;charsetUTF-8 languagejava % % taglib prefixc urihttp://java.sun.com/jsp/jstl/core % html head title成绩统计图表/title script src${pageContext.request.contextPath}/js/echarts.min.js/script link relstylesheet href${pageContext.request.contextPath}/css/style.css /head body div idmain stylewidth: 100%; height: 500px;/div script typetext/javascript // 1. 从 JSP 的 requestScope 获取后端传来的 ListChartVO var categories []; var scores []; c:forEach items${chartData} varitem categories.push(${item.courseName}); scores.push(${item.avgScore}); /c:forEach // 2. 初始化 echarts 实例 var myChart echarts.init(document.getElementById(main)); // 3. 配置图表选项Option var option { title: { text: 各科平均分统计, left: center }, tooltip: { trigger: axis }, xAxis: { type: category, data: categories // 动态注入的课程名数组 }, yAxis: { type: value, name: 平均分, min: 0, max: 100 }, series: [{ name: 平均分, type: bar, data: scores // 动态注入的分数数组 }] }; // 4. 使用刚配置好的 option 渲染图表 myChart.setOption(option); // 5. 响应式窗口大小改变时重新渲染 window.addEventListener(resize, function () { myChart.resize(); }); /script /body /html玄学细节c:forEach必须在script标签内因为它是服务端 JSP 标签会在 HTML 发送给浏览器前就执行完毕生成的是纯 JS 代码categories.push(数学); scores.push(85);。myChart.resize()是 echarts 的响应式必备否则浏览器窗口缩放时图表会变形。pageContext.request.contextPath是获取应用上下文路径的安全写法确保 JS/CSS 路径不随部署路径变化而失效比如部署在/myapp下contextPath就是/myapp。4. 运行与部署全流程从 MySQL 建库到 Tomcat 启动一步不跳过再好的代码跑不起来等于零。这一章带你从零开始把项目真正在本地跑起来。每一步都对应一个具体命令或点击动作拒绝“自行百度”。4.1 数据库准备建库、建表、插测试数据3 分钟搞定启动 MySQL 服务Windows服务管理器中启动MySQL80Macbrew services start mysqlLinuxsudo systemctl start mysqld。登录 MySQL 客户端mysql -u root -p输入密码后进入mysql提示符。创建数据库并指定字符集CREATE DATABASE studentdb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE studentdb;执行建表 SQL以下内容复制粘贴进mysql-- 创建 student 表 CREATE TABLE student ( id INT PRIMARY KEY AUTO_INCREMENT, stu_id VARCHAR(20) UNIQUE NOT NULL, name VARCHAR(50) NOT NULL, class_name VARCHAR(50), gender VARCHAR(10) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 创建 course 表 CREATE TABLE course ( id INT PRIMARY KEY AUTO_INCREMENT, course_id VARCHAR(20) UNIQUE NOT NULL, course_name VARCHAR(100) NOT NULL ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 创建 score 表 CREATE TABLE score ( id INT PRIMARY KEY AUTO_INCREMENT, stu_id VARCHAR(20) NOT NULL, course_id VARCHAR(20) NOT NULL, score DECIMAL(5,2) CHECK(score 0 AND score 100), exam_date DATE, FOREIGN KEY (stu_id) REFERENCES student(stu_id), FOREIGN KEY (course_id) REFERENCES course(course_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;插入几条测试数据方便立刻看到效果INSERT INTO student (stu_id, name, class_name, gender) VALUES (2021001, 张三, 计算机2101, 男), (2021002, 李四, 计算机2101, 女); INSERT INTO course (course_id, course_name) VALUES (CS101, Java程序设计), (CS102, 数据库原理); INSERT INTO score (stu_id, course_id, score, exam_date) VALUES (2021001, CS101, 85.5, 2023-09-15), (2021001, CS102, 92.0, 2023-09-20), (2021002, CS101, 78.0, 2023-09-15), (2021002, CS102, 88.5, 2023-09-20);提示如果执行FOREIGN KEY时报错ERROR 1215 (HY000): Cannot add foreign key constraint大概率是student和course表还没建或者建表时没用ENGINEInnoDB。删掉表重来即可DROP TABLE IF EXISTS score; DROP TABLE IF EXISTS student; DROP TABLE IF EXISTS course;4.2 Tomcat 配置与项目部署两种方式任选其一方式一Eclipse 内置 Tomcat最简单推荐新手确保 Eclipse 已安装Eclipse IDE for Enterprise Java and Web Developers带 Web Tools Platform。File → Import → Web → Existing Dynamic Web Project浏览到你的StudentScoreSystem文件夹勾选Copy projects into workspaceFinish。右键项目 →Properties → Project Facets确认Dynamic Web Module版本是3.1对应web.xml中的version3.1Java版本是1.8或11。Window → Preferences → Server → Runtime Environments点击Add选择Apache Tomcat v9.0或 v8.5NextBrowse选择你下载解压的 Tomcat 根目录如D:\apache-tomcat-9.0.83Finish。File → New → Other → Server选择Tomcat v9.0 ServerNextAdd你的StudentScoreSystem项目Finish。右键服务器 →Start等待控制台输出Server startup in [xxx] milliseconds。浏览器打开http://localhost:8080/StudentScoreSystem/看到登录页即成功。方式二手动部署到独立 Tomcat更贴近生产环境将整个StudentScoreSystem文件夹复制到 Tomcat 的webapps/目录下路径apache-tomcat-9.0.83\webapps\StudentScoreSystem。确认StudentScoreSystem\WEB-INF\lib\下有mysql-connector-java-5.1.47.jar版本必须匹配你的 MySQL5.1.x 适配 MySQL 5.7/8.0。修改StudentScoreSystem\WEB-INF\classes\util\DBUtil.java中的数据库连接信息private static final String URL jdbc:mysql://localhost:3306/studentdb?useSSLfalseserverTimezoneUTC; private static final String USER root; // 你的 MySQL 用户名 private static final String PASSWORD your_password; // 你的 MySQL 密码启动 Tomcat双击bin\startup.batWindows或bin/startup.shMac/Linux。查看logs\catalina.outLinux/Mac或logs\catalina.logWindows确认没有ClassNotFoundException: com.mysql.jdbc.Driver说明驱动加载成功和SQLException说明数据库连接成功。浏览器访问http://localhost:8080/StudentScoreSystem/。注意如果遇到HTTP Status 404 – Not Found先检查webapps下文件夹名是否和 URL 中的一致大小写敏感再检查web.xml中的welcome-file是否指向存在的文件index.jsp。4.3 常见问题排查3 条真实踩坑记录帮你省下 2 小时调试时间现象 1登录时提示 “HTTP Status 404 – /StudentScoreSystem/login”原因web.xml中url-pattern/login/url-pattern和 JSP 表单的action不一致。常见错误是 JSP 里写了form actionlogin相对路径会拼接到当前 URL 后而当前页是http://localhost:8080/StudentScoreSystem/index.jsp提交后变成http://localhost:8080/StudentScoreSystem/index.jsp/login自然 404。解决统一用绝对路径。JSP 表单写成form action${pageContext.request.contextPath}/login这样无论从哪个页面提交都会正确指向http://localhost:8080/StudentScoreSystem/login。现象 2echarts 图表区域一片空白控制台无报错原因scoreChart.jsp中echarts.min.js的路径错误导致 JS 文件 404 加载失败。常见于没用${pageContext.request.contextPath}直接写script src/js/echarts.min.js而项目部署在/myapp下实际路径应为/myapp/js/echarts.min.js。解决检查浏览器开发者工具F12的Network标签页筛选JS看echarts.min.js是否返回 200。如果不是修改 script 标签为script src${pageContext.request.contextPath}/js/echarts.min.js。现象 3成绩录入后listScore.jsp显示空列表但数据库里有数据原因ScoreDAO.listAll()方法中 SQL 查询语句写错比如漏了FROM score或者JOIN时 ON 条件写成ON s.stu_id c.stu_idc是 course 表没有stu_id字段。解决在ScoreDAO的listAll()方法里把 SQL 语句System.out.println(sql);打印出来复制到 MySQL 客户端执行看是否能查出数据。重点检查表别名和字段名是否匹配。5. 二次开发实战从“能跑”到“能改”3 个高频改造需求的落地指南项目跑通只是起点。课程设计要加分实训报告要体现思考实际工作要快速响应需求——这些都要求你能动手改。下面三个改造需求覆盖了 80% 的真实场景我用“改什么、怎么改、验证方法”三步法带你实操。5.1 需求增加“按班级查询学生成绩”功能后端 前端联动这是最常被老师点名的功能。目标在searchScore.jsp页面增加班级下拉框选择后只显示该班级所有学生的成绩。改什么后端ScoreDAO新增方法ListScore listByClass(String className)SQL 加JOIN student ON score.stu_id student.stu_id WHERE student.class_name ?。前端searchScore.jsp增加select nameclassNameoption value全部班级/optionoption value计算机2101计算机2101/option/select和对应表单提交逻辑。ServletSearchScoreServlet的doPost()中获取className参数调用新 DAO 方法。怎么改代码片段// src/dao/ScoreDAO.java 新增方法 public ListScore listByClass(String className) throws SQLException { String sql SELECT s.*, st.name AS stu_name, c.course_name FROM score s JOIN student st ON s.stu_id st.stu_id JOIN course c ON s.course_id c.course_id WHERE st.class_name ?; return queryRunner.query(sql, new BeanListHandler(Score.class), className); }!-- searchScore.jsp 表单部分 -- form action${pageContext.request.contextPath}/searchScore methodpost label班级/label select nameclassName option value全部班级/option option value计算机2101计算机2101/option option value软件2102软件2102/option /select input typesubmit value查询 /form// src/servlet/SearchScoreServlet.java 的 doPost() String className request.getParameter(className); ListScore scoreList; if (className ! null !className.trim().isEmpty()) { scoreList p a hrefhttps://download.csdn.net/download/CDWLX/13231851 stylecolor:#ec7500;font-size:14px; 本文还有配套的精品资源点击获取 /a img altmenu-r.4af5f7ec.gif srchttps://csdnimg.cn/release/wenkucmsfe/public/img/menu-r.4af5f7ec.gif stylewidth:16px;margin-left:4px;vertical-align:text-bottom;cursor:text; /p