☰
教务系统为什么首选MySQL+Java组合?
2026/10/14 6:56:50 网站建设 项目流程

简介:本资源是一份面向高校计算机专业学生的数据库课程设计实践材料,聚焦教务管理系统的完整开发流程,以MySQL为数据存储核心、Java为应用实现语言,帮助初学者掌握需求分析、ER建模、SQL脚本编写、JDBC连接、前后端基础交互等关键能力。压缩包共28个文件,含9个Java源码(涵盖Student、Course、Score等核心业务类)、9个编译后class文件、1个edu_manage.sql建库建表脚本、2个properties配置文件(含数据库连接参数)、2个运行依赖jar包,以及项目工程文件(.project、.classpath)和系统结构说明图(png),整体4.45MB,结构规范,开箱即学。已有2649人学习下载,可直接导入Eclipse运行调试,配套SQL脚本与目录模块清晰对应,便于理解教务场景下多表关联、事务处理与权限分层设计逻辑,是数据库原理与Java综合实训的典型参考范例。

1. 教务管理系统为什么非得用 MySQL + Java?——一个被反复验证的“稳态组合”

你可能已经见过太多教务系统演示视频:登录页、课程表、成绩录入、学籍查询……界面五花八门,但后台数据库建表脚本里,CREATE TABLE student (...) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;这行几乎从不缺席;而项目结构里,src/main/java/com/example/jwgl/下永远躺着StudentService.java和CourseController.java。这不是巧合,而是高校数据库课程设计中,MySQL + Java 成为事实标准的底层逻辑:它不追求前沿炫技,却精准卡在教学目标、工程约束与学生实操能力的交点上。这个组合能让你在三周内完成一个可运行、可调试、可展示、可答辩的完整系统——不是跑通 Hello World,而是真实处理“张三选了《数据库原理》但冲突了两门实验课”这类业务逻辑;它强制你理解事务隔离级别(比如成绩批量更新时不能漏掉某条记录),也逼你写出带参数校验的 JDBC 批量插入;更重要的是,它的报错信息足够直白(SQLException: Duplicate entry '2023001' for key 'student.student_id'),让新手第一次真正看懂“主键冲突”到底发生在哪一行代码、哪一张表、哪一个字段。如果你正被课程设计 deadline 追着跑,又不想在部署阶段被 Tomcat 端口冲突或 MySQL 时区错误拖垮,那这篇笔记就是为你写的实战路线图。

2. 从零搭起教务系统骨架:建库、建表、连通 Java 的最小可行路径

2.1 用 MySQL Workbench 或命令行初始化教务库与核心表结构

教务系统最常翻车的起点,不是代码写错,而是数据库没建对。别急着写 Java,先确保这四张表在 MySQL 里稳稳立住:student(学生)、teacher(教师)、course(课程)、sc(选课关系)。注意:sc表必须是联合主键(student_id,course_id),且两个字段都设外键——这是后续级联删除和数据一致性的基石。以下是经过多次答辩验证的建表语句,已适配 MySQL 8.0+ 默认配置(严格模式 + utf8mb4):

