Java课程设计学生成绩管理系统:5张表设计与JDBC实现要点
发布时间:2026/9/18 15:35:40来源:尧图网络
简介这是一份面向高校计算机专业学生的《Java学生成绩管理系统》课程设计报告适用于软件工程、计算机科学等专业的课程设计参考。报告以学生成绩管理为业务场景完整覆盖学生信息管理、课程成绩维护、按学号/姓名查询、分数段统计、报表输出、用户权限管理等功能模块并涉及C/S模式架构、SQL Server 2000数据库设计与Java/VB混合开发等内容。资源包内共1个doc文档整体大小205KB文件为完整的课程设计报告文本包含任务书、进度安排、中英文摘要、需求分析、概要设计、详细设计及成绩评定表等栏目结构清晰、格式规范可直接作为撰写课程设计报告的结构模板和内容参考。该资源已有786人学习下载适合需要快速了解学生成绩管理系统设计流程、报告撰写规范或答辩准备的学生使用。1. 这个课设每年都有人挂挂在哪学生成绩管理系统是 Java 课程设计里出现频率最高的题目但它恰恰是每年答辩翻车最多的题目之一。多数人的失败不在功能上而在需求边界不清、数据库表设计不合理、代码和文档对不上这三件事上。比如把“学生管理”做成一个包含密码找回、头像上传、短信通知的大杂烩再比如成绩表里不存学期字段导致同一个学生同一门课出现多条记录。写全套 Java 增删改查并不难难的是让数据模型经得起追问某学生某学期某门课为什么只能有一条成绩记录排名为什么不用自写的冒泡排序这篇讲一条适合课程设计交付的路线需求分析、5 张表、JDBC 连接池、核心 CRUD 与统计 SQL以及答辩时最容易被问到的几个边界问题。2. 成绩系统的需求分析用例、ER 图、5 张表定边界2.1 先把功能边界画出来管理员、学生、老师各自能做什么课程设计最忌讳功能越做越多。合理的做法是先列角色再给每个角色列 35 个用例画出用例图之后冻结需求。常见角色划分如下。角色核心用例说明管理员维护学生、教师、班级、课程信息重置账号密码不做角色细粒度授权一个 admin 账号即可教师录入成绩、修改成绩、按课程统计平均分与及格率不做批量导入 Excel用页面录入即可学生查询本人全部课程成绩、查看自己的单科排名与平均分只能查自己不能查别人这里涉及鉴权用例图用 ProcessOn、StarUML 或 draw.io 画都可以导出 PNG 直接贴到课程设计报告里。要注意的是报告的图评委会对照代码检查用例图上写了“成绩批量导入”代码里却没有对应入口这会成为答辩减分项。因此不打算做的功能一开始就不要画进用例图。2.2 数据库设计为什么是这 5 张表而不是 1 张表很多初学者只建两张表student 和 score班级、课程信息全用字符串塞进去。这在功能演示上没问题但被问到“怎么统计某教师教的课程平均分”时就会卡住。常见的 5 张表设计是user登录账号、student学生信息、teacher教师信息、course课程信息、score成绩记录。关键点在于score表要把学期作为业务主键的一部分。成绩本质上是“学生 课程 学期”三维度确定唯一记录如果表里没有学期字段补考记录就无法表示——这个点能现场解释清楚说明你确实理解关系建模。user表与student、teacher各自通过user_id关联避免学生表和教师表里重复维护密码字段。2.3 建表 SQL 与约束取舍下面是用 MySQL 8.0 建表的参考 SQL字符集统一 utf8mb4排序规则 utf8mb4_unicode_ci。CREATE DATABASE IF NOT EXISTS grade_system DEFAULT CHARACTER SET utf8mb4; CREATE TABLE user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL, role ENUM(ADMIN, TEACHER, STUDENT) NOT NULL ) ENGINEInnoDB; CREATE TABLE student ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL UNIQUE, student_no VARCHAR(20) NOT NULL UNIQUE, name VARCHAR(50) NOT NULL, gender ENUM(M, F) DEFAULT M, class_name VARCHAR(50) NOT NULL, CONSTRAINT fk_student_user FOREIGN KEY (user_id) REFERENCES user(id) ) ENGINEInnoDB; CREATE TABLE teacher ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL UNIQUE, teacher_no VARCHAR(20) NOT NULL UNIQUE, name VARCHAR(50) NOT NULL, title VARCHAR(20) ) ENGINEInnoDB; CREATE TABLE course ( id INT PRIMARY KEY AUTO_INCREMENT, course_no VARCHAR(20) NOT NULL UNIQUE, name VARCHAR(50) NOT NULL, teacher_id INT NOT NULL, credit DECIMAL(3,1) DEFAULT 2.0, CONSTRAINT fk_course_teacher FOREIGN KEY (teacher_id) REFERENCES teacher(id) ) ENGINEInnoDB; CREATE TABLE score ( id INT PRIMARY KEY AUTO_INCREMENT, student_id INT NOT NULL, course_id INT NOT NULL, semester VARCHAR(20) NOT NULL, score DECIMAL(5,2) NOT NULL, CHECK (score 0 AND score 100), CONSTRAINT uk_score_one UNIQUE (student_id, course_id, semester), CONSTRAINT fk_score_student FOREIGN KEY (student_id) REFERENCES student(id), CONSTRAINT fk_score_course FOREIGN KEY (course_id) REFERENCES course(id) ) ENGINEInnoDB;这段 SQL 里值得在报告里写清楚的是uk_score_one这个唯一约束它保证同一个学生、同一门课、同一个学期只能有一条成绩记录。代码层面的拦截是最后一道防线数据库约束才是兜底。外键在课程设计项目中建议保留虽然会影响一点插入性能但能体现参照完整性意识。3. 技术选型与工程结构JDBC 还是 MyBatis目录怎么摆3.1 三条路线怎么选JDBC控制台、JSPServlet、Spring Boot课程设计的技术栈由课程要求决定但可以从三档里选。技术路线适用场景实现成本答辩风险纯 JDBC 控制台 / Swing课程只要求 Java SE最低界面简陋容易被追问“成绩单打印格式”Servlet JSP JDBC课程要求 Java Web中等JSP 已老但报告好写Spring Boot MyBatis课程允许框架较高老师追问框架原理时需要能答上我一般建议课程设计优先考虑 Servlet JSP JDBC 路线。原因有两个一是它把请求处理、数据库访问、页面渲染三层都暴露出来答辩时能讲的东西多二是如果课程不允许用框架这条路不会撞红线三是后面学 Spring Boot 时能理解框架到底封装了什么。你如果已经熟练 Spring Boot直接用它也行但报告里至少要写一段“为什么不用 JDBC”的比较分析。3.2 分层目录结构与依赖清单无论哪条路线包结构都可以统一。下面是一个 Maven 风格的 Java Web 工程目录包名可以换成你自己的反域名。src/main/java/com/school/grade/ ├── entity/ -- User, Student, Teacher, Course, Score ├── dao/ -- UserDao, ScoreDao, CourseDao ├── service/ -- ScoreService, LoginService ├── servlet/ -- LoginServlet, ScoreAddServlet, ScoreQueryServlet ├── util/ -- DBUtil, Md5Util src/main/resources/ ├── db.properties -- 数据库连接配置 └── hikari.properties src/main/webapp/ ├── WEB-INF/ ├── css/ js/ └── login.jsp index.jsp这个结构对应“实体类、数据访问、业务逻辑、控制层”四层。课程设计报告里画一张这样的包图胜过写一段“系统采用分层设计”的套话。实体类只是属性加 getter/setterDAO 层只写 SQLService 层处理事务和业务判断Servlet 只做参数接收和页面跳转。3.3 用 HikariCP 替换 DriverManager 的理由与配置课程设计默认教的是DriverManager.getConnection()但它有个硬伤每次请求都新建物理连接数据显示不了等到答辩现场开多个页面就会卡。更严重的是很多人的代码里连接不关闭导致 MySQL 报Too many connections。常见做法是在课程设计里引入 HikariCP 连接池代码量增加很少但性能和稳定性都上来了。# src/main/resources/hikari.properties jdbcUrljdbc:mysql://localhost:3306/grade_system?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai usernameroot password你的密码 driverClassNamecom.mysql.cj.jdbc.Driver maximumPoolSize10 minimumIdle2 connectionTimeout30000package com.school.grade.util; import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; import javax.sql.DataSource; import java.io.InputStream; import java.sql.Connection; import java.sql.SQLException; import java.util.Properties; public class DBUtil { private static final HikariDataSource dataSource; static { try (InputStream in DBUtil.class.getClassLoader() .getResourceAsStream(hikari.properties)) { Properties props new Properties(); props.load(in); HikariConfig config new HikariConfig(props); dataSource new HikariDataSource(config); } catch (Exception e) { throw new ExceptionInInitializerError(e); } } public static Connection getConnection() throws SQLException { return dataSource.getConnection(); } public static DataSource getDataSource() { return dataSource; } }代码的逻辑是类加载时通过HikariConfig(props)读取配置并初始化连接池之后所有 DAO 都从getConnection()拿连接。maximumPoolSize10对课设足够minimumIdle2避免空闲时占用数据库资源。这里不用DriverManager的直接收益是即使你只在 DAO 里写了Class.forName前的加载逻辑连接复用也会让整个系统稳定很多。4. 核心代码登录、成绩录入与统计的 JDBC 实现4.1 登录实现PreparedStatement 防注入与密码处理登录是整套系统的入口也是评委最爱看的代码之一。最容易弄出问题的就是用了字符串拼接 SQL以及密码明文存放。下面是登录校验的 DAO 写法。public User login(String username, String password) throws SQLException { String sql SELECT id, username, role FROM user WHERE username ? AND password ?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, username); ps.setString(2, Md5Util.md5(password)); try (ResultSet rs ps.executeQuery()) { if (rs.next()) { User user new User(); user.setId(rs.getInt(id)); user.setUsername(rs.getString(username)); user.setRole(rs.getString(role)); return user; } } } return null; }这段代码有两个必须能在答辩时说清楚的点。第一PreparedStatement用?占位由 JDBC 驱动处理特殊字符转义从机制上堵住 SQL 注入不要写WHERE username username 。第二密码不存明文Md5Util.md5()对输入做摘要后和库里比对即使数据库被脱库拿到的也不是原始明文。课程设计做到 MD5 加盐就足够了。4.2 成绩批量录入addBatch 与事务边界教师按班级录入一门课的成绩时通常会一次提交几十条记录这时一条条 executeUpdate 性能不好而且如果第 20 条失败前面 19 条已经写进库里数据就脏了。正确的做法是用批处理加事务。public void batchAddScores(ListScore scores) throws SQLException { String sql INSERT INTO score (student_id, course_id, semester, score) VALUES (?, ?, ?, ?); Connection conn null; try { conn DBUtil.getConnection(); conn.setAutoCommit(false); try (PreparedStatement ps conn.prepareStatement(sql)) { for (Score s : scores) { ps.setInt(1, s.getStudentId()); ps.setInt(2, s.getCourseId()); ps.setString(3, s.getSemester()); ps.setBigDecimal(4, s.getScore()); ps.addBatch(); } ps.executeBatch(); } conn.commit(); } catch (SQLException e) { if (conn ! null) { conn.rollback(); } throw e; } finally { if (conn ! null) { conn.setAutoCommit(true); conn.close(); } } }关键点是setAutoCommit(false)放在获取连接之后executeBatch()执行完统一commit()一旦出错则rollback()回滚整批。注意finally里要把自动提交恢复为 true否则连接归还连接池后状态被污染下一个使用者会掉进隐式事务里。这个坑调试起来比较隐蔽值得在报告的测试部分单独写上一条“批量录入失败时数据未残留”。4.3 统计查询SQL 聚合与班级排名的两种写法成绩统计是课程设计的重头戏很多人的做法是把所有成绩查到 List 里再用 Java 循环算平均分和排名。数据量小看不出问题但被问到“几千个学生时怎么办”就会卡住。合理的做法是让数据库完成聚合计算。-- 某门课程各班级平均分与及格率 SELECT s.class_name, ROUND(AVG(sc.score), 2) AS avg_score, ROUND(SUM(CASE WHEN sc.score 60 THEN 1 ELSE 0 END) / COUNT(*) * 100, 2) AS pass_rate FROM score sc JOIN student s ON sc.student_id s.id WHERE sc.course_id ? GROUP BY s.class_name ORDER BY avg_score DESC;CASE WHEN在 MySQL 里可以用于条件计数这里统计及格人数再除以总数就是及格率。另一个高频需求是“学生想看自己这门课排第几”MySQL 8.0 可用ROW_NUMBER() OVER但如果课设环境是 MySQL 5.7用下面这种自连接计数法更稳妥。SELECT s.name, sc.score, 1 (SELECT COUNT(*) FROM score sc2 WHERE sc2.course_id sc.course_id AND sc2.semester sc.semester AND sc2.score sc.score) AS rank_no FROM score sc JOIN student s ON sc.student_id s.id WHERE sc.course_id ? AND sc.semester ? AND s.class_name ? ORDER BY rank_no ASC;子查询的含义是“统计同课程同班级里比我分数高的人数再加 1 就是我的名次”。分数相同时会都排到同一个名次行为与体育比赛并列排名一致。写这个 SQL 并能解释清楚子查询的执行顺序比背一段堆排序代码更能赢得答辩好感。5. 运行排错驱动、连接串、乱码与环境变量的 5 个高频坑5.1 五个高频异常一表排查课程设计答辩前夜的报错有很强的共性下面按出现频率排个表每条对应最常见的诱因和处理路径。报错信息高频原因处理方式ClassNotFoundException: com.mysql.cj.jdbc.Driver驱动 jar 没放进WEB-INF/lib或 classpath检查 jar 是否在发布后的 lib 目录里Access denied for user rootlocalhost密码错误或用户无远程权限核对 db.properties确认密码是否含特殊字符Unknown database grade_system建库脚本没执行用命令行先跑一遍建库 SQLPublic Key Retrieval is not allowedMySQL 8.0 默认 caching_sha2_password 认证连接串加allowPublicKeyRetrievaltrueToo many connections连接未关闭或连接池过大检查所有 DAO 是否用了 try-with-resources把最大连接数调小表里最后一条最容易被忽视。很多人只在finally里 close 了 ResultSet漏了 PreparedStatement 和 Connection。用 try-with-resources 最省心它保证三个资源按声明逆序关闭。5.2 连接串参数含义与乱码处理乱码问题几乎是 Java 课设必考。MySQL 连接串里的characterEncodingutf8只解决数据库读写编码如果页面还是乱要检查三层页面ContentType、Servlet 接收请求的编码、MySQL 连接串。在 Servlet 里最直接的做法是写一个编码过滤器对每个请求设置 UTF-8。WebFilter(/*) public class EncodingFilter implements Filter { Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { request.setCharacterEncoding(UTF-8); response.setCharacterEncoding(UTF-8); response.setContentType(text/html;charsetUTF-8); chain.doFilter(request, response); } }setCharacterEncoding(UTF-8)把 POST 请求体按 UTF-8 解码setContentType指定浏览器按 UTF-8 渲染页面。这里要说清楚的是GET 请求的查询参数编码不受这个过滤器控制中文参数要改 Tomcat 的server.xml连接器加URIEncodingUTF-8。如果你的系统只做了成绩录入用 POST 就够这个坑可以避开。5.3 环境变量java 与 javac 不一致时先查什么如果是在本机跑起来遇到java -version正常但javac报错找不到通常是 PATH 里有个旧的 JRE 路径在前。课程设计环境里常见的情况是装过 JDK 8 又装了 JDK 17多个版本并存。命令行里直接敲where java和where javac看解析顺序再把 JDK 的 bin 目录提到 PATH 最前面。需要在答辩前验证的事项java -version与javac -version主版本号一致JAVA_HOME指向 JDK 安装目录而不是 JRE 目录如果用的 Mavenmvn -version显示的是同一个 JDK。用 IDE 的普遍存在一个问题IDE 内置的 JRE 跑得挺好命令行的 Java 编译不了你的源码。这个状态本身没问题但要心里有数别在答辩演示时敲命令行命令被环境问题卡住。6. 从课设到简历成绩统计的 Stream 写法与答辩加分点6.1 把统计从 SQL 换成 Java Stream 的时机答辩评委常会问“如果数据量不大能不能用 Java 代码算统计”。这是一个展示 Java 8 特性的机会。把某学期全部成绩查出来后用 Stream 分组做汇总代码比循环累加清晰得多。MapInteger, DoubleSummaryStatistics stats scoreList.stream() .collect(Collectors.groupingBy( Score::getCourseId, Collectors.summarizingDouble(Score::getScore) )); stats.forEach((courseId, summary) - { System.out.println(课程 courseId 平均分: summary.getAverage() , 最高分: summary.getMax() , 最低分: summary.getMin() , 参考人数: summary.getCount()); });DoubleSummaryStatistics一次遍历收集计数、均值、最大值、最小值比手写四个累加变量少一半代码。但要注意它的边界如果你还要算及格率和班级维度排名Stream 代码会变成多层嵌套可读性反而下降。所以合理的写法是单课程统计用 SQL全量数据在内存里做多维汇总用 Stream两者在代码里都保留答辩时分别解释各自的适用场景。6.2 答辩必答的三类追问第一类问数据完整性uk_score_one唯一约束和CHECK (score 0 AND score 100)就是答案要能说出“应用层拦截只是第一道防线”。第二类问性能连接池的参数maximumPoolSize10的依据以及为什么排名用 SQL 子查询而不是把全表查到内存排序。第三类问安全登录时PreparedStatement防止注入的原理以及密码 MD5 摘要后的存储方式。把这三个问题想清楚课设答辩基本就稳了。如果你想把这段代码写进简历项目描述里建议写“使用 HikariCP 连接池管理连接通过唯一约束保证成绩数据一致性统计查询下沉到 SQL 聚合层”对应的这些都是这次课设里真实实现过的内容。本文还有配套的精品资源点击获取
网站建设高端定制企业官网