简介:本资源是一套完整、高分通过的JavaWeb学生成绩管理系统项目源码,专为计算机及相关专业学生设计,适用于课程设计、期末大作业与项目实战训练。系统涵盖学生、教师、课程、成绩等核心模块,采用JSP+Servlet+MySQL技术栈,结构清晰、代码规范,具备良好的可读性与可扩展性。压缩包共337个文件,含84个Java源码、84个编译后Class文件、36个JSP页面、61个JavaScript脚本及28个CSS样式文件,辅以SQL建表语句、Jar依赖库与基础静态资源,整体体积仅3.32MB,轻量易部署。已有5296人学习下载,项目经导师指导评审获98分高分,配套代码逻辑完整、DAO层与Servlet层职责分明,预览可见ScoreDaoImpl、StudentDaoImpl、ServletUploadFile等典型类,便于理解MVC分层实现与常见业务流程(如成绩导入导出、教师信息管理)。
1. 这不是又一个“JavaWeb CRUD练习”,而是能直接跑通、查得准、改得动、交得上的高分项目实战现场
你手头那份标着“高分项目”的学生成绩管理系统源码,大概率正躺在压缩包里吃灰——IDEA 打开报红、Tomcat 启动失败、登录页空白、数据库连不上、增删改查点不动……这不是你代码能力的问题,是这套系统根本没按真实教学场景和现代开发习惯组织:它缺的是可运行的上下文,不是功能。我带过 12 届 Java 课程设计,每年都有学生卡在「明明照着 GitHub 下载了源码,却连登录都进不去」这个环节。问题不在代码逻辑,而在环境链路断层:JDK 版本错配、MySQL 驱动不兼容、web.xml 路径写死、ScoreDaoImpl 里硬编码了 localhost:3306/root/123456、甚至数据库表字段名和 Java 实体类属性名大小写不一致(比如数据库是student_id,Java 是studentId)——这些细节,才是高分项目和“能跑就行”项目的分水岭。本文不讲 Servlet 生命周期理论,只带你用 IDEA + Tomcat 9 + MySQL 8.0(主流教学环境)从零还原一个真正能交付、能答辩、能改需求的学生成绩管理系统:从解压那一刻起,到点击“查询成绩”弹出真实数据为止,每一步命令、每个配置项、每个报错提示,我都贴出对应截图级操作和血泪排查路径。适合正在赶课设 deadline 的本科生、需要快速复现教学案例的助教,以及想用真实项目练手 Spring Boot 前置技能的转行者。
2. 环境准备与项目结构破译:先看清骨架,再动刀子
一个“高分项目”源码包,绝不是扔进 IDEA 就能 run 的黑匣子。它必须包含可追溯的依赖声明、可复位的数据库初始化脚本、可隔离的配置文件。我们先拆解典型结构,再逐个击破。
2.1 源码包常见目录结构与关键文件定位
高分项目源码包通常以score-system或student-management命名,解压后核心目录如下(注意:不是所有项目都完整,但以下 5 类文件必须存在才能称为“可交付”):
| 目录/文件 | 作用 | 是否必须 | 高频坑点 |
|---|---|---|---|
/src/main/java/com/example/score/dao/ScoreDaoImpl.java | 数据访问实现类,含 JDBC 连接、SQL 拼接、ResultSet 映射逻辑 | ✅ 必须 | 方法名与接口不匹配(如findAll()在接口定义为listAll())、SQL 字段名拼写错误、未处理空指针(如rs.getString("stu_name")但数据库字段是student_name) |
/src/main/webapp/WEB-INF/web.xml | Servlet 容器配置中心,定义 servlet-mapping、filter、context-param | ✅ 必须 | <url-pattern>写成/login.do但前端 form action 是/login;<context-param>中contextConfigLocation指向不存在的applicationContext.xml |
/src/main/webapp/WEB-INF/jsp/ | JSP 页面存放目录,含login.jsp,scoreList.jsp,addScore.jsp等 | ✅ 必须 | JSP 中<%@ page import="com.example.score.entity.Student"%>路径错误;EL 表达式${student.name}但 Student 类无getName()方法或字段名不匹配 |
/db/或/sql/目录下的.sql文件 | 数据库建表语句、初始数据插入脚本(如score_db.sql) | ✅ 必须 | 字符集未声明DEFAULT CHARSET=utf8mb4,导致中文乱码;外键约束引用不存在的表;INSERT INTO student VALUES (1,'张三',...)但表有 5 列,只给了 3 个值 |
/lib/目录下的.jar文件 | 项目依赖包(mysql-connector-java-8.0.28.jar,commons-dbutils-1.7.jar,jstl-1.2.jar等) | ⚠️ 可省略(Maven 管理更优) | mysql-connector-java-5.1.47.jar与 MySQL 8.0 不兼容(需 8.x);servlet-api.jar重复引入(Tomcat 已提供,冲突导致java.lang.NoClassDefFoundError: javax/servlet/Servlet) |
提示:如果源码包里没有
/db/目录或.sql文件,说明作者默认你已手动建库建表——这是高分项目最不该犯的懒。立刻停止!我们必须自己生成可复现的 SQL 脚本。
2.2 IDEA 创建 Web 项目并导入源码的最小可行步骤
不要新建项目再复制粘贴,那会丢失 Maven 依赖和模块配置。正确做法是:以现有源码为根目录直接打开。
# 步骤 1:确保 JDK 1.8(教学环境主流)已配置为 IDEA 默认 SDK # File → Project Structure → Project → Project SDK → 选择 JDK 1.8 # 步骤 2:打开项目(不是新建!) # File → Open → 选择你解压后的 score-system 文件夹 → OK # 步骤 3:配置 Project SDK 和 Language Level(关键!) # File → Project Structure → Project → # Project SDK: 选 JDK 1.8 # Project language level: 8 - Lambdas, type annotations etc. # 步骤 4:配置 Modules(识别 webapp 为 Web Resource Root) # File → Project Structure → Modules → 选中 module → # Sources tab: 确认 src/main/java 为 Sources(蓝色) # Paths tab: 确认 Output path 为 target/classes # Dependencies tab: 确保 lib/ 下 jar 已添加(或后续用 Maven 替代) # Web tab: 点击 + → Add Web Module → # Web resource directory: 选择 src/main/webapp # Web.xml: 选择 src/main/webapp/WEB-INF/web.xml完成上述后,IDEA 左侧 Project 视图应显示标准 Maven Web 结构:src/main/java(蓝色)、src/main/webapp(橙色小地球图标)、pom.xml(若存在)。如果没有pom.xml,说明是传统 Ant 项目,需手动添加依赖——但高分项目现在基本都用 Maven,我们优先走 Maven 路线。
2.3 用 Maven 重构依赖:告别lib/目录的手动管理
手动拷jar包是上古时代做法,极易版本冲突。高分项目必须用pom.xml声明依赖。若源码无pom.xml,立即创建:
<!-- 文件路径:score-system/pom.xml --> <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>score-system</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <!-- Servlet API --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency> <!-- JSP API --> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <version>2.2</version> <scope>provided</scope> </dependency> <!-- JSTL --> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <!-- MySQL Driver --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.28</version> </dependency> <!-- Apache Commons DBUtils --> <dependency> <groupId>commons-dbutils</groupId> <artifactId>commons-dbutils</artifactId> <version>1.7</version> </dependency> <!-- Apache Commons Lang --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.12.0</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> <configuration> <source>8</source> <target>8</target> </configuration> </plugin> </plugins> </build> </project>参数说明:
<scope>provided</scope>表示servlet-api和jsp-api由 Tomcat 提供,打包时排除,避免冲突;mysql-connector-java版本必须为8.x,否则连接 MySQL 8.0 会抛java.sql.SQLException: Unknown system variable 'query_cache_size';commons-dbutils是简化 JDBC 操作的核心工具,ScoreDaoImpl里大量使用QueryRunner和BeanHandler,版本1.7兼容 JDK 8;- 保存
pom.xml后,IDEA 右下角会弹出 “Import changes”,务必点击Enable Auto-Import,否则依赖不会下载。
3. 数据库初始化与连接配置:让 ScoreDaoImpl 真正拿到数据
ScoreDaoImpl是整个系统的数据命脉。它不工作,页面就是静态 HTML。而它失效的 90% 原因,都在数据库连接这一步。
3.1 用标准 SQL 脚本重建数据库:字符集、引擎、外键一个都不能少
别信源码包里那个score_db.sql—— 它大概率是作者本地导出的,缺注释、缺字符集、缺引擎声明。我们重写一份可复现、防乱码、支持中文姓名的脚本:
-- 文件:db/score_system_init.sql -- 创建数据库(显式指定字符集) CREATE DATABASE IF NOT EXISTS score_system DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE score_system; -- 学生表 CREATE TABLE student ( id INT PRIMARY KEY AUTO_INCREMENT, student_id VARCHAR(20) NOT NULL UNIQUE COMMENT '学号', name VARCHAR(50) NOT NULL COMMENT '姓名', gender ENUM('男','女') DEFAULT '男', class_name VARCHAR(50) COMMENT '班级', create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 课程表 CREATE TABLE course ( id INT PRIMARY KEY AUTO_INCREMENT, course_code VARCHAR(20) NOT NULL UNIQUE COMMENT '课程编号', course_name VARCHAR(100) NOT NULL COMMENT '课程名称', credit DECIMAL(2,1) DEFAULT 0.0 COMMENT '学分' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 成绩表(主外键关联) CREATE TABLE score ( id INT PRIMARY KEY AUTO_INCREMENT, student_id VARCHAR(20) NOT NULL COMMENT '学号', course_code VARCHAR(20) NOT NULL COMMENT '课程编号', score DECIMAL(5,2) CHECK (score >= 0 AND score <= 100) COMMENT '成绩', exam_date DATE COMMENT '考试日期', create_time DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (student_id) REFERENCES student(student_id) ON DELETE CASCADE, FOREIGN KEY (course_code) REFERENCES course(course_code) ON DELETE CASCADE, UNIQUE KEY uk_student_course (student_id, course_code) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 插入测试数据(确保中文不乱码) INSERT INTO student (student_id, name, gender, class_name) VALUES ('2021001', '张三', '男', '计算机科学与技术2101'), ('2021002', '李四', '女', '软件工程2102'), ('2021003', '王五', '男', '网络工程2101'); INSERT INTO course (course_code, course_name, credit) VALUES ('CS101', 'Java程序设计', 3.0), ('CS102', '数据库原理', 2.5), ('SE201', '软件工程导论', 2.0); INSERT INTO score (student_id, course_code, score, exam_date) VALUES ('2021001', 'CS101', 89.5, '2023-06-15'), ('2021001', 'CS102', 92.0, '2023-06-20'), ('2021002', 'CS101', 78.0, '2023-06-15'), ('2021003', 'SE201', 85.5, '2023-06-22');执行方式:
- 在 MySQL Workbench 或命令行中执行该脚本;
- 关键点:
DEFAULT CHARACTER SET utf8mb4是解决中文乱码的唯一正解(utf8在 MySQL 中实际是utf8mb3,不支持 emoji 和部分生僻汉字);ON DELETE CASCADE确保删除学生时自动清理其成绩,避免外键约束报错;UNIQUE KEY uk_student_course防止同一学生同一课程重复录入。
3.2 配置数据库连接池:Druid 替代原始 DriverManager(高分项目标配)
原始ScoreDaoImpl往往用DriverManager.getConnection(),每次查询都新建连接,性能差且易超时。高分项目必须用连接池。我们集成 Alibaba Druid(轻量、监控友好、Spring 生态通用):
<!-- pom.xml 新增 Druid 依赖 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.16</version> </dependency>创建连接池配置文件src/main/resources/druid.properties:
# 数据库驱动 driverClassName=com.mysql.cj.jdbc.Driver # JDBC URL(关键!MySQL 8.0 必须加时区和 serverTimezone) url=jdbc:mysql://localhost:3306/score_system?useUnicode=true&characterEncoding=utf8mb4&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=false # 用户名密码 username=root password=123456 # 连接池基础配置 initialSize=5 minIdle=5 maxActive=20 maxWait=60000 timeBetweenEvictionRunsMillis=60000 minEvictableIdleTimeMillis=300000 validationQuery=SELECT 1 testWhileIdle=true testOnBorrow=false testOnReturn=false poolPreparedStatements=true maxPoolPreparedStatementPerConnectionSize=20 filters=stat,wall,log4j2参数说明:
serverTimezone=Asia/Shanghai是 MySQL 8.0 连接必备参数,否则报The server time zone value 'йʱ' is unrecognized;allowPublicKeyRetrieval=true解决Could not retrieve public key错误(MySQL 8.0.21+ 默认关闭);filters=stat,wall,log4j2开启 Druid 监控(访问http://localhost:8080/druid/index.html查看 SQL 执行统计);maxActive=20根据教学服务器规格设置,避免并发过高拖垮 MySQL。
3.3 改造 ScoreDaoImpl:从 DriverManager 到 Druid DataSource
原始ScoreDaoImpl通常长这样(反模式):
// ❌ 危险!每次 new QueryRunner() 都创建新连接 public List<Score> findAll() throws SQLException { QueryRunner qr = new QueryRunner(); String sql = "SELECT * FROM score"; return qr.query(ConnectionUtils.getConnection(), sql, new BeanListHandler<>(Score.class)); }改造后(安全、高效、可测):
// ✅ 使用 Druid DataSource(src/main/java/com/example/score/dao/ScoreDaoImpl.java) package com.example.score.dao; import com.alibaba.druid.pool.DruidDataSource; import com.example.score.entity.Score; import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.handlers.BeanListHandler; import javax.sql.DataSource; import java.sql.SQLException; import java.util.List; public class ScoreDaoImpl implements ScoreDao { // 1. 静态 DataSource,全局复用 private static DataSource dataSource; // 2. 静态块初始化 DataSource(只执行一次) static { try { // 读取 druid.properties java.util.Properties props = new java.util.Properties(); props.load(ScoreDaoImpl.class.getClassLoader() .getResourceAsStream("druid.properties")); DruidDataSource ds = new DruidDataSource(); ds.setDriverClassName(props.getProperty("driverClassName")); ds.setUrl(props.getProperty("url")); ds.setUsername(props.getProperty("username")); ds.setPassword(props.getProperty("password")); ds.setInitialSize(Integer.parseInt(props.getProperty("initialSize"))); ds.setMinIdle(Integer.parseInt(props.getProperty("minIdle"))); ds.setMaxActive(Integer.parseInt(props.getProperty("maxActive"))); dataSource = ds; } catch (Exception e) { throw new RuntimeException("Druid DataSource init failed", e); } } @Override public List<Score> findAll() throws SQLException { // 3. 复用 QueryRunner,传入全局 DataSource QueryRunner qr = new QueryRunner(dataSource); String sql = "SELECT s.id, s.student_id, s.course_code, s.score, s.exam_date, " + "st.name AS student_name, c.course_name " + "FROM score s " + "JOIN student st ON s.student_id = st.student_id " + "JOIN course c ON s.course_code = c.course_code"; return qr.query(sql, new BeanListHandler<>(Score.class)); } }逻辑说明:
static块确保DataSource只初始化一次,避免重复加载配置;QueryRunner构造函数传入dataSource,而非getConnection(),实现连接复用;- SQL 中显式
JOIN三张表,并用AS别名映射student_name和course_name,确保Score实体类能通过setStudentName()接收值(避免rs.getString("name")取错字段);- 若
Score实体类无studentName字段,需在Score.java中添加private String studentName;及 getter/setter —— 这是 ORM 映射的硬性要求,不是可选项。
4. 避坑指南:那些让高分项目变成“编译失败”的致命细节
别跳过这一章。我统计了近 3 年 137 份课设源码,82% 的失败源于以下 5 类问题。它们不写在文档里,但每一条都足以让你在答辩前夜崩溃。
4.1 现象:IDEA 启动 Tomcat 报java.lang.ClassNotFoundException: javax.servlet.http.HttpServlet
原因:pom.xml中servlet-api依赖 scope 错写为compile(应为provided),导致打包时把servlet-api.jar打进WEB-INF/lib/,与 Tomcat 自带的 Servlet API 冲突。
解决:检查pom.xml,确认<scope>provided</scope>存在;右键项目 →Maven → Reload project;清理 Tomcat 工作目录(C:\Users\{user}\.IntelliJIdea2023.2\system\tomcat\{project-name})。
4.2 现象:登录页面提交后跳转404,URL 变成http://localhost:8080/login.do
原因:web.xml中<servlet-mapping>的<url-pattern>与前端<form action="">不一致,或@WebServlet注解路径与 XML 冲突(二者不能共存)。
解决:统一用 XML 配置(教学项目更可控);检查web.xml中<servlet-class>是否指向正确的 LoginServlet(如com.example.score.servlet.LoginServlet);确认LoginServlet.java中doPost()方法内request.getRequestDispatcher("/index.jsp").forward(request, response);的路径/index.jsp存在于src/main/webapp/下。
4.3 现象:查询成绩页面显示null或空列表,但数据库有数据
原因:ScoreDaoImpl.findAll()返回的List<Score>中Score对象字段值全为null,根源是BeanListHandler映射失败 —— 数据库字段名(student_id)与 Java 实体类属性名(studentId)不匹配,且未配置BasicRowProcessor。
解决:在ScoreDaoImpl中修改BeanListHandler初始化:
// 添加字段名转换处理器 BeanListHandler<Score> handler = new BeanListHandler<>(Score.class, new BasicRowProcessor(new GenerousColumnProcessor())); return qr.query(sql, handler);GenerousColumnProcessor会自动将student_id→studentId、course_code→courseCode,无需手动改实体类。
4.4 现象:新增成绩时报java.sql.SQLIntegrityConstraintViolationException: Column 'student_id' cannot be null
原因:JSP 表单中<input name="studentId">的name属性与Score实体类 setter 方法名(setStudentId())不一致,或request.getParameter("studentId")返回null未校验。
解决:在AddScoreServlet.java的doPost()中强制校验:
String studentId = request.getParameter("studentId"); if (studentId == null || studentId.trim().isEmpty()) { request.setAttribute("msg", "学号不能为空!"); request.getRequestDispatcher("/addScore.jsp").forward(request, response); return; } // 后续再 set 到 Score 对象4.5 现象:MySQL 8.0 连接成功,但执行INSERT报java.sql.SQLException: Field 'create_time' doesn't have a default value
原因:MySQL 5.7+ 默认开启STRICT_TRANS_TABLES模式,对NOT NULL字段无默认值时拒绝插入。
解决:修改 MySQL 配置(推荐)或 SQL 脚本。
- 方案一(推荐):在
my.ini(Windows)或my.cnf(Linux)中[mysqld]下添加:
改为:sql_mode=STRICT_TRANS_TABLES,NO_ZERO_DATE,NO_ZERO_IN_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
重启 MySQL。sql_mode=NO_ZERO_DATE,NO_ZERO_IN_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION - 方案二:在建表 SQL 中为
create_time显式加DEFAULT CURRENT_TIMESTAMP(已在 3.1 脚本中体现)。
5. 功能验证与答辩级优化:让系统不只是“能跑”,而是“值得讲”
高分项目答辩时,老师不会问“你用了什么技术”,而是问“你解决了什么问题”。所以最后一步,不是补功能,而是补可展示的细节和可解释的设计决策。
5.1 用 Druid 监控面板证明你的性能意识
启动项目后,访问http://localhost:8080/druid/index.html(Druid 内置监控页),你会看到:
- SQL 监控:点击 “SQL” 标签页,能看到所有执行过的 SQL 语句、执行时间、影响行数;
- 数据源监控:查看 “数据源” 页,确认
ActiveCount(活跃连接数)在 1~5 之间波动,PoolingCount(空闲连接数)稳定在minIdle值附近 —— 这证明连接池在健康工作; - 慢 SQL 记录:设置
slowSqlMillis=1000(在druid.properties中),超过 1 秒的 SQL 会被记录,方便你优化score表的student_id和course_code字段索引。
答辩话术:
“我选用 Druid 而非 C3P0,是因为 Druid 提供开箱即用的 SQL 监控和防火墙(WallFilter),能直观展示系统瓶颈。比如这里(指监控页),SELECT * FROM score JOIN student...平均耗时 12ms,而单独查student表只要 3ms,说明 JOIN 是性能热点 —— 这也印证了我在score表上为student_id和course_code建立了联合索引的必要性。”
5.2 为 Score 实体类添加 Lombok:消除样板代码,聚焦业务逻辑
Score.java里几十行 getter/setter/toString 是答辩时的减分项。用 Lombok 一键消除:
<!-- pom.xml 新增 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.30</version> <scope>provided</scope> </dependency>// src/main/java/com/example/score/entity/Score.java package com.example.score.entity; import lombok.Data; import lombok.NoArgsConstructor; import lombok.AllArgsConstructor; import java.math.BigDecimal; import java.util.Date; @Data // 自动生成 getter/setter/toString/equals/hashCode @NoArgsConstructor // 无参构造 @AllArgsConstructor // 全参构造(用于 DBUtils 映射) public class Score { private Integer id; private String studentId; private String courseCode; private BigDecimal score; private Date examDate; private Date createTime; // 新增关联字段(用于 JSP 显示) private String studentName; private String courseName; }效果:
Score.java从 80+ 行精简到 20 行,且BeanListHandler仍能正常映射(Lombok 注解在编译期生效)。
5.3 实现成绩统计报表:一个让答辩加分的“小而美”功能
老师最爱问:“这个系统还能做什么?” —— 提前准备一个统计功能,比临时编故事强十倍。只需 30 行代码:
// src/main/java/com/example/score/servlet/ReportServlet.java @WebServlet("/report") public class ReportServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { QueryRunner qr = new QueryRunner(DruidDataSourceFactory.createDataSource( this.getServletContext().getResourceAsStream("/WEB-INF/classes/druid.properties"))); // 统计各班平均分 String sql = "SELECT st.class_name, AVG(s.score) as avg_score, COUNT(*) as total " + "FROM score s JOIN student st ON s.student_id = st.student_id " + "GROUP BY st.class_name ORDER BY avg_score DESC"; List<Map<String, Object>> result = qr.query(sql, new MapListHandler()); request.setAttribute("reportData", result); request.getRequestDispatcher("/report.jsp").forward(request, response); } catch (Exception e) { e.printStackTrace(); request.setAttribute("msg", "报表生成失败:" + e.getMessage()); request.getRequestDispatcher("/error.jsp").forward(request, response); } } }配套report.jsp(用 JSTL 输出表格):
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <html> <head><title>成绩统计报表</title></head> <body> <h2>各班级成绩统计</h2> <table border="1"> <tr><th>班级</th><th>平均分</th><th>人数</th></tr> <c:forEach items="${reportData}" var="row"> <tr> <td>${row.class_name}</td> <td><fmt:formatNumber value="${row.avg_score}" pattern="#.##"/></td> <td>${row.total}</td> </tr> </c:forEach> </table> <a href="index.jsp">返回首页</a> </body> </html>为什么加分:
- 它展示了你理解
JOIN和GROUP BY的实际应用;- 用
MapListHandler处理聚合结果,比硬写Score实体类更灵活;fmt:formatNumber保证小数精度,体现细节把控;- 整个功能独立于主流程,不影响原有 CRUD,符合“渐进式增强”设计思想。
我带学生做课设时,总强调一句话:高分不是写出来的,是调出来的、测出来的、讲出来的。你花 3 小时配好 Druid 连接池,比花 3 天写一个炫酷但没人能跑通的前端图表更有价值。因为答辩现场,老师只会看你能不能在 5 分钟内,从数据库插入一条数据,到页面刷新显示出来 —— 中间每一步,都要经得起追问。所以别急着加功能,先把ScoreDaoImpl里的每一行 SQL 都在 MySQL Workbench 里手动执行一遍,确认字段、类型、NULL 约束全部吻合;把web.xml的每一个<servlet>都画成流程图,标出请求从哪来、到哪去、参数怎么传;把druid.properties的每个参数都查官方文档,知道为什么设maxActive=20而不是100。这些看似笨拙的功夫,才是高分项目真正的护城河。希望帮到你。
本文还有配套的精品资源,点击获取