-- 创建教务数据库,显式指定字符集避免中文乱码 CREATE DATABASE jwgl_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- 切换到该库 USE jwgl_db; -- 学生表:学号为主键,姓名非空,入学年份用 YEAR 类型更语义化 CREATE TABLE student ( student_id CHAR(10) PRIMARY KEY, name VARCHAR(20) NOT NULL, gender ENUM('男', '女') DEFAULT '男', enrollment_year YEAR NOT NULL, major VARCHAR(30) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 教师表:工号为主键,职称用 VARCHAR 而非 ENUM(职称可能动态增减) CREATE TABLE teacher ( teacher_id CHAR(8) PRIMARY KEY, name VARCHAR(20) NOT NULL, title VARCHAR(20), department VARCHAR(30) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 课程表:课程号为主键,学分用 DECIMAL(3,1) 支持 2.5 学分等场景 CREATE TABLE course ( course_id CHAR(8) PRIMARY KEY, course_name VARCHAR(50) NOT NULL, credit DECIMAL(3,1) DEFAULT 2.0, hours INT DEFAULT 48, teacher_id CHAR(8), FOREIGN KEY (teacher_id) REFERENCES teacher(teacher_id) ON DELETE SET NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 选课表:联合主键 + 双外键,ON DELETE CASCADE 确保删学生时自动清空其选课记录 CREATE TABLE sc ( student_id CHAR(10), course_id CHAR(8), score DECIMAL(4,1) CHECK (score >= 0 AND score <= 100), PRIMARY KEY (student_id, course_id), FOREIGN KEY (student_id) REFERENCES student(student_id) ON DELETE CASCADE, FOREIGN KEY (course_id) REFERENCES course(course_id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

逻辑说明:ENGINE=InnoDB是硬性要求,只有它支持事务和外键;CHAR(10)比VARCHAR(10)更适合学号这种定长字符串,索引效率更高;ON DELETE CASCADE在删除学生时自动清理sc表记录,避免出现“幽灵选课”;CHECK约束直接在数据库层拦截非法成绩(如 -5 分或 150 分),比 Java 层校验更可靠。

2.2 Maven 项目中集成 MySQL 驱动与基础连接池

新建一个 Maven 项目(JDK 11+),在pom.xml中引入 MySQL Connector/J 8.0+ 和 HikariCP 连接池——别用老旧的mysql-connector-java5.x,它不兼容 MySQL 8.0 的默认认证插件(caching_sha2_password)。HikariCP 启动快、性能稳,是当前 Java 生态事实标准:

<dependencies> <!-- MySQL 8.0+ 驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-j</artifactId> <version>8.3.0</version> </dependency> <!-- HikariCP 连接池 --> <dependency> <groupId>com.zaxxer</groupId> <artifactId>HikariCP</artifactId> <version>5.0.1</version> </dependency> <!-- 简单日志门面,避免 slf4j 绑定冲突 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-simple</artifactId> <version>2.0.12</version> </dependency> </dependencies>

接着创建DatabaseConnection.java,封装连接获取逻辑。关键参数必须显式设置,否则中文会乱码、时区会错乱:

import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; import javax.sql.DataSource; import java.sql.Connection; import java.sql.SQLException; public class DatabaseConnection { private static DataSource dataSource; static { HikariConfig config = new HikariConfig(); // 数据库地址:localhost:3306,库名 jwgl_db,注意 useSSL=false(本地开发无需 SSL) config.setJdbcUrl("jdbc:mysql://localhost:3306/jwgl_db?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8mb4"); config.setUsername("root"); // 替换为你的 MySQL 用户名 config.setPassword("your_password"); // 替换为你的密码 // 强制指定字符集,解决中文存取乱码 config.addDataSourceProperty("characterEncoding", "utf8mb4"); config.addDataSourceProperty("useUnicode", "true"); // 连接池基础参数:最小空闲 5,最大连接 20,超时 30 秒 config.setMinimumIdle(5); config.setMaximumPoolSize(20); config.setConnectionTimeout(30000); // 验证连接有效性(MySQL 8.0+ 推荐用 SELECT 1) config.setConnectionTestQuery("SELECT 1"); dataSource = new HikariDataSource(config); } public static Connection getConnection() throws SQLException { return dataSource.getConnection(); } }

参数说明:serverTimezone=Asia/Shanghai解决时间字段(如enrollment_year)读写错位问题;characterEncoding=utf8mb4是中文支持的底线,缺一不可;setConnectionTestQuery("SELECT 1")让连接池每次取连接前执行轻量查询,避免拿到失效连接;minimumIdle=5保证池中总有 5 个热连接,减少首次请求延迟。

2.3 写第一个 DAO 方法:用 PreparedStatement 安全插入学生信息

DAO(Data Access Object)层是 Java 操作数据库的“守门人”。以插入学生为例,必须用PreparedStatement而非字符串拼接,否则name='张三'; DROP TABLE student;'这类注入攻击会直接摧毁你的答辩演示。以下StudentDao.java是最小可用实现:

import java.sql.*; public class StudentDao { // 插入学生:参数化 SQL,杜绝 SQL 注入 public boolean insertStudent(String studentId, String name, String gender, int year, String major) { String sql = "INSERT INTO student (student_id, name, gender, enrollment_year, major) VALUES (?, ?, ?, ?, ?)"; try (Connection conn = DatabaseConnection.getConnection(); PreparedStatement pstmt = conn.prepareStatement(sql)) { pstmt.setString(1, studentId); // ? 位置 1 → student_id pstmt.setString(2, name); // ? 位置 2 → name pstmt.setString(3, gender); // ? 位置 3 → gender pstmt.setInt(4, year); // ? 位置 4 → enrollment_year pstmt.setString(5, major); // ? 位置 5 → major int rows = pstmt.executeUpdate(); // 执行插入,返回影响行数 return rows == 1; // 成功则返回 true } catch (SQLException e) { // 关键:打印完整异常栈,方便定位是 SQL 错还是连接错 e.printStackTrace(); return false; } } // 查询所有学生:返回 List<Student>,此处简化为打印(实际应封装 Student 实体类) public void listAllStudents() { String sql = "SELECT student_id, name, gender, enrollment_year, major FROM student"; try (Connection conn = DatabaseConnection.getConnection(); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(sql)) { System.out.println("学号\t姓名\t性别\t入学年份\t专业"); while (rs.next()) { System.out.printf("%s\t%s\t%s\t%d\t%s%n", rs.getString("student_id"), rs.getString("name"), rs.getString("gender"), rs.getInt("enrollment_year"), rs.getString("major") ); } } catch (SQLException e) { e.printStackTrace(); } } }

逻辑说明:pstmt.setString(1, studentId)中的1指第一个?占位符,顺序必须严格对应 SQL 中?出现顺序;executeUpdate()用于 INSERT/UPDATE/DELETE,返回影响行数;try-with-resources自动关闭Connection、PreparedStatement、ResultSet,避免连接泄漏——这是答辩时老师最爱问的“资源是否释放”问题。

3. 核心业务闭环:实现“学生选课”与“成绩录入”的事务控制

3.1 为什么选课操作必须用事务?——一个真实翻车场景还原

想象这个场景:学生张三点击“选《数据库原理》”,后端代码先查课程剩余容量,再插入sc表记录。如果查完容量是 50,插入前另一学生李四也选了同一门课,导致容量瞬间变 49,而张三的插入仍成功——结果就是超员选课。这就是典型的脏读 + 非原子操作。解决方案只有一个:把“查容量”和“插记录”包进同一个数据库事务,用conn.setAutoCommit(false)控制,失败则conn.rollback()。下面CourseSelectionService.java实现了带容量检查的选课:

import java.sql.*; public class CourseSelectionService { // 选课:检查课程容量 + 插入选课记录,全程事务控制 public boolean selectCourse(String studentId, String courseId) { String checkSql = "SELECT COUNT(*) FROM sc WHERE course_id = ?"; String insertSql = "INSERT INTO sc (student_id, course_id) VALUES (?, ?)"; // 注意:这里必须手动管理事务,不能依赖连接池默认 auto-commit try (Connection conn = DatabaseConnection.getConnection()) { conn.setAutoCommit(false); // 关闭自动提交,开启事务 try (PreparedStatement checkStmt = conn.prepareStatement(checkSql); PreparedStatement insertStmt = conn.prepareStatement(insertSql)) { // 步骤1:查当前选课人数 checkStmt.setString(1, courseId); ResultSet rs = checkStmt.executeQuery(); rs.next(); int currentCount = rs.getInt(1); // 步骤2:查课程总容量(假设 course 表有 capacity 字段,需提前添加) // 若无 capacity 字段,此处可简化为硬编码阈值,如 if (currentCount >= 100) ... if (currentCount >= 100) { // 示例阈值:100人上限 throw new RuntimeException("课程已满,无法选课"); } // 步骤3:插入选课记录 insertStmt.setString(1, studentId); insertStmt.setString(2, courseId); int rows = insertStmt.executeUpdate(); if (rows != 1) { throw new RuntimeException("选课记录插入失败"); } // 步骤4:提交事务 conn.commit(); return true; } } catch (SQLException e) { // 事务内任何异常,回滚所有操作 try { if (e.getSQLState() != null && e.getSQLState().startsWith("08")) { System.err.println("数据库连接异常,事务已自动回滚"); } } catch (Exception ignored) {} return false; } catch (RuntimeException e) { // 业务逻辑异常(如课程已满),主动回滚 try (Connection conn = DatabaseConnection.getConnection()) { conn.rollback(); System.err.println("业务异常:" + e.getMessage()); } catch (SQLException ex) { ex.printStackTrace(); } return false; } } }

关键点:conn.setAutoCommit(false)是事务起点;conn.commit()是成功终点;conn.rollback()是失败终点;checkSql和insertSql必须在同一个Connection对象下执行,否则事务无效;if (currentCount >= 100)中的 100 是硬编码示例,实际应从course表读取capacity字段(需提前ALTER TABLE course ADD COLUMN capacity INT DEFAULT 100;)。

3.2 成绩录入的幂等性设计:如何防止重复提交导致成绩覆盖?

成绩录入接口最容易被前端重复点击触发(比如学生点两次“提交成绩”),导致同一条sc记录的score被覆盖两次。解决方案是:用INSERT ... ON DUPLICATE KEY UPDATE语句,而非先查后更。因为sc表主键是(student_id, course_id),当插入重复主键时,直接更新score字段,既避免并发冲突,又保证幂等:

public class ScoreService { // 录入/更新成绩:利用主键冲突实现幂等更新 public boolean updateScore(String studentId, String courseId, double score) { // 如果记录存在则更新 score,不存在则插入新记录 String sql = "INSERT INTO sc (student_id, course_id, score) VALUES (?, ?, ?) " + "ON DUPLICATE KEY UPDATE score = VALUES(score)"; try (Connection conn = DatabaseConnection.getConnection(); PreparedStatement pstmt = conn.prepareStatement(sql)) { pstmt.setString(1, studentId); pstmt.setString(2, courseId); pstmt.setDouble(3, score); int rows = pstmt.executeUpdate(); // rows=1 表示新插入,rows=2 表示更新(INSERT 1行 + UPDATE 1行) return rows > 0; } catch (SQLException e) { e.printStackTrace(); return false; } } }

原理说明:VALUES(score)引用的是INSERT子句中?对应的值,不是原记录的旧值;ON DUPLICATE KEY UPDATE由 MySQL 原生支持,比 Java 层加锁或查重更高效、更可靠;此语句天然支持“补录成绩”(记录不存在)和“修改成绩”(记录存在)两种场景,前端无需区分。

4. 避坑指南:教务系统开发中 5 个高频血泪问题与解法

4.1 现象:Java 读取 MySQL 的YEAR类型字段总是少一年(如 2023 显示为 2022)

原因:MySQL 的YEAR类型在 JDBC 驱动中默认映射为java.sql.Date,而Date构造函数会将年份减去 1900(历史遗留 bug)。
解决:在 JDBC URL 中添加yearIsDateType=false参数,并改用ResultSet.getInt()读取:

jdbc:mysql://localhost:3306/jwgl_db?...&yearIsDateType=false
// 读取 enrollment_year 时 int year = rs.getInt("enrollment_year"); // 直接 getInt,不要 getYear()

4.2 现象:插入中文姓名时报错Incorrect string value: '\xE5\xBCxA...' for column 'name'

原因:数据库、表、字段未统一使用utf8mb4,或 JDBC URL 缺少characterEncoding=utf8mb4。
解决:三步走——① 创建库时指定CHARACTER SET utf8mb4;② 建表时每个VARCHAR字段后加CHARACTER SET utf8mb4;③ JDBC URL 必须含characterEncoding=utf8mb4&useUnicode=true。验证命令:SHOW CREATE TABLE student;查看字段字符集。

4.3 现象:sc表外键约束不起作用,删学生后选课记录还在

原因:建表时用了ENGINE=MyISAM(不支持外键),或FOREIGN KEY语句语法错误(如字段类型不匹配:student_id是CHAR(10),但外键引用的student.student_id是VARCHAR(10))。
解决:① 所有表必须ENGINE=InnoDB;② 外键字段类型、长度、字符集必须与被引用字段完全一致;③ 执行SHOW ENGINE INNODB STATUS;查看外键错误日志。

4.4 现象:HikariCP 连接池启动时报Failed to validate connection,但 MySQL 服务正常

原因:MySQL 8.0+ 默认认证插件为caching_sha2_password,而旧版驱动不兼容;或serverTimezone未设置导致连接超时。
解决:① 确保pom.xml使用mysql-connector-j8.0+;② JDBC URL 必须含serverTimezone=Asia/Shanghai;③ 若仍失败,在 MySQL 中执行ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'your_password';切换认证方式。

4.5 现象:SELECT * FROM student WHERE name LIKE '%张%'查不到数据,但= '张三'可以

原因:LIKE查询在utf8mb4下需确保字段排序规则为utf8mb4_unicode_ci(而非utf8mb4_general_ci),后者对某些中文匹配不敏感。
解决:重建字段排序规则:

ALTER TABLE student MODIFY name VARCHAR(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

5. 答辩加分项:用视图 + 存储过程实现“班级平均分统计”并导出 Excel

5.1 创建教学班成绩统计视图:把多表关联逻辑沉淀到数据库层

教务系统答辩时,老师常问:“如果要查每个班级的平均分,SQL 怎么写?”此时若现场手敲 5 表 JOIN,大概率出错。更好的做法是预先创建视图,把复杂逻辑封装起来,Java 层只查视图。以下视图class_avg_score关联student、sc、course,按专业分组计算平均分:

-- 创建视图:班级(专业)平均分统计 CREATE VIEW class_avg_score AS SELECT s.major AS 专业, c.course_name AS 课程名称, COUNT(sc.student_id) AS 选课人数, ROUND(AVG(sc.score), 2) AS 平均分, MIN(sc.score) AS 最低分, MAX(sc.score) AS 最高分 FROM student s JOIN sc ON s.student_id = sc.student_id JOIN course c ON sc.course_id = c.course_id WHERE sc.score IS NOT NULL GROUP BY s.major, c.course_name;

验证视图:SELECT * FROM class_avg_score ORDER BY 专业, 课程名称;

优势:视图 SQL 只需写一次,Java 层调用SELECT * FROM class_avg_score即可获得结构化结果;ROUND(AVG(), 2)直接返回保留两位小数的平均分,避免 Java 层格式化;WHERE sc.score IS NOT NULL过滤掉未录入成绩的记录,数据更真实。

5.2 用存储过程生成“某课程成绩分析报告”,并支持导出为 CSV

视图解决了查询,但“导出 Excel”需要文件操作,Java 做更合适。不过,我们可以用存储过程预计算关键指标(如不及格率、优秀率),再由 Java 调用并写入 CSV。以下存储过程proc_course_analysis接收课程号,输出分析结果:

DELIMITER $$ CREATE PROCEDURE proc_course_analysis(IN p_course_id CHAR(8)) BEGIN DECLARE total_count INT DEFAULT 0; DECLARE fail_count INT DEFAULT 0; DECLARE excellent_count INT DEFAULT 0; -- 统计总人数、不及格(<60)、优秀(>=90) SELECT COUNT(*), COUNT(CASE WHEN score < 60 THEN 1 END), COUNT(CASE WHEN score >= 90 THEN 1 END) INTO total_count, fail_count, excellent_count FROM sc WHERE course_id = p_course_id AND score IS NOT NULL; -- 返回结果集 SELECT p_course_id AS 课程号, total_count AS 总人数, fail_count AS 不及格人数, ROUND(fail_count / total_count * 100, 2) AS 不及格率, excellent_count AS 优秀人数, ROUND(excellent_count / total_count * 100, 2) AS 优秀率; END$$ DELIMITER ;

调用方式:CALL proc_course_analysis('CS101');

为什么用存储过程:计算逻辑(如百分比)在数据库层完成,减少网络传输数据量;INTO变量赋值让逻辑更清晰;CALL语句可被 Java 的CallableStatement直接调用,无缝集成。

5.3 Java 层调用存储过程并导出 CSV:用 OpenCSV 避免手动拼接逗号的玄学错误

Maven 引入 OpenCSV(比手写BufferedWriter更可靠):

<dependency> <groupId>com.opencsv</groupId> <artifactId>opencsv</artifactId> <version>5.7.1</version> </dependency>

Java 调用并导出:

import com.opencsv.CSVWriter; import java.io.FileWriter; import java.sql.*; public class ReportExporter { public static void exportCourseAnalysis(String courseId, String outputPath) { String sql = "{CALL proc_course_analysis(?)}"; try (Connection conn = DatabaseConnection.getConnection(); CallableStatement cstmt = conn.prepareCall(sql)) { cstmt.setString(1, courseId); ResultSet rs = cstmt.executeQuery(); // 写入 CSV 文件 try (CSVWriter writer = new CSVWriter(new FileWriter(outputPath))) { // 写入表头 writer.writeNext(new String[]{"课程号", "总人数", "不及格人数", "不及格率", "优秀人数", "优秀率"}); // 写入数据行 while (rs.next()) { String[] row = { rs.getString("课程号"), String.valueOf(rs.getInt("总人数")), String.valueOf(rs.getInt("不及格人数")), rs.getString("不及格率"), String.valueOf(rs.getInt("优秀人数")), rs.getString("优秀率") }; writer.writeNext(row); } } System.out.println("成绩分析报告已导出至:" + outputPath); } catch (Exception e) { e.printStackTrace(); } } }

血泪经验:OpenCSV 会自动处理字段中的逗号、换行符、双引号,而手写writer.write(s1 + "," + s2)在s1="张三,李四"时直接破坏 CSV 结构;writer.writeNext()是安全边界;导出路径建议用绝对路径(如"D:/jwgl_report.csv"),避免相对路径找不到文件。

6. 我的三个答辩通关习惯:从代码规范到演示话术的细节打磨

教务系统答辩不是比谁功能多,而是比谁稳定、清晰、可解释。我带过几届学生做这个课题,发现最终拿高分的,往往赢在三个被忽略的细节上。

第一个习惯:所有 SQL 语句单独抽离成常量,且带业务注释。
别把String sql = "SELECT ...";散落在 DAO 方法里。我在SqlConstants.java中统一管理:

public class SqlConstants { // 学生选课统计:用于首页仪表盘 public static final String STUDENT_COURSE_COUNT = "SELECT s.name, COUNT(sc.course_id) as course_count " + "FROM student s LEFT JOIN sc ON s.student_id = sc.student_id " + "GROUP BY s.student_id, s.name ORDER BY course_count DESC LIMIT 10"; // 成绩分布统计:用于柱状图 public static final String SCORE_DISTRIBUTION = "SELECT FLOOR(score/10)*10 as score_range, COUNT(*) as count " + "FROM sc WHERE score IS NOT NULL GROUP BY score_range ORDER BY score_range"; }

这样做的好处是:答辩时老师问“这个统计怎么算的”,我能立刻打开这个文件,指着注释说“这是首页显示选课最多的 10 个学生”,而不是在几十个方法里翻找;而且 IDE 的全局搜索能瞬间定位所有 SQL,重构时不会漏掉。

第二个习惯:演示前必做“断网测试”。
把笔记本 WiFi 和网线全拔掉,只留本地 MySQL 和 Java 进程。然后运行所有核心流程:新增学生、选课、录成绩、查视图、导出 CSV。为什么?因为答辩现场投影仪一连,有时会意外触发网络代理或防火墙,导致localhost解析失败。我见过太多同学演示到一半弹出Connection refused,慌得重装 MySQL。而断网测试通过,意味着所有依赖都是本地闭环,稳定性拉满。

第三个习惯:准备三句“万能解释话术”,应对所有技术质疑。

  • 当老师问“为什么不用 Spring Boot?”:“课程设计目标是掌握 JDBC 底层交互和事务控制,Spring Boot 的自动配置会掩盖这些关键机制,我们选择手动管理连接和事务,确保每一步都可控、可调试。”
  • 当老师问“外键用 CASCADE 安全吗?”:“在教务系统中,学生删除是极低频操作,CASCADE 能保证数据一致性;若需审计,我们可在删除前记录日志表,这是业务权衡后的合理选择。”
  • 当老师指出“成绩录入没做权限控制”:“当前版本聚焦核心数据流闭环,权限模块计划作为扩展项,采用 RBAC 模型,教师只能录自己授课课程的成绩,管理员可全局操作——这部分已在需求文档第 3.2 节明确。”
    这三句话不是推脱,而是把“没做”转化为“有规划、有取舍、有依据”,展现工程思维。

最后一点私人建议:答辩 PPT 的架构图,别画成 UML 那种抽象框图。就用一张最朴素的三层图——顶部写“MySQL(jwgl_db)”,中间写“Java(DAO/Service/Controller)”,底部写“Console / Swing GUI”,用箭头标出“JDBC 连接”和“SQL 查询”。老师一眼看懂技术栈,不费脑,不挑刺。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询