当前位置: 首页 > news >正文

从零构建课堂教学过程管理系统——数据库原理课程设计全记录

作者:KerensaLiu | 2026年5月 | 数据库原理课程设计


前言

这篇文章将完整记录我从数据库设计、SQL脚本编写、Java后端开发、Swing UI设计到最终部署的全过程。如果你想了解如何把一个数据库课程设计从纸面变成可运行的应用,这篇文章应该能帮到你。


一、作业要求分析

先来看看作业到底要求了什么:

信息管理需求(6类数据)

  1. 课堂教学基本信息:教师、学生、课程、教学内容、授课地点、授课时间
  2. 过程管理记录:过程分类(测验/作业/实验/考试等),每类过程的记录
  3. 过程与教学内容的相关性:每个考核对应哪个知识点
  4. 过程与培养能力的相关性:每个考核培养了什么能力
  5. 学生表现情况:每个学生在每次考核中的得分、参与状态
  6. 过程评价体系:每类过程考核的评分标准

功能需求(6项基础 + n项拓展)

  1. 基础表的CRUD + 各种约束机制
  2. 查询某个学生在某次过程考核中的详细信息
  3. 统计每位学生每次考核的成绩,以及平均成绩
  4. 按知识点分类统计,找出成绩最优和最差的3人
  5. 查询未参与考核的学生
  6. 复杂查询 + 存储过程 + 触发器 + 断言

拓展要求

  • 最小函数依赖集 + 范式分析 + 证明
  • ER图 + 关系模式映射 + DDL + 10条测试数据
  • 自定义拓展信息管理 + 补充函数依赖
  • 所有统计创建为视图
  • 用户管理 + 完整性约束

我的应对策略

面对这么多要求,我决定做一个完整的桌面应用,而不仅仅是SQL脚本合集。技术栈选择:

技术 理由
数据库 MySQL 8.0 课程要求
后端 Java 17 + JDBC 稳定可靠,学校机房兼容
前端 Java Swing + 莫兰迪配色 原生UI,无需额外安装
构建 Maven 依赖管理方便
工具 IntelliJ IDEA + DBeaver 开发+数据库管理

二、数据库设计

2.1 实体识别与关系设计

从需求中我抽象出13个实体/关系:

核心实体:Teacher(教师)        —— 上课的人Student(学生)        —— 被考核的人ClassInfo(班级)      —— 学生所属班级Classroom(教室)      —— 上课地点TeachingContent(教学内容)—— 知识点/章节ProcessCategory(过程分类)—— 考核类型ProcessRecord(过程记录)—— 具体的考核活动AbilityTarget(能力目标)—— 培养目标关联实体:Schedule(授课安排)   —— 教师+教室+内容+时间StudentPerformance(学生表现)—— 学生+考核+成绩ProcessAbility(过程-能力)—— 考核→能力映射EvaluationCriteria(评价标准)—— 分类→评分标准Users(用户)          —— 登录系统

2.2 最小函数依赖集

以几个核心表为例:

Teacher表

F_min = { teacher_id → teacher_no, name, title, department, email, phone }

教师ID决定所有其他属性,不存在部分依赖和传递依赖。候选键:teacher_id, teacher_no

StudentPerformance表

F_min = { perf_id → student_id, record_id, score, status, comment, submit_time }

同时存在自然候选键 (student_id, record_id) → 所有属性。任选一个为主键即可。

ProcessRecord表

F_min = { record_id → content_id, category_id, title, description, record_date, total_score }

record_id 决定一切,单属性候选键。

2.3 范式分析

