简介这是一套基于ServletJSPMySQL实现的JavaWeb学生成绩管理系统升级版面向Java初学者与Web开发入门学习者聚焦教学管理场景下的成绩录入、查询、统计与可视化需求。资源包共344个文件包含86个Java源码如ScoreDaoImpl、StudentDaoImpl等核心DAO与Service类、86个编译后class文件、62个JavaScript脚本含ECharts图表初始化与数据渲染逻辑、37个JSP页面涵盖登录、课程管理、成绩录入等完整功能视图以及28个CSS样式文件整体压缩包仅3.59MB轻量易部署。已有6724人学习下载体现了其在实践教学中的广泛认可。读者可直接运行项目获得含前后端交互、数据库操作、权限控制及ECharts动态统计图如各科成绩分布、班级平均分趋势在内的完整可运行系统同时通过清晰的分层代码结构DAO/Service/Servlet/JSP深入理解JavaWeb经典MVC开发模式。1. 一个跑在 Tomcat 里的“老派但能打”的学生成绩系统不靠 Spring Boot纯 ServletJSPMySQL 搞定增删改查动态图表你可能刚学完 Java Web 基础手头有《Java Web 编程入门》的课设作业或者正被导师塞来一个“用传统技术栈做成绩管理”的毕设任务——别慌这个项目就是为你量身写的“可落地、可交差、可讲清楚”的完整工程。它没上 Spring Boot没用 Vue/React而是用最原始也最透彻的 Servlet 处理请求、JSP 渲染页面、MySQL 存数据外加 ECharts 插入统计图把“学生信息录入→课程绑定→成绩录入→按班级/课程/学期多维查询→柱状图/折线图可视化”这条链路全打通了。它不是玩具 Demo而是真能部署到本地 Tomcat、支持 50 学生、3~5 门课程、多学期成绩录入的真实小系统所有代码都在一个标准 Web 工程结构里没有隐藏模块没有外部依赖黑盒连数据库建表语句都写死在init.sql里。如果你需要的是“今天下载、今晚配环境、明早就能跑通并截图给老师看”的资源那它比一堆花哨但跑不起来的 GitHub 项目更靠谱——毕竟Servlet 的doPost()里怎么取参数、JSP 的c:forEach怎么遍历 List、ECharts 的option怎么从 Java 后端 JSON 包里接数据这里全给你写明白了。2. 技术栈与工程结构为什么选 ServletJSP这不是倒退是可控性刚需这个项目的技术选型不是怀旧而是在教学场景和轻量部署下做的理性选择。Spring Boot 虽快但对初学者来说自动配置、Starter 依赖、内嵌 Tomcat 这些“便利”背后全是黑匣子——你改个端口失败可能卡在application.properties和pom.xml的隐式冲突里而 ServletJSP 是“所见即所得”web.xml里配路径StudentServlet.java里写逻辑list.jsp里写 HTMLJSTL错在哪日志打在哪一眼可见。更重要的是它强制你理解 HTTP 请求生命周期从浏览器发 POST 到/addScoreTomcat 如何路由到ScoreServletdoPost()怎么解析request.getParameter(studentId)再怎么调 DAO 层执行INSERT INTO score...最后request.setAttribute(msg, 添加成功)转发回 JSP。这种链路透明性是调试能力的底层肌肉。2.1 目录结构一个标准 Java Web 工程的骨架整个项目是一个标准的 Eclipse/IDEA 可识别的 Dynamic Web Project目录层级清晰无冗余src/ ├── dao/ // 数据访问层纯 JDBC 封装含 ConnectionUtil单例获取连接 │ ├── StudentDAO.java │ ├── CourseDAO.java │ └── ScoreDAO.java ├── entity/ // 实体类Student、Course、Score含 getter/setter 和 toString() │ ├── Student.java │ ├── Course.java │ └── Score.java ├── servlet/ // 控制层每个功能一个 Servlet继承 HttpServlet │ ├── StudentServlet.java // 处理学生列表、新增、删除 │ ├── CourseServlet.java // 处理课程管理 │ └── ScoreServlet.java // 核心成绩录入、修改、按条件查询、导出统计数据 ├── util/ // 工具类DBUtils简化 JDBC 操作、DateUtil格式化日期 │ └── DBUtils.java WebContent/ ├── WEB-INF/ │ ├── web.xml // 核心配置Servlet 映射、欢迎页、过滤器UTF-8 编码 │ └── lib/ // 仅 3 个 jarmysql-connector-java-5.1.47.jar、jstl-1.2.jar、standard-1.1.2.jar ├── css/ │ └── style.css // 简洁表格样式、按钮 hover 效果 ├── js/ │ ├── echarts.min.js // ECharts 4.9.0兼容 IE10已压缩 │ └── chart-init.js // 初始化图表接收 JSP 传来的 JSON 数据渲染柱状图/折线图 ├── images/ │ └── logo.png // 页面左上角小图标 ├── index.jsp // 首页导航菜单学生/课程/成绩/统计 ├── student/ │ ├── list.jsp // 学生列表页表格展示 “新增”按钮 │ ├── add.jsp // 新增表单页姓名、学号、班级、入学年份输入框 │ └── edit.jsp // 编辑页回显数据的表单 ├── course/ │ ├── list.jsp // 课程列表课程名、学分、开课学期 │ └── add.jsp // 课程新增表单 ├── score/ │ ├── list.jsp // 成绩主页面含查询表单按班级/课程/学期 成绩表格 “新增成绩”按钮 │ ├── add.jsp // 成绩录入页下拉选择学生、课程、学期输入分数 │ └── chart.jsp // 统计图表页核心含 ECharts 容器 JSP 脚本块生成 JSON 数据 └── common/ └── header.jsp // 公共页眉包含导航栏提示web.xml中关键配置必须严格匹配 Servlet 类名和 URL 模式例如servlet servlet-nameScoreServlet/servlet-name servlet-classservlet.ScoreServlet/servlet-class /servlet servlet-mapping servlet-nameScoreServlet/servlet-name url-pattern/score/*/url-pattern /servlet-mapping这意味着所有以/score/开头的请求如/score/list,/score/add都会由ScoreServlet的doGet()或doPost()处理*是通配符不是占位符。2.2 数据库设计三张表外键约束清晰直接贴进 MySQL 就能用数据库名为studentdb字符集utf8mb4排序规则utf8mb4_unicode_ci避免中文乱码。建表语句全部写在项目根目录的init.sql文件中复制粘贴到 MySQL 客户端即可执行。三张表关系明确无冗余字段-- 学生表主键 id学号唯一索引 CREATE TABLE student ( id INT PRIMARY KEY AUTO_INCREMENT, stu_no VARCHAR(20) NOT NULL UNIQUE, name VARCHAR(50) NOT NULL, class_name VARCHAR(50), enrollment_year YEAR ); -- 课程表主键 id课程名唯一 CREATE TABLE course ( id INT PRIMARY KEY AUTO_INCREMENT, course_name VARCHAR(100) NOT NULL UNIQUE, credit INT DEFAULT 2, semester VARCHAR(20) -- 如 2023-2024-1 表示第一学期 ); -- 成绩表联合主键 (student_id, course_id, semester)外键关联 student 和 course CREATE TABLE score ( student_id INT NOT NULL, course_id INT NOT NULL, semester VARCHAR(20) NOT NULL, score DECIMAL(5,2) CHECK(score 0 AND score 100), PRIMARY KEY (student_id, course_id, semester), FOREIGN KEY (student_id) REFERENCES student(id) ON DELETE CASCADE, FOREIGN KEY (course_id) REFERENCES course(id) ON DELETE CASCADE );注意score表的主键设计是关键。它用(student_id, course_id, semester)作为联合主键确保“同一个学生在同一个学期同一门课”只有一条成绩记录避免重复录入。ON DELETE CASCADE表示如果删除某个学生或课程其对应的成绩记录会自动清除省去手动清理的麻烦。2.3 核心交互流程一个成绩录入请求的完整生命周期我们以“老师录入张三在《Java程序设计》课程 2023-2024-1 学期的成绩为 89.5 分”为例走一遍从点击按钮到页面刷新的全过程前端触发在score/add.jsp页面用户选择学生“张三”对应student.id101、课程“Java程序设计”对应course.id201、学期“2023-2024-1”输入分数89.5点击“提交”请求发送表单methodpostaction/score/add浏览器向 Tomcat 发送 POST 请求携带参数studentId101courseId201semester2023-2024-1score89.5Servlet 接收ScoreServlet.doPost()被调用通过request.getParameter()获取各参数封装成Score对象业务校验检查studentId和courseId是否真实存在调用StudentDAO.findById()和CourseDAO.findById()分数是否在 0~100 之间DAO 执行校验通过后调用ScoreDAO.add(score)内部执行 SQLINSERT INTO score (student_id, course_id, semester, score) VALUES (?, ?, ?, ?)使用PreparedStatement防止 SQL 注入结果反馈插入成功request.setAttribute(msg, 成绩录入成功)然后request.getRequestDispatcher(list.jsp).forward(request, response)转发回成绩列表页JSP 渲染list.jsp通过c:if test${not empty msg}判断并显示提示信息同时调用ScoreDAO.findAll()查询最新数据用c:forEach循环输出表格行。这个流程里每一步的输入、输出、异常分支都清晰可见没有框架魔法全是可控代码。3. 核心模块拆解ScoreServlet 与 ECharts 图表页的硬核实现ScoreServlet是整个系统的中枢神经它不处理页面展示只专注“数据流转”而chart.jsp则是用户体验的高光时刻把枯燥的数字变成直观的图表。这两块代码是项目价值的核心体现也是你二次开发时最常动的地方。3.1 ScoreServlet一个方法一个职责拒绝大杂烩ScoreServlet.java的doPost()方法根据request.getRequestURI()的末尾路径分发到不同私有方法。这种设计比用if-else判断action参数更符合 RESTful 思路也便于后期扩展。以下是add()方法的完整实现含关键注释// servlet/ScoreServlet.java protected void add(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 1. 获取请求参数注意参数名必须与表单 input 的 name 属性完全一致 String studentIdStr request.getParameter(studentId); String courseIdStr request.getParameter(courseId); String semester request.getParameter(semester); String scoreStr request.getParameter(score); // 2. 参数非空校验基础防护 if (studentIdStr null || courseIdStr null || semester null || scoreStr null) { request.setAttribute(msg, 参数缺失请检查表单填写); request.getRequestDispatcher(/score/add.jsp).forward(request, response); return; } try { // 3. 类型转换字符串转数字捕获 NumberFormatException int studentId Integer.parseInt(studentIdStr); int courseId Integer.parseInt(courseIdStr); double score Double.parseDouble(scoreStr); // 4. 业务校验学生是否存在 StudentDAO studentDAO new StudentDAO(); Student student studentDAO.findById(studentId); if (student null) { request.setAttribute(msg, 学生ID不存在 studentId); request.getRequestDispatcher(/score/add.jsp).forward(request, response); return; } // 5. 课程是否存在 CourseDAO courseDAO new CourseDAO(); Course course courseDAO.findById(courseId); if (course null) { request.setAttribute(msg, 课程ID不存在 courseId); request.getRequestDispatcher(/score/add.jsp).forward(request, response); return; } // 6. 分数范围校验 if (score 0 || score 100) { request.setAttribute(msg, 成绩必须在 0~100 之间); request.getRequestDispatcher(/score/add.jsp).forward(request, response); return; } // 7. 构建 Score 对象并保存 Score scoreObj new Score(); scoreObj.setStudentId(studentId); scoreObj.setCourseId(courseId); scoreObj.setSemester(semester); scoreObj.setScore(score); ScoreDAO scoreDAO new ScoreDAO(); boolean success scoreDAO.add(scoreObj); // 返回 true 表示插入成功 // 8. 结果跳转成功则回列表页失败则留在当前页并提示 if (success) { request.setAttribute(msg, ✅ 成绩录入成功学生 student.getName() 课程 course.getCourseName() 分数 score); request.getRequestDispatcher(/score/list.jsp).forward(request, response); } else { request.setAttribute(msg, ❌ 录入失败请重试或联系管理员); request.getRequestDispatcher(/score/add.jsp).forward(request, response); } } catch (NumberFormatException e) { // 专门捕获数字转换异常提示更友好 request.setAttribute(msg, ❌ 学号或课程ID格式错误请输入纯数字); request.getRequestDispatcher(/score/add.jsp).forward(request, response); } }逻辑说明这段代码体现了“防御式编程”思想。它不假设前端传来的一定是合法数据而是层层校验先判空再转类型再查数据库再业务规则分数范围最后才执行插入。每个return都确保流程不会继续向下执行避免空指针或脏数据。request.getRequestDispatcher().forward()是服务器端跳转URL 不变适合表单提交后的结果反馈而response.sendRedirect()是客户端重定向URL 会变适合登录成功后跳首页等场景。3.2 chart.jspJSP 生成 JSONECharts 消费数据零 AJAX 请求很多新手以为 ECharts 必须用 AJAX 异步加载数据其实大可不必。在这个项目里chart.jsp在服务端就完成了数据聚合和 JSON 生成然后直接注入到前端 JS 变量中ECharts 初始化时直接读取。这样做有两个好处一是减少一次 HTTP 请求页面加载更快二是数据生成逻辑如按班级统计平均分完全由 Java 控制更安全、更易调试。以下是chart.jsp的核心部分展示了如何从数据库查出“各班级平均分”并转成 ECharts 所需的categoriesX轴和dataY轴数组% page contentTypetext/html;charsetUTF-8 languagejava % % taglib prefixc urihttp://java.sun.com/jsp/jstl/core % % page importjava.util.* % % page importentity.* % % page importdao.* % !DOCTYPE html html head title成绩统计图表/title script src../js/echarts.min.js/script /head body div idmain stylewidth: 100%; height: 500px;/div script typetext/javascript // 1. JSP 脚本块在页面渲染前由服务器执行 Java 代码生成 JSON 字符串 % // 查询各班级平均分SQL 为 SELECT class_name, AVG(s.score) as avg_score FROM student st JOIN score s ON st.ids.student_id GROUP BY st.class_name ScoreDAO scoreDAO new ScoreDAO(); ListMapString, Object classAvgList scoreDAO.findClassAverage(); // 构建 categories 数组班级名 StringBuilder categoriesBuilder new StringBuilder([); // 构建 data 数组平均分 StringBuilder dataBuilder new StringBuilder([); for (int i 0; i classAvgList.size(); i) { MapString, Object row classAvgList.get(i); String className (String) row.get(class_name); Double avgScore (Double) row.get(avg_score); // 拼接 JSON 字符串注意引号转义 if (i 0) { categoriesBuilder.append(,); dataBuilder.append(,); } categoriesBuilder.append(\).append(className).append(\); dataBuilder.append(avgScore ! null ? String.format(%.2f, avgScore) : 0.00); } categoriesBuilder.append(]); dataBuilder.append(]); % // 2. 将 JSP 生成的字符串赋值给 JS 变量 var categories % categoriesBuilder.toString() %; var seriesData % dataBuilder.toString() %; // 3. 初始化 ECharts 实例 var myChart echarts.init(document.getElementById(main)); var option { title: { text: 各班级平均分统计, left: center }, tooltip: { trigger: axis, formatter: {b}班{c}分 // {b} 是 X轴值班级名{c} 是 Y轴值平均分 }, xAxis: { type: category, data: categories // 这里直接用了 JSP 生成的数组 }, yAxis: { type: value, name: 平均分, min: 0, max: 100, interval: 10 }, series: [{ name: 平均分, type: bar, // 柱状图 data: seriesData, // 这里直接用了 JSP 生成的数组 itemStyle: { color: #5470C6 } }], grid: { left: 3%, right: 4%, bottom: 3%, containLabel: true } }; // 4. 使用刚指定的配置项和数据显示图表 myChart.setOption(option); /script /body /html参数说明findClassAverage()是ScoreDAO中的一个新方法它执行聚合查询返回ListMapString, Object其中每个Map对应一行结果key 为列名class_name,avg_score。JSP 脚本块里的StringBuilder是为了安全拼接 JSON避免直接out.print()可能引发的 XSS虽然此处数据来自数据库风险低但养成习惯很重要。最终生成的 JS 变量categories和seriesData是标准 JSON 数组ECharts 可直接消费。这种“服务端预计算 客户端渲染”的模式在数据量不大1000 条时性能和可维护性远超纯前端 AJAX。4. 环境搭建与运行从零开始5 分钟跑通你的第一个 Java Web 项目别被“Tomcat”、“JDBC”这些词吓住。这个项目对环境要求极低一台能上网的 Windows/Mac/Linux 电脑加上 JDK 8 和一个轻量级 IDE甚至记事本都能改就能完成全部操作。下面步骤经过实测跳过所有玄学环节直击关键。4.1 基础环境准备JDK 8 Tomcat 8.5 MySQL 5.7或 8.0JDK 8必须是 8u202 及以上版本因mysql-connector-java-5.1.47.jar依赖较新的 JVM 特性。验证命令java -version输出应为java version 1.8.0_XXX。Tomcat 8.5.x推荐 8.5.94最新稳定版不要用 9.x 或 10.x因为web.xml的 DTD 声明和 Servlet API 版本不兼容。解压后记住路径比如D:\apache-tomcat-8.5.94。MySQL 5.7 或 8.0安装时务必勾选“Add MySQL to PATH”并设置 root 密码为123456项目默认密码后续可在util/ConnectionUtil.java中修改。验证命令mysql -u root -p输入密码后进入 MySQL 命令行。提示如果 MySQL 8.0 连接报错Public Key Retrieval is not allowed请在ConnectionUtil.java的 JDBC URL 后追加?allowPublicKeyRetrievaltrueuseSSLfalse这是 MySQL 8.0 的安全策略变更不是项目 bug。4.2 项目导入 IDEA三步搞定告别 Eclipse 兼容性问题虽然项目是 Eclipse 创建的但在 IDEA 中导入更稳。打开 IDEA →File→Project Structure→Project→ 设置Project SDK为 JDK 8 →OK。然后File→New→Project from Existing Sources...→ 选择项目根目录含src/和WebContent/的文件夹在向导中选择Import project from external model→Eclipse→ 勾选Create module groups→Next关键一步在Project Settings→Modules→ 选中项目名 →Sources标签页 → 点击WebContent文件夹 → 点击上方Mark as: Web Resources Root→ 再点击src文件夹 →Mark as: Sources最后Project Settings→Artifacts→→Web Application: Archive→From modules...→ 选择你的模块 →OK。这一步生成.war包是部署必需。注意WebContent必须标记为 Web Resources Root否则index.jsp、css/、js/等静态资源无法被 Tomcat 正确映射。这是新手导入失败的最高频原因务必确认。4.3 数据库初始化执行 init.sql一劳永逸打开 MySQL 客户端如 MySQL Workbench 或命令行执行以下命令-- 创建数据库 CREATE DATABASE studentdb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- 切换到该库 USE studentdb; -- 执行 init.sql 文件内容复制粘贴即可 -- 此处省略建表语句即上文 2.2 节的 SQL排查如果执行init.sql报错Unknown character set: utf8mb4说明 MySQL 版本太低5.5.3请升级或临时改为utf8不推荐可能存 emoji 乱码。4.4 Tomcat 配置与启动让项目跑在 localhost:8080在 IDEA 中Run→Edit Configurations...→→Tomcat Server→Local→Configure...→Application server选择你解压的 Tomcat 路径 →OK。然后Deployment标签页 →→Artifact→ 选择你刚才创建的.war exploded带 exploded 的那个→Application context改为/根路径这样访问http://localhost:8080就是首页Server标签页 →JRE选择 JDK 8 →On Update action选Update classes and resources热更新→On frame deactivation选Update resources点击右上角绿色三角形启动 Tomcat。启动成功后控制台会输出INFO: Server startup in [xxx] ms然后浏览器打开http://localhost:8080看到首页导航栏即表示项目已跑通。5. 避坑指南那些让我重启三次 Tomcat 的血泪经验这个项目看似简单但环境差异和细节疏忽足以让你卡在“404”或“500”一整天。以下是我在某高校实验室帮 12 个同学调试时高频出现的 5 个坑按现象、原因、解决三步法整理亲测有效。5.1 现象访问http://localhost:8080显示 404但 Tomcat 启动日志无报错原因IDEA 中Artifact未正确部署或Application context被误设为/student等非根路径导致资源映射失败。解决回到Run→Edit Configurations...→Deployment→ 检查Application context是否为/再检查Artifact是否选中了xxx.war exploded不是xxx.war最后点击Fix按钮如果出现。5.2 现象点击“新增学生”按钮页面跳转到http://localhost:8080/student/add但显示 404原因web.xml中StudentServlet的url-pattern配置错误比如写成了/student/add精确匹配而实际应该用通配符/student/*。解决打开WebContent/WEB-INF/web.xml找到StudentServlet的servlet-mapping将url-pattern改为/student/*保存后重启 Tomcat。5.3 现象成绩录入后list.jsp表格为空但数据库里有数据原因ScoreDAO.findAll()方法中 SQL 语句写错比如漏了JOIN或WHERE条件导致查不到记录或Score实体类的setter方法名与数据库字段不匹配如数据库字段student_id但setStudent_id()应为setStudentId()。解决在ScoreDAO.findAll()的 SQL 后加System.out.println(sql)复制到 MySQL 客户端执行看是否返回预期数据检查Score.java中setStudentId(int studentId)方法名是否驼峰命名去掉下划线JDBCResultSet映射依赖 getter/setter 名称。5.4 现象chart.jsp页面空白浏览器控制台报错echarts is not defined原因echarts.min.js文件路径错误。chart.jsp中script src../js/echarts.min.js的..表示上一级目录但若chart.jsp在WebContent/score/chart.jsp则..指向WebContent/而js/文件夹在WebContent/js/路径是对的但如果误把js/放在WebContent/WEB-INF/js/WEB-INF 下资源不可被直接访问就会 404。解决在浏览器地址栏直接访问http://localhost:8080/js/echarts.min.js如果显示 JS 代码则路径正确如果 404则把js/文件夹从WEB-INF/移出放到WebContent/同级目录下。5.5 现象中文显示为??如学生姓名显示“张??”原因三个地方编码不统一① MySQL 数据库/表字符集不是utf8mb4②ConnectionUtil.java中 JDBC URL 缺少?characterEncodingutf8mb4③web.xml中filter的CharacterEncodingFilter未配置或encoding参数不是UTF-8。解决① 用SHOW CREATE DATABASE studentdb;确认字符集② 修改ConnectionUtil.getConnection()中的 URL 为jdbc:mysql://localhost:3306/studentdb?characterEncodingutf8mb4useSSLfalse③ 确保web.xml中有如下 filter 配置filter filter-nameCharacterEncodingFilter/filter-name filter-classorg.apache.catalina.filters.SetCharacterEncodingFilter/filter-class init-param param-nameencoding/param-name param-valueUTF-8/param-value /init-param /filter filter-mapping filter-nameCharacterEncodingFilter/filter-name url-pattern/*/url-pattern /filter-mapping6. 二次开发实战从“能跑”到“能改”加一个“成绩导出 Excel”功能现在系统能跑、能查、能画图但老师提了个新需求“能不能把全班成绩导出成 Excel 表格我发给家长”——这正是检验你是否真正吃透项目的试金石。下面我带你从零加一个ExportServlet不引入任何新框架如 Apache POI只用原生 Servlet 输出 CSV逗号分隔值Excel 可直接双击打开兼容性 100%且代码不到 50 行。6.1 新增 ExportServlet三步注册专注数据组装首先在src/servlet/下新建ExportServlet.java// servlet/ExportServlet.java package servlet; import dao.ScoreDAO; import entity.Score; 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.io.PrintWriter; import java.net.URLEncoder; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; public class ExportServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 1. 设置响应头告知浏览器这是附件且文件名为含时间戳的 CSV String fileName 成绩导出_ new SimpleDateFormat(yyyyMMdd_HHmmss).format(new Date()) .csv; fileName URLEncoder.encode(fileName, UTF-8); // 解决中文文件名乱码 response.setContentType(text/csv;charsetUTF-8); response.setHeader(Content-Disposition, attachment; filename fileName); // 2. 获取 PrintWriter用于写 CSV 内容 PrintWriter out response.getWriter(); // 3. 写 CSV 表头用英文避免 Excel 乱码 out.println(StudentID,StudentName,CourseName,Semester,Score); // 4. 查询所有成绩数据 ScoreDAO scoreDAO new ScoreDAO(); ListScore scoreList scoreDAO.findAll(); // 此方法需在 ScoreDAO 中已存在 // 5. 遍历数据拼接 CSV 行字段间用逗号字符串用双引号包裹 for (Score score : scoreList) { // 这里需要 ScoreDAO.findAll() 返回的 Score 对象包含学生姓名和课程名 // 所以 ScoreDAO.findAll() 应改为 JOIN 查询或新增 findAllWithNames() 方法 // 为简化假设 score 对象已有 getName() 和 getCourseName() 方法 String line String.format(\%s\,\%s\,\%s\,\%s\,\%.2f\, score.getStudentId(), score.getStudentName(), // 需在 Score 实体类中添加此字段及 getter score.getCourseName(), // 同上 score.getSemester(), score.getScore()); out.println(line); } out.close(); } }逻辑说明ExportServlet只做一件事——生成 CSV 响应流。response.setContentType(text/csv;charsetUTF-8)告诉浏览器这是 CSV 文本且编码为 UTF-8Content-Disposition头强制浏览器下载而非在页面打开URLEncoder.encode()解决中文文件名在 Chrome/Firefox 下的乱码问题。CSV 格式简单每行一条记录字段用逗号分隔含逗号或换行的字段用双引号包裹。这里用String.format()拼接比字符串更安全。6.2 扩展 ScoreDAO 与 Score 实体让 JOIN 查询结果可被直接使用ExportServlet依赖Score对象能直接拿到学生姓名和课程名但原始Score实体只有studentId和courseId。我们需要扩展它在entity/Score.java中添加两个新字段及 getterprivate String studentName; // 新增学生姓名 private String courseName; // 新增课程名称 // getter 方法IDEA 可自动生成 public String getStudentName() { return studentName; } public void setStudentName(String studentName) { this.studentName studentName; } public String getCourseName() { return courseName; } public void setCourseName(String courseName) { this.courseName courseName; }在dao/ScoreDAO.java中新增findAllWithNames()方法执行三表 JOIN// dao/ScoreDAO.java public ListScore findAllWithNames() { String sql SELECT s.student_id, s.course_id, s.semester, s.score, st.name AS studentName, c.course_name AS courseName FROM score s JOIN student st ON s.student_id st.id JOIN course c ON s.course_id c.id; return queryList(sql, Score.class); // 假设 queryList 是通用查询方法能将 ResultSet 映射到 Score 对象 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