
简介这份PDF实验报告面向计算机相关专业学生与数据库课程学习者围绕MySQL学生成绩管理系统的完整设计流程展开可用于课程实验参考、项目设计借鉴或数据库课程复习。报告从项目背景与编写目的切入依次覆盖可行性分析、功能与性能需求、数据库需求分析及数据字典设计具体给出学生、教师、课程、班级、系等实体的字段类型、主外键与关联关系并附功能结构图与顶层数据流图帮助读者理清从需求到建表的完整思路。资源包内共1个PDF文件约486KB篇幅紧凑便于打印或对照实验步骤查阅。目前已有10437人学习下载适合需要完成数据库课程设计、撰写实验报告或梳理成绩管理系统建模逻辑的读者参考借鉴。1. 从一份实验报告到能跑的成绩管理系统这套 MySQL 设计到底交付了什么期末周最怕什么不是改卷子是教务催着要各班成绩汇总而你手里只有一堆 ExcelVLOOKUP 一拉就错行。这份《MySQL学生成绩管理系统设计实验报告》解决的正是这个场景它把“系—专业—班级—学生—课程—选课—成绩”这条链路用八张表串起来配上一套 Java Eclipse 的客户端和 MySQL 5.6 的库做成一个能登录、能录成绩、能按角色查数据的小系统。适合谁一是数据库课程设计要交作业的学生二是想拿一个“麻雀虽小五脏俱全”的 CRUD 项目练手 JDBC 和 SQL 的后端新手。它不炫技但表结构、视图、触发器、存储过程、权限、事务六件套齐全拿来当二次开发的骨架正合适。2. 技术栈与目录结构Eclipse JDBC MySQL 5.6 的最小闭环2.1 为什么是这套“老而稳”的组合报告里写得很直白Windows 10、MySQL 5.6.17、Eclipse 4.12.0、Navicat。这套组合放到今天看版本偏旧但对课程设计和教学项目来说反而是优点——MySQL 5.6 对存储过程、触发器、事件调度的支持已经完整语法又不像 8.x 那样默认改了认证插件caching_sha2_password会让老 JDBC 驱动直接连不上新手少踩一层坑。Eclipse 配 JDK 8 是当年教学标配java.sql包原生就能干活不需要 Spring 那一套。技术选型的逻辑可以拆成三层看数据层MySQL 负责所有持久化业务规则尽量下沉到数据库——视图做查询封装、触发器做级联删除、存储过程做成绩修改和学分统计、GRANT做角色权限。这是这份报告最有价值的部分它把“数据库不只是存数据”这件事讲透了。连接层JDBC 直连DriverManager.getConnection拿ConnectionPreparedStatement防注入ResultSet遍历结果。没有连接池对几十人并发的校园场景够用。表现层Swing 或控制台都行报告里给了登录、成绩查询、教师管理三个界面截图说明是带 GUI 的桌面程序。提示如果你本机装的是 MySQL 8.x跑这份代码前先把连接串加上?useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue否则大概率报时区或公钥检索错误。2.2 工程目录怎么摆报告没给完整源码树但按这个项目的模块划分一个能直接跑的工程目录常见做法是这样ScoreManager/ ├── src/ │ ├── com/sms/db/ # DBUtil驱动加载、连接获取、资源关闭 │ ├── com/sms/entity/ # Student、Teacher、Course、CV 等实体类 │ ├── com/sms/dao/ # StudentDao、ScoreDao、UserDao封装 SQL │ ├── com/sms/service/ # 业务逻辑登录校验、成绩统计 │ └── com/sms/ui/ # LoginFrame、TeacherFrame、StudentFrame ├── sql/ │ ├── 01_schema.sql # 建库建表 索引 │ ├── 02_view.sql # 三个视图 │ ├── 03_procedure.sql # 存储过程 │ ├── 04_trigger.sql # 级联删除触发器 │ └── 05_grant.sql # 用户与授权 └── lib/ └── mysql-connector-java-5.1.x.jar这个分层的意义在于dao层只认 SQLservice层只认业务ui层只认按钮事件。二次开发时改一个查询不用动界面换界面不用动 SQL。很多同学把 SQL 直接写在按钮的actionPerformed里第一版能跑第二版加个“按班级筛选”就全乱套了。2.3 建库建表八张表的关系骨架报告 4.5.1 给了建表语句但原稿里Index Student1(Sno)这种写法在 MySQL 里其实会报错——INDEX后面跟的是索引名括号里才是列名而且主键列再建普通索引是冗余的。我一般会改成下面这样顺手把外键补上CREATE DATABASE IF NOT EXISTS Stu DEFAULT CHARSET utf8mb4; USE Stu; CREATE TABLE Student ( Sno INT(5) NOT NULL, Sname CHAR(20) NOT NULL, Sex CHAR(2), Mno INT(5), Classno INT(5), PRIMARY KEY (Sno), KEY idx_student_class (Classno), CONSTRAINT fk_stu_major FOREIGN KEY (Mno) REFERENCES Major(Mno), CONSTRAINT fk_stu_class FOREIGN KEY (Classno) REFERENCES Class(Cnum) ) ENGINEInnoDB; CREATE TABLE CV ( Sno INT(5) NOT NULL, Cno INT(5) NOT NULL, Result CHAR(5), PRIMARY KEY (Sno, Cno), KEY idx_cv_cno (Cno), CONSTRAINT fk_cv_stu FOREIGN KEY (Sno) REFERENCES Student(Sno), CONSTRAINT fk_cv_course FOREIGN KEY (Cno) REFERENCES Course(Cno) ) ENGINEInnoDB;几个参数值得说清楚CV表用(Sno, Cno)联合主键天然保证一个学生一门课只有一条成绩记录比在应用层判重可靠得多Result用CHAR(5)是原报告的选择能存95、88.5甚至缺考但代价是不能直接SUM后面统计视图里sum(result)能跑通是因为 MySQL 会隐式转换遇到非数字就变 0——这是个隐患二次开发时建议改成DECIMAL(5,1)并单独加一个status字段。ENGINEInnoDB必须显式写因为只有它支持事务和外键MyISAM 下你的事务代码全是摆设。3. 数据库核心对象拆解视图、触发器、存储过程、权限四件套3.1 三个视图把常用查询固化下来报告 4.6.1 设计了三个视图思路很清晰把多表 JOIN 藏进视图前端只写SELECT * FROM 视图 WHERE ...。-- 成绩查询视图学生 选课前端按 cnum 或 sno 过滤 CREATE VIEW Rselect AS SELECT s.Classno AS cnum, cv.Sno, s.Sname, cv.Cno, cv.Result FROM Student s JOIN CV cv ON s.Sno cv.Sno; -- 学生所学课程及学分 CREATE VIEW Total AS SELECT cv.Sno, cv.Cno, c.Cname, c.Credit FROM Course c JOIN CV cv ON c.Cno cv.Cno; -- 学生总分与平均分 CREATE VIEW SumScore AS SELECT cv.Sno AS 学号, s.Sname AS 姓名, SUM(cv.Result) AS 总成绩, AVG(cv.Result) AS 平均成绩 FROM Student s JOIN CV cv ON s.Sno cv.Sno GROUP BY cv.Sno, s.Sname;逻辑说明Rselect是给教师端“按班级看成绩”用的cnum就是班级号Total解决“这个学生修了哪些课、多少学分”SumScore直接出总分平均分省得每次在 Java 里循环累加。参数上注意GROUP BY必须带上Sname否则在ONLY_FULL_GROUP_BY模式下MySQL 5.7 起默认开启会直接报错。原报告里视图名用了Rselect、sum这种sum是聚合函数关键字建视图时最好避开我上面改成了SumScore。3.2 触发器删课程时把选课记录一起带走报告里的触发器是整份文档里少见的“有业务思考”的片段——删除Course里的课程CV和ST里对应的记录要跟着删否则留下孤儿数据。DELIMITER $$ CREATE TRIGGER trg_course_delete AFTER DELETE ON Course FOR EACH ROW BEGIN DELETE FROM CV WHERE Cno OLD.Cno; DELETE FROM ST WHERE Cno OLD.Cno; END$$ DELIMITER ;逐段看AFTER DELETE表示主表删完再清理子表FOR EACH ROW是行级触发批量删课程时每行都会执行一次OLD.Cno取的是被删那一行的旧值这是触发器里唯一能拿到删除前数据的方式。原稿里DELIMITER %%和DELIMITER混用、结尾少了分号直接粘贴到 Navicat 会报语法错误上面这版是能跑的写法。注意如果CV表已经建了指向Course的外键且没写ON DELETE CASCADE这个触发器反而会和外键约束打架——删课程时外键先拦下来触发器根本没机会执行。二选一要么用触发器手动清要么建外键时写ON DELETE CASCADE别两个都上。3.3 存储过程改成绩和查学分报告 4.6.2 给了两个存储过程原稿被截断得厉害按意图补全后是这样DELIMITER $$ CREATE PROCEDURE Xiugai(IN p_sno INT, IN p_cno INT, IN p_result CHAR(5)) BEGIN UPDATE CV SET Result p_result WHERE Sno p_sno AND Cno p_cno; END$$ CREATE PROCEDURE Xuefen(IN p_sno INT) BEGIN SELECT cv.Sno, s.Sname, SUM(c.Credit) AS 总学分 FROM CV cv JOIN Course c ON cv.Cno c.Cno JOIN Student s ON cv.Sno s.Sno WHERE cv.Sno p_sno GROUP BY cv.Sno, s.Sname; END$$ DELIMITER ;Xiugai的三个入参对应“哪个学生、哪门课、改成多少”用IN模式只进不出Xuefen按学号聚合学分。这里有个细节Credit在报告里定义成CHAR(2)SUM时靠隐式转换如果哪天录进一个空字符串结果会静默变 0。稳妥做法是把Credit改成TINYINT或者在SUM(CAST(c.Credit AS UNSIGNED))里显式转换。3.4 权限设计三种角色三套 GRANT这是报告里最容易被忽略但最该学的部分。它建了user表存账号MD5 和 SHA1 双哈希再用 MySQL 原生用户做权限隔离-- 管理员全库全表 GRANT ALL ON Stu.* TO cu1localhost WITH GRANT OPTION; -- 学生只能查成绩 GRANT SELECT ON Stu.CV TO cu2localhost; -- 教师能查能改成绩 GRANT SELECT, UPDATE ON Stu.CV TO cu3localhost;参数说明WITH GRANT OPTION让cu1能把权限再授出去只给管理员学生只给SELECT连Student表都不给看防止爬全班信息教师给UPDATE但不给DELETE改错了还能救删了就没后悔药。这套设计比在 Java 里写if (role.equals(teacher))靠谱得多——应用层能被绕过数据库权限绕不过。提示user表里存MD5(110)这种写法在今天看太弱MD5 彩虹表一秒破。二次开发时换成SHA2(pwd, 256)加盐或者干脆交给应用层用 BCrypt别在 SQL 里硬编码明文密码。4. 事务与并发改成绩为什么要包一层 START TRANSACTION4.1 报告里的两个事务存储过程报告 4.6.3 给了BB改学分和BC改成绩两个过程核心结构一模一样开事务 → 查旧值 →DO SLEEP(20)→ 改 → 查新值 → 出错回滚、没出错提交。这个SLEEP(20)不是废话是故意留的 20 秒窗口方便你开两个 Navicat 窗口演示并发。DELIMITER $$ CREATE PROCEDURE BC(IN p_sno INT, IN p_cno INT, IN p_result CHAR(5)) BEGIN DECLARE t_err INT DEFAULT 0; DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET t_err 1; START TRANSACTION; SELECT SUM(Result) AS 改前总分, AVG(Result) AS 改前平均分 FROM CV WHERE Sno p_sno; SELECT Result AS 改前成绩 FROM CV WHERE Sno p_sno AND Cno p_cno; DO SLEEP(20); -- 演示窗口此时另一个会话能看到未提交前的旧值 UPDATE CV SET Result p_result WHERE Sno p_sno AND Cno p_cno; SELECT SUM(Result) AS 改后总分, AVG(Result) AS 改后平均分 FROM CV WHERE Sno p_sno; IF t_err 1 THEN ROLLBACK; ELSE COMMIT; END IF; END$$ DELIMITER ;逐段讲DECLARE CONTINUE HANDLER FOR SQLEXCEPTION是错误捕获钩子任何 SQL 异常都会把t_err置 1 但不中断过程这样后面的IF才有机会判断START TRANSACTION之后的所有写操作进同一个事务ROLLBACK撤销全部COMMIT落盘。参数p_sno、p_cno定位记录p_result是新成绩。4.2 这个设计真正想教的是什么SLEEP(20)的用意是让你在会话 A 执行BC的 20 秒里去会话 B 查CV表——你会发现 B 读到的还是旧成绩因为 A 没提交InnoDB 的默认隔离级别REPEATABLE READ下 B 读的是快照。等 A 提交后 B 再查才变。这就是“事务的原子性和隔离性”最直观的演示比背概念强一百倍。但这里有个血泪经验SLEEP期间如果 A 会话直接关掉 Navicat 窗口连接断开事务会被自动回滚你前面查的“改前值”全白看。演示时别手贱关窗口。4.3 并发改同一行会怎样如果两个教师同时对同一个学生的同一门课调BC第二个UPDATE会等第一个提交行锁拿到锁后用自己的p_result覆盖——后写的赢前一个的成绩丢了。报告没处理这个二次开发时可以在UPDATE前加乐观锁UPDATE CV SET Result p_result, version version 1 WHERE Sno p_sno AND Cno p_cno AND version p_old_version;受影响行数为 0 就说明被人改过提示“成绩已被他人修改请刷新”。这是从“能跑”到“敢用”的关键一步。5. 避坑与排查跑这份实验报告最容易翻车的五个地方5.1 连不上库Public Key Retrieval is not allowed现象Eclipse 里一运行就抛java.sql.SQLException: Public Key Retrieval is not allowed或者Unknown system variable query_cache_size。原因JDBC 驱动版本和 MySQL 服务端版本不匹配。5.1.x 驱动连 8.x 服务端会因认证插件和系统变量差异报错反过来 8.x 驱动连 5.6 也可能因serverTimezone缺失报时区错。解决驱动和服务端版本对齐。连 5.6 用mysql-connector-java-5.1.47连接串写jdbc:mysql://localhost:3306/Stu?useUnicodetruecharacterEncodingutf8连 8.x 用8.0.x驱动并补serverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue。5.2 建表语句整段报错现象把报告 4.5.1 的CREATE TABLE粘进 Navicat第一条就红。原因Index Student1(Sno)语法不对INDEX后应跟索引名再跟列另外表之间有外键依赖Student引用了Major和Class如果先建Student后建Major外键会失败。解决按Depart → Major → Class → Student → Teacher → Course → CV → ST的顺序建表索引写成KEY idx_name (col)主键列不用再建普通索引。5.3 触发器建了但不生效现象删了Course里一门课CV里对应记录还在。原因要么CV的外键带了ON DELETE RESTRICT默认删除被外键拦住触发器压根没触发要么触发器创建时DELIMITER没改BEGIN...END里的分号被客户端提前截断只建了个空壳。解决SHOW TRIGGERS;确认触发器存在且Statement完整外键和触发器二选一用触发器就把外键改成ON DELETE NO ACTION或干脆不建外键。5.4 存储过程里中文别名乱码现象SELECT SUM(Result) AS 总成绩在 Navicat 里显示正常Java 里rs.getString(总成绩)拿到???。原因连接串没指定字符集或者库/表/列的CHARSET不是utf8mb4。解决建库时DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_general_ci连接串加characterEncodingutf8Java 源文件编码设为 UTF-8。别名尽量用英文前端再映射成中文显示省得跨层传参时编码打架。5.5 学生账号能改成绩现象用cu2登录界面上“修改”按钮是灰的但直接调接口居然改成功了。原因只在前端做了按钮禁用后端没校验数据库层cu2只有SELECT权限——但如果 Java 程序统一用root连库权限隔离就形同虚设。解决每个角色用各自的数据库账号连库登录时按角色选连接配置后端每个写操作前再查一次角色。前端禁用是体验数据库GRANT才是底线。6. 二次开发改造从课程设计到能用的三个升级方向拿到这份报告最值的不是照抄是把它当骨架往上加肉。我一般会按三个方向改每个方向都能独立验证。方向一把成绩字段从CHAR(5)换成DECIMAL(5,1)并加状态位。原设计能存“缺考”是灵活但统计时隐式转换会把非数字变 0平均分直接失真。改法是Result DECIMAL(5,1) NULL加Status ENUM(normal,absent,deferred) DEFAULT normal统计视图里AVG(CASE WHEN Statusnormal THEN Result END)。验证方法插一条Statusabsent的记录看平均分是否把它排除。方向二给CV加version字段做乐观锁。前面 4.3 提过两个教师并发改同一行会互相覆盖。加字段后UPDATE ... WHERE version ?Java 里判断executeUpdate()返回值为 0 就弹“数据已变更”。验证方法开两个客户端同时改同一行第二个必然提示冲突而不是静默覆盖。方向三把权限从数据库账号下沉到应用层会话。现在靠cu1/cu2/cu3三个 MySQL 用户隔离部署时得维护三套连接配置稍不注意就全用root。更常见的做法是应用统一用一个受限账号连库登录后在Session里存角色DAO 层每个方法接收role参数做校验数据库层只保留“禁止 DROP/TRUNCATE”这类兜底权限。验证方法用学生角色直接调ScoreDao.update()应抛权限异常。改造点原设计改后验证方式成绩类型CHAR(5)DECIMAL(5,1)Status插缺考记录看平均分并发控制无version乐观锁双客户端同时改权限位置数据库 GRANT应用层 Session 兜底 GRANT越权调用 DAO改完这三处这个项目就从“交完作业就吃灰”变成能往简历上写的作品——它有真实的事务、真实的并发处理、真实的权限边界。我当年第一次做课程设计把所有 SQL 塞进按钮事件里老师一问“两个人同时改成绩会怎样”就哑了从那以后我每次写带写操作的模块都强制先问自己一句“并发下这行数据谁说了算”再动手。希望帮到你。本文还有配套的精品资源点击获取