所有13张表全部达到 BCNF(Boyce-Codd范式)

  • 2NF(无部分依赖):所有表的主键都是单属性(除了ProcessAbility是双属性组合主键),天然满足2NF。对于复合主键表ProcessAbility,非主属性 expected_level 完全函数依赖于整个主键 (record_id, ability_id),不存在部分依赖。

  • 3NF(无传递依赖):以Student表为例——student_id → class_id → class_name 看起来像传递依赖,但实际上 class_name 不在Student表中(它在ClassInfo表中),Student只存 class_id 作为外键。这恰恰是规范化的设计。

  • BCNF(所有决定因素都是候选键):我检查了每个表的每个函数依赖,确保决定因素都包含候选键。例如在StudentPerformance表中,(student_id, record_id) 是一个候选键,而 perf_id 是另一个。所有依赖的决定因素都是候选键。

2.4 ER图(关系描述)

image

关键关系说明

  • Teacher : Schedule = 1 : N(一个老师有多个时间段的课)
  • Student : ProcessRecord = N : M(通过StudentPerformance关联)
  • ProcessRecord : AbilityTarget = N : M(通过ProcessAbility关联)
  • TeachingContent : ProcessRecord = 1 : N(一个知识点可能有多次考核)

2.5 完整约束设计

除了主键和外键,我还设计了多种用户自定义约束:

-- CHECK约束
CHECK (gender IN ('男', '女'))         -- 性别枚举
CHECK (email LIKE '%@%.%')             -- 邮箱格式
CHECK (score BETWEEN 0 AND 100)        -- 成绩范围
CHECK (weight BETWEEN 0 AND 100)       -- 权重范围
CHECK (capacity BETWEEN 1 AND 500)     -- 教室容量
CHECK (start_time < end_time)          -- 时间逻辑
CHECK (week_day BETWEEN 1 AND 7)       -- 星期范围
CHECK (start_week > 0 AND end_week <= 20) -- 周次范围-- 断言(用触发器实现)
-- 过程记录日期必须在学期范围内
-- 过程分类权重总和不超过100%
-- 出勤成绩不超过10分

三、SQL脚本编写

整个数据库的SQL脚本分为7个文件,按顺序执行:

3.1 文件结构

database/
├── 01_database_design.sql    ← 数据库创建 + 设计文档
├── 02_create_tables.sql      ← DDL:13张表 + 约束 + 索引
├── 03_insert_data.sql        ← DML:每表10+条测试数据
├── 04_create_views.sql       ← 10个视图
├── 05_stored_procedures.sql  ← 7个存储过程
├── 06_triggers.sql           ← 9个触发器
└── 07_complex_queries.sql    ← 13个复杂查询示例

3.2 10个视图

视图 用途 对应功能
v_student_process_detail 学生考核详情 功能(2)
v_student_assessment_stat 分类成绩统计 功能(3)
v_student_avg_score 学生平均成绩 功能(3)
v_content_assessment_stat 知识点考核统计 功能(4)
v_content_best_worst 最优最差排名 功能(4)
v_absent_students 未参与学生 功能(5)
v_category_summary 分类汇总 拓展
v_student_ranking 成绩排名 拓展
v_process_ability 过程-能力视图 拓展
v_full_schedule 完整课表 拓展

3.3 7个存储过程

sp_get_student_all_performance(IN student_id)     -- 学生全部考核
sp_get_student_process_score(IN student_id, record_id) -- 单次成绩
sp_get_student_summary(IN student_id)             -- 综合统计
sp_get_content_best_worst(IN content_id)          -- 知识点排名
sp_get_absent_students(IN record_id)              -- 缺席查询
sp_upsert_performance(...)                        -- 成绩录入
sp_get_score_distribution(IN category_id)         -- 分数分布

3.4 9个触发器

触发器 触发时机 功能
trg_before_insert_performance BEFORE INSERT 成绩≤考核总分
trg_before_update_performance BEFORE UPDATE 成绩≤考核总分
trg_after_insert_process AFTER INSERT 自动为学生创建缺席记录
trg_after_insert_student AFTER INSERT 自动创建已有考核的缺席记录
trg_before_delete_student BEFORE DELETE 级联删除表现记录
trg_before_insert_category BEFORE INSERT 权重总和≤100%
trg_before_update_category BEFORE UPDATE 权重总和≤100%
trg_check_record_date BEFORE INSERT 日期在学期范围内
trg_check_attendance_score BEFORE INSERT 出勤分≤10

四、Java应用架构

4.1 项目结构

src/main/java/com/classflow/
├── Main.java                    # 入口:登录窗口
├── config/
│   └── DatabaseConfig.java      # 数据库连接配置
├── model/                       # 实体类 13个
│   ├── Teacher.java
│   ├── Student.java
│   ├── ClassInfo.java
│   ├── Classroom.java
│   ├── TeachingContent.java
│   ├── Schedule.java
│   ├── ProcessCategory.java
│   ├── ProcessRecord.java
│   ├── StudentPerformance.java
│   ├── AbilityTarget.java
│   ├── ProcessAbility.java
│   ├── EvaluationCriteria.java
│   └── User.java
├── dao/                         # 数据访问层 11个
│   ├── BaseDAO.java             # 通用CRUD基类
│   ├── TeacherDAO.java ~ UserDAO.java
├── ui/
│   ├── MainFrame.java           # 主窗口 + 顶部栏
│   ├── theme/
│   │   └── MorandiTheme.java    # 莫兰迪配色系统
│   └── panels/
│       ├── BasicInfoPanel.java  # 基础信息管理(6子标签)
│       ├── ProcessPanel.java    # 过程考核管理(4子标签)
│       ├── QueryPanel.java      # 查询与统计(10项查询)
│       └── UserPanel.java       # 用户管理
└── util/└── DBUtil.java              # JDBC工具类

4.2 架构设计思路

三层架构(简化版):

UI Layer (Swing)  →  DAO Layer (JDBC)  →  MySQL↓                      ↓Panels              BaseDAO基类↓                      ↓事件处理            ResultSet映射↓                      ↓表格展示            Model对象

为什么不直接用Spring?
课程设计要求的是数据库原理,不是企业级开发。用纯JDBC + Swing可以:

  1. 清晰展示SQL语句的执行过程
  2. 没有框架黑盒,每行代码都在掌控之中
  3. IDEA Community版就能跑,不依赖任何付费工具

4.3 BaseDAO设计

所有DAO继承这个基类,核心方法只有4个:

// 查询多行 → List<T>
protected List<T> query(String sql, Object... params)// 查询单行 → T or null
protected T querySingle(String sql, Object... params)// 更新/删除 → 受影响行数
protected int executeUpdate(String sql, Object... params)// 插入 → 自增ID
protected int executeInsert(String sql, Object... params)

子类只需实现 mapRow(ResultSet rs) 方法,完成ResultSet到Model的映射。这样避免了每个DAO都重复写JDBC的样板代码。


五、莫兰迪配色UI设计

5.1 配色理念

莫兰迪色系以意大利画家乔治·莫兰迪命名,特点是低饱和度柔和安静。特别适合需要长时间操作的管理系统——不刺眼,减少视觉疲劳。

5.2 色板

主背景    #F5F0EB  暖米白    
面板背景  #E8E0D5  暖浅驼                   
工具栏    #DDD5C8  浅暖灰    
按钮      #C5D0C0  莫兰迪绿  
主文字    #5D5348  深棕灰    
次要文字  #8B7D6B  中棕灰    
边框      #D1CBC0  暖灰      
强调色    #D4C5C7  灰粉
辅助强调  #B5C4B1  灰绿
表头      #CCC4B6  浅米棕

5.3 代码实现

public static void styleButton(JButton btn) {btn.setBackground(BUTTON_BG);        // #C5D0C0btn.setForeground(BUTTON_TEXT);      // #4A423A 深棕文字btn.setFont(FONT_BUTTON);            // 微软雅黑 Bold 13btn.setBorder(new EmptyBorder(8, 18, 8, 18));btn.setFocusPainted(false);btn.setCursor(new Cursor(Cursor.HAND_CURSOR));// 鼠标悬停变深btn.addMouseListener(new MouseAdapter() {public void mouseEntered(MouseEvent e) { btn.setBackground(BUTTON_HOVER); }public void mouseExited(MouseEvent e) { btn.setBackground(BUTTON_BG); }});
}

5.4 表格交替行配色

table.setDefaultRenderer(Object.class, new DefaultTableCellRenderer() {@Overridepublic Component getTableCellRendererComponent(...) {if (!isSelected) {c.setBackground(row % 2 == 0 ? Color.WHITE : TABLE_ALT_ROW);}return c;}
});

六、功能实现详解

6.1 登录系统

直接对比用户名和密码(课程项目简化处理):

SELECT u.*, CASE WHEN u.role='teacher' THEN t.name WHEN u.role='student' THEN s.name END AS related_name
FROM Users u 
LEFT JOIN Teacher t ON ... 
LEFT JOIN Student s ON ...
WHERE u.username=? AND u.password_hash=? AND u.is_active=1

三种角色:admin(全部权限)、teacher(教师)、student(学生)。

6.2 基础信息管理(Tab 1)

6个子标签,每个都是完整的CRUD:

教师管理 | 学生管理 | 班级管理 | 教室管理 | 教学内容 | 授课安排

核心实现——用 Map<String, TabInfo> 确保每个标签页独立管理自己的表格:

private record TabInfo(JTable table, DefaultTableModel model) {}
private Map<String, TabInfo> tabMap = new LinkedHashMap<>();private void loadTableData(String type) {TabInfo ti = tabMap.get(type);  // 获取当前标签的表格DefaultTableModel tm = ti.model();tm.setRowCount(0);// 根据type加载不同数据...
}

踩坑记录:一开始我把 tabletableModel 作为类字段,每个 createEntityPanel() 都会覆盖它们。结果点"教师管理"时,数据写到了最后一个标签(授课安排)的表格里,看起来像没数据。调试了很久才发现这个问题。

6.3 过程考核管理(Tab 2)

4个子标签:

过程分类 | 过程记录 | 学生表现 | 评价标准

过程分类管理考核类型和权重,权重总和不超过100%(触发器保障)。

学生表现的亮点是使用 ON DUPLICATE KEY UPDATE 实现成绩的插入或更新:

INSERT INTO StudentPerformance (student_id, record_id, score, status, comment)
VALUES (?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE score = VALUES(score),status = VALUES(status),comment = VALUES(comment),submit_time = CURRENT_TIMESTAMP

(student_id, record_id) 上有UNIQUE约束,所以同一学生对同一考核只能有一条记录。

6.4 查询与统计(Tab 3)

10项查询功能,左侧按钮列表,右侧表格+详细区域:

查询 SQL要点
学生考核详细信息 多表JOIN:Student → Performance → Record → Content
学生过程考核统计 视图 v_student_avg_score,聚合统计
学生平均成绩排名 RANK() OVER (ORDER BY overall_avg DESC)
知识点考核统计(降序) GROUP BY content_id + student,ORDER BY AVG(score) DESC
未参与考核学生 CROSS JOIN + LEFT JOIN,WHERE sp.status IS NULL
过程分类成绩分布 CASE WHEN 分段统计
班级成绩对比 AVG + MAX + MIN + STDDEV 聚合
学生能力达成度 ProcessAbility → AbilityTarget 关联查询
授课计划查看 视图 v_full_schedule
成绩预警 HAVING AVG(score) < 60

为什么用直接SQL而不是视图?
部分查询(如未参与考核)需要动态参数(process_id可选),且DAO的ResultSet映射对视图列不兼容。所以我选择在UI层直接写原生SQL——对于一个课程项目来说,这比过度抽象更实用。


七、开发过程中踩过的坑

7.1 DBeaver的DELIMITER问题

最初存储过程和触发器用了 DELIMITER $$ 语法,但在DBeaver中执行时报语法错误。原因是DBeaver对DELIMITER的处理与mysql命令行不同。

解决:去掉 DELIMITER,每个 CREATE PROCEDURE/TRIGGER ... BEGIN ... END; 作为独立语句执行。

7.2 DROP IF EXISTS的"不存在"中断

DROP TRIGGER IF EXISTS xxx 在触发器不存在时产生一个MySQL Note,DBeaver默认配置下会当成错误中断执行。

解决:首次执行时直接去掉 DROP IF EXISTS,因为触发器本来就不存在。

7.3 标签页表格共享Bug

前面提到过——多个子标签页的 tableModel 被反复覆盖,导致数据写入错误表格。

解决:用 Map<String, TabInfo> 为每个标签页独立存储其表格引用。

7.4 按钮白色文字看不清

最初的按钮配色是莫兰迪绿底 + 白字(#FFFFFF),在浅色背景下对比度不够。

解决:改为浅绿底(#C5D0C0)+ 深棕字(#4A423A),对比度大幅提升。

7.5 Maven依赖下载失败

国内访问Maven Central经常超时,导致MySQL Connector和FlatLaf无法下载。

解决:配置阿里云Maven镜像(~/.m2/settings.xml),清理缓存的 .lastUpdated 文件后重新下载。


八、部署与运行指南

8.1 环境准备

  • MySQL 8.0+
  • JDK 17+
  • IntelliJ IDEA Community
  • DBeaver

8.2 数据库初始化

在DBeaver中按顺序执行 database/ 目录下的SQL文件:

01 → 02 → 03 → 04 → 05 → 06
  • 01-04:全选 + Ctrl+Enter 即可
  • 05-06:右键 → Execute SQL Script(或逐段选择执行)

8.3 配置密码

修改两个文件中的密码为你的MySQL root密码:

  • DatabaseConfig.java 第6行
  • application.properties 第4行

8.4 运行

IDEA打开项目 → Maven自动下载依赖 → 运行 Main.java → 登录。

预设账号:

角色 用户名 密码
管理员 admin admin
教师 zhangming 123456
学生 chenxm 123456

九、测试数据一览

为了让所有统计功能都有意义的数据,我插入了12名学生 × 12次过程考核 = 144条表现记录:

成绩分布设计

  • 优秀(90+):陈小明、吴丽华、陈雨桐
  • 良好(80-89):林小红、刘思雨、赵雪梅、何晓芳
  • 中等(60-79):黄志强、周建国、郑鹏飞
  • 不及格(<60):杨浩宇、马天宇

缺席/请假记录:故意在部分学生中插入了 absentexcused 状态,确保"未参与考核学生"查询有结果显示。


十、项目总结与收获

10.1 技术收获

  1. 数据库设计:从需求分析 → 实体识别 → 函数依赖 → 范式分析 → ER图 → DDL,完整走了一遍数据库设计流程。BCNF的理论在实践中的体现变得非常清晰。

  2. 约束的重要性:CHECK、UNIQUE、外键、触发器这些约束不是纸上谈兵——没有它们,数据质量根本无法保证。ON DUPLICATE KEY UPDATE 这种MySQL特性在成绩录入场景中非常实用。

  3. 视图的价值:把复杂查询封装成视图,不仅简化了SQL,还让统计逻辑和业务逻辑解耦。

  4. Swing开发的细节:Swing虽然古老,但理解了它的线程模型(EDT)、布局管理器、事件分发后,依然能写出体验不错的桌面应用。

10.2 项目统计

数据库表:13张
视图:10个
存储过程:7个
触发器:9个
Java类:35个
SQL总行数:~500行
Java总行数:~2800行
测试数据:144条学生表现记录
Git提交:9次

10.3 改进方向

如果这个项目继续迭代,我会考虑:

  • 连接池:目前每次查询都创建新连接,用HikariCP可以大幅提升性能
  • 密码加密:目前明文存储和比较,生产环境必须用BCrypt
  • 分页查询:数据量大时需要分页加载
  • 图表可视化:用JFreeChart给统计结果加上图表
  • Spring Boot迁移:如果作为真实项目,后端应该迁移到Spring Boot + MyBatis

10.4 一点感悟

这个项目让我体会到:数据库原理不是背概念,而是在设计每一张表、每一条约束、每一个查询时,自然地运用范式理论、关系代数、事务特性。当一个SQL查询写出80行、跨了6张表还能正确运行时,你会感觉到数据库设计的魅力。

如果你也在做类似的课程设计,希望这篇文章能给你一些参考。


项目仓库:github.com/KerensaLiu709/ClassFlow

技术栈:Java 17 + Swing + MySQL 8.0 + Maven

http://www.jsqmd.com/news/824116/

相关文章:

  • 70行代码实现MCU性能热点分析:基于Cortex-M中断采样的轻量级Profiler
  • 树莓派机械爪控制:从PWM原理到软硬件集成实践
  • DataCleaner:企业级数据质量管理的开源利器
  • 第13章:C++ 静态分析工具
  • 硬件产品出海必读:从Type A到Type O,不同国家电源插头标准与适配设计要点
  • 2025年CMS怎么选?从传统到无头再到AI原生,一份深度选型指南
  • 深入解析主权身份:DID与可验证凭证构建去中心化数字身份
  • 贵阳高评价沙发定制厂家盘点 工程级实力客观对比 - 奔跑123
  • 电气噪声抑制实战:从原理到电磁屏蔽的电子系统稳定性设计
  • 基于OpenClaw构建智能家居环境感知系统:从传感器到自动化规则
  • 大语言模型底层逻辑:从LM到Agent的完整工作流解析!
  • 贵州酒店家具厂家实力排行:工程定制维度实测 - 奔跑123
  • Midjourney v6.2建筑专属更新深度拆解:仅0.3%用户掌握的--tile+--style raw+--sref三重空间锚定技术
  • 2026 四川角钢怎么选 西南 TOP 代理商拆解|成都行情涨跌与 5-6 月预测 - 四川盛世钢联营销中心
  • 线段树进阶
  • 企业级浏览器自动化测试架构设计:Chrome for Testing的高可用解决方案与实践指南
  • 2026年5月广东评价高的移动洗手间出租/户外移动洗手间出租厂家推荐 - 品牌鉴赏师
  • 汽车电子贴片晶振选型指南:从32.768kHz到高速接口的频点解析与设计实践
  • 元戎启行校招怎么准备:别只看赛道热度,更要看学习式路线和工程落地
  • nRF52840开发板移植CircuitPython实战:从编译到蓝牙应用
  • 中文大模型智能路由框架:多模型自动调度与成本优化实践
  • 从Processing到Arduino IDE:一个让硬件编程变简单的GUI故事(附STM32兼容板配置避坑)
  • 利用CTranslate2与INT8量化,实现Whisper语音识别7倍加速
  • 构建AI记忆中枢:使用memory-sync实现多源数据实时向量化同步
  • 如何用Parabolic实现终极视频下载:200+网站支持,完全免费的多媒体解决方案
  • 别再重装系统了!Ubuntu 20.04 下 libsnark 零知识证明环境一次搭建成功的保姆级避坑指南
  • 多智能体协作:真正难的不是能力,而是治理
  • 【权威验证】基于17国田野案例的NotebookLM人类学效能报告:信息提取准确率提升63.8%,编码耗时下降71%
  • Fusion 360 CAM实战:从零设计到CNC铣削木质机械键盘键帽
  • 别再乱用`define了!SV宏定义实战避坑指南(从`ifdef到字符串拼接)