尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Java学生成绩管理系统:JDBC连接MySQL与三层结构实战

Java学生成绩管理系统:JDBC连接MySQL与三层结构实战 简介这是一份基于Java GUI的学生成绩管理系统课程设计与期末作业资源适合Java初学者及需要完成类似管理系统的在校学生。系统实现了完整的业务闭环登录验证、学生/教师注册、成绩添加、修改、查询、删除、文件批量导入、学生账号管理及密码修改界面采用Swing构建数据存储于MySQL数据库并附有可运行的jar驱动。包体共80个文件其中28个Java源文件、42个class编译文件、2个jar驱动包、2个docx实验报告文档另有jpg背景图、xls数据文件和Eclipse工程配置压缩包约5.74MB解压后可直接用Eclipse导入结构清晰便于二次开发。实验报告包含系统设计、数据库表结构、关键代码说明及测试记录可帮助理解Java连接数据库与GUI事件处理的完整流程。该资源已有9122人学习下载无论是用于课程设计参考还是功能模块复用都具有实用价值。1. 学生成绩管理系统从能跑通到能通过答辩很多 Java 课程设计拿到“学生成绩管理系统”这个题目时第一反应是找一套现成代码改改但答辩时老师随口问一句“JDBC 连接串里为什么要加 serverTimezone”“成绩表为什么不用 DOUBLE 而用 DECIMAL”代码是别人写的你答不上来整个报告就站不住。这个项目的核心不是界面做得多花哨而是把 Java 与数据库的增删改查、事务、分层拆清楚。这篇文章按“建库建表 → JDBC 封装 → DAO 模板 → 避坑 → 验收”的顺序给出一套不用 Spring Boot 也能跑、能写进实验报告、能扛住提问的实现路径。适合正在做课程设计、准备 Java 基础面试或者想完整走一遍 JDBC 流程的读者。2. 用 JDBC MySQL 搭出核心骨架建表、连接封装与三层结构2.1 为什么先别上 Spring Boot MyBatis课程设计的边界判断现在网上的管理系统教程一抓一大把很多直接用 Spring Boot MyBatis甚至前端还套一个 Vue。但对于“java实现学生成绩管理系统含数据库以及实验报告”这个题目我一般不建议一上来就上全家桶。原因有几个课程设计答辩时老师大概率会问“JDBC 的六个步骤是什么”“PreparedStatement 和 Statement 的区别”这些基础不讲清楚框架只会把底层细节藏起来另外很多机房环境没有外网或者 Maven 仓库不稳定原生 JDBC 只要一个 mysql-connector-java 的 jar 包就能跑还有一点三层结构entity/dao/service/ui本身就是 Java 面试题里常考的分层思想写在报告里比贴一堆框架配置更讨喜。如果你的目标是想让这个项目进简历那当然可以后续改造。常见做法是把 DAO 层换成 MyBatisservice/ui 保持不动改造量很小。但课程设计阶段把原生 JDBC 跑通基本功练扎实后面换框架才有底气。2.2 数据库设计学生表、课程表、成绩表与两个关键约束成绩管理系统最少要有三张表学生表、课程表、成绩表。很多同学图省事只建一张表把课程名列在学生记录里后果是同一学生选多门课时数据冗余老师答辩时一问“这个设计怎么保证数据一致性”就崩。三表结构是最稳的答案。建表 SQL 如下CREATE DATABASE student_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE student_db; CREATE TABLE student ( student_id INT PRIMARY KEY AUTO_INCREMENT COMMENT 学号, student_name VARCHAR(32) NOT NULL COMMENT 姓名, gender CHAR(1) DEFAULT 男 COMMENT 性别, class_name VARCHAR(32) DEFAULT NULL COMMENT 班级, created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间 ) ENGINEInnoDB COMMENT学生表; CREATE TABLE course ( course_id INT PRIMARY KEY AUTO_INCREMENT COMMENT 课程编号, course_name VARCHAR(64) NOT NULL UNIQUE COMMENT 课程名, credit DECIMAL(3,1) DEFAULT 0.0 COMMENT 学分 ) ENGINEInnoDB COMMENT课程表; CREATE TABLE score ( id INT PRIMARY KEY AUTO_INCREMENT, student_id INT NOT NULL COMMENT 学号, course_id INT NOT NULL COMMENT 课程编号, score DECIMAL(5,2) NOT NULL COMMENT 成绩保留两位小数, exam_date DATE DEFAULT NULL COMMENT 考试日期, UNIQUE KEY uk_stu_course (student_id, course_id), CONSTRAINT fk_score_student FOREIGN KEY (student_id) REFERENCES student(student_id) ON DELETE CASCADE, CONSTRAINT fk_score_course FOREIGN KEY (course_id) REFERENCES course(course_id) ON DELETE CASCADE ) ENGINEInnoDB COMMENT成绩表;这段建表语句有几个细节我每次都会强调student_id 用 INT AUTO_INCREMENT不是用学号字符串当主键。字符串主键做关联查询时索引更大、占空间更多而且学号可能包含中文或前缀排序时容易出“字典序”问题后面第 4 章专门讲这个坑。score 表的 score 字段选 DECIMAL(5,2) 而不是 DOUBLE因为 DOUBLE 在浮点运算时会有精度误差DECIMAL 是精确小数排平均分时不容易差 0.01。唯一键 uk_stu_course 保证同一学生同一门课只能有一条成绩避免重复录入。外键用 ON DELETE CASCADE删除学生或课程时自动清理关联成绩业务上合理报告里也能解释清楚为什么“删学生不会留下孤儿成绩”。2.3 数据库连接封装从 DriverManager 到连接池的三种写法连接部分是 JDBC 里最容易写乱的地方。第一种是每次 getConnection 都写一遍驱动加载和连接串代码重复不说驱动类实际只要加载一次第二种是写一个 DBUtil 工具类把 URL、账号、密码放配置文件第三种是引入连接池比如 HikariCP适合后续想拿这个项目当工作作品集的读者。课程设计用第二种就够了。我最常给读者贴的 DBUtil 是这样import java.sql.*; public class DBUtil { // serverTimezoneMySQL 8 以后必须指定否则报时区错误 // characterEncodingutf8mb4保证中文写入和读出不乱码 // useSSLfalse本地开发关闭 SSL 握手减少连接耗时 private static final String URL jdbc:mysql://localhost:3306/student_db?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8mb4; private static final String USER root; private static final String PASSWORD 你自己的数据库密码; static { try { Class.forName(com.mysql.cj.jdbc.Driver); } catch (ClassNotFoundException e) { throw new ExceptionInInitializerError(MySQL 驱动未找到请检查 mysql-connector-java 的 jar 包); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } public static void close(Connection conn, Statement stmt, ResultSet rs) { if (rs ! null) { try { rs.close(); } catch (SQLException ignored) {} } if (stmt ! null) { try { stmt.close(); } catch (SQLException ignored) {} } if (conn ! null) { try { conn.close(); } catch (SQLException ignored) {} } } }这段逻辑说明一下static 块里写 Class.forName类加载时只执行一次驱动注册从 JDBC 4.0 起驱动可以自动注册但显式写出来能让答辩老师看到你知道这个步骤。serverTimezoneAsia/Shanghai 是 MySQL 8 连接常见的硬性要求不写大概率报 The server time zone value ... is unrecognized。关闭资源的顺序是反的先关 ResultSet再关 Statement最后关 Connection。try-with-resources 能自动处理但在手工 close 的早期代码里这个顺序经常被写错导致连接没释放。想升级到连接池时只需要把 getConnection() 换成从 HikariDataSource 获取业务代码不用动。连接池的两个核心参数是 maximumPoolSize 和 idleTimeout课程设计里给 10 个连接和 60 秒空闲回收就够不要照抄生产环境的 200 个连接过大的池子反而会让本机测试变慢。2.4 学生管理的最小增删改查JDBC 方法与自增主键回填建好表、封装好连接接下来就是增删改查。我以 StudentDAO 的核心插入方法为例public class StudentDAO { // 插入学生并回填自增主键 public int insert(Student s) { String sql INSERT INTO student(student_name, gender, class_name) VALUES(?, ?, ?); try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) { ps.setString(1, s.getName()); ps.setString(2, s.getGender()); ps.setString(3, s.getClassName()); int rows ps.executeUpdate(); try (ResultSet keys ps.getGeneratedKeys()) { if (keys.next()) { s.setId(keys.getInt(1)); } } return rows; } catch (SQLException e) { e.printStackTrace(); return 0; } } // 根据主键删除 public int deleteById(int id) { String sql DELETE FROM student WHERE student_id ?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setInt(1, id); return ps.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); return 0; } } }说明几个关键点prepareStatement(sql, Statement.RETURN_GENERATED_KEYS) 这行不能省。默认执行 INSERT 后拿不到自增 id只有在 prepareStatement 时声明要返回生成的键后面用 getGeneratedKeys() 才能读到。业务下单时常见做法是插入订单后立刻用这个 id 去插订单明细。所有用户输入都用 ? 占位符传参不要用字符串拼 SQL。这个习惯从第一个 DAO 方法开始就要养好否则成绩里有一个单引号比如名字“张三”SQL 直接语法错误更不要说注入风险。返回 int 表示影响行数插入成功返回 1删除失败返回 0业务层靠这个判断操作是否成功比返回 boolean 更能表达“影响了几行”。try-with-resources 会把两个资源自动关闭所以代码里不需要出现 DBUtil.close()简历里写“掌握 JDBC 资源管理”时也说得出口。到这里一个“学生表”的增删改查骨架已经通了。课程和成绩两张表套路完全一样只是字段换成课程名/学分、成绩/考试日期。真正开始写业务层和界面之前建议先把这个最小闭环跑通避免一口气写完几十个方法再调试满屏报错分不清是谁的问题。3. 把 DAO 层做成通用模板泛型、参数绑定、分页与事务学生、课程、成绩三张表的 DAO 写完后你会发现结构几乎一模一样都是拿连接、编译 SQL、填参、执行、映射结果、关资源。这时候就该抽公共层了。这一章讲我做 DAO 模板时最常用的四个写法以及实验报告里怎么把这些写明白。3.1 通用 DAO 抽取泛型、RowMapper 与可复用的查询方法抽取的核心是“把不变的流程放到父类把变化的部分留给子类”。查询部分最值得抽因为每个实体都要 listAll、getById。我一般是这样写一个 BaseDAOpublic abstract class BaseDAOT { // 子类实现把结果集的一行映射成实体 protected abstract T mapRow(ResultSet rs) throws SQLException; // 通用查询执行带参数的 SQL返回实体列表 protected ListT queryList(String sql, Object... args) { ListT list new ArrayList(); try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { for (int i 0; i args.length; i) { ps.setObject(i 1, args[i]); } try (ResultSet rs ps.executeQuery()) { while (rs.next()) { list.add(mapRow(rs)); } } } catch (SQLException e) { e.printStackTrace(); } return list; } }子类只需要处理一行数据的映射public class StudentDAO extends BaseDAOStudent { Override protected Student mapRow(ResultSet rs) throws SQLException { Student s new Student(); s.setId(rs.getInt(student_id)); s.setName(rs.getString(student_name)); s.setGender(rs.getString(gender)); s.setClassName(rs.getString(class_name)); return s; } public ListStudent listAll() { return queryList(SELECT * FROM student ORDER BY student_id); } public Student getById(int id) { ListStudent list queryList(SELECT * FROM student WHERE student_id ?, id); return list.isEmpty() ? null : list.get(0); } }这里多说一句泛型的作用BaseDAO 里的 T 在父类里只是一个占位符子类继承时填成 Student父类的通用方法就知道返回什么类型不需要写强制转换。如果你在原型里看到 (Student) rs.getObject(...) 这种强转说明设计上少抽了一层。Object... args 是可变参数调用时写 getById(id) 会自动包装成数组参数个数随便传父类统一从 1 开始 set兼顾了三个子类的不同参数数量。3.2 PreparedStatement 参数绑定为什么拼接 SQL 是给自己挖坑这个知识点同时也是 Java 面试题里很喜欢问的“PreparedStatement 和 Statement 的区别”。最表面的区别是 Statement 直接拼 SQLPreparedStatement 用占位符预编译。常见做法里课程设计默认用 PreparedStatement原因有两个第一是安全性。SQL 注入的本质是用户输入被当成 SQL 片段执行。假设登录查询写成了String sql SELECT * FROM student WHERE student_name name ;用户输入 王五 OR 11拼接后变成 WHERE student_name 王五 OR 11条件恒真整个表被查出来。这不只是安全笑话在成绩管理这种小系统里同样会发生因为学生信息录入时没人保证名字里没有特殊字符。第二是性能。MySQL 对 ? 占位符的 SQL 会做预编译缓存同一句 SQL 只要结构不变执行计划可以复用。但要注意这个缓存默认是关闭的想让缓存生效连接串要额外加jdbc:mysql://localhost:3306/student_db?cachePrepStmtstrueprepStmtCacheSize250不加这两个参数PreparedStatement 只是“安全”性能提升并没那么明显。这是我踩过的一个小坑也建议你在报告里写一句“连接串开启预编译缓存”答辩老师会认为你研究过参数。三种常用的参数设置方式对比方法适用类型说明setStringVARCHAR/CHAR最常用注意不要对数值字段用会产生隐式转换setInt / setLongINT/BIGINT数值参数避免字符串拼接setObject任意自动映射风险是类型不明确时可能走错 JDBC 类型3.3 分页查询LIMIT 的 offset 计算与页面参数传递成绩表数据不多时不分页也能跑但“分页”是数据库增删改查里的高频考点实验报告里写清楚能加分。MySQL 分页用 LIMIT offset, pageSize注意 offset 是从 0 开始的public ListScore listScoreByPage(int pageNum, int pageSize) { int offset (pageNum - 1) * pageSize; // 第 1 页 offset 0第 2 页 offset pageSize String sql SELECT * FROM score ORDER BY id LIMIT ?, ?; return queryList(sql, offset, pageSize); }三个容易出错的地方pageNum 从 1 开始还是从 0 开始前后端必须约定一致。常见做法是界面显示第 1 页传给 DAO 的也是 1换算公式写 offset (pageNum - 1) * pageSize。LIMIT ?, ? 两个参数都是数字如果误传字符串 “10” 也能隐式转换但建议都走 setInt类型明确。大数据量下偏移量越深查询越慢LIMIT 100000, 20 会先读取 100020 行再丢弃。课程设计里不用优化但报告里可以提一句“深分页可用游标代替 OFFSET”老师会觉得你视野不止于课程设计。3.4 批量录入成绩事务边界与回滚的完整代码“录入 40 个学生的期末成绩第 20 个没录入成功前 20 个也不应该入库”——这是一个事务场景。JDBC 默认每条语句自动提交这恰恰是问题所在。批量写入必须手动管理事务边界public void batchInsertScore(ListScore scores) { String sql INSERT INTO score(student_id, course_id, score, exam_date) VALUES(?, ?, ?, ?); try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { conn.setAutoCommit(false); // 关闭自动提交 for (Score s : scores) { ps.setInt(1, s.getStudentId()); ps.setInt(2, s.getCourseId()); ps.setBigDecimal(3, s.getScore()); ps.setDate(4, Date.valueOf(s.getExamDate())); // 假设 getExamDate() 返回 LocalDate ps.addBatch(); // 攒一批 } ps.executeBatch(); // 一次性发给 MySQL conn.commit(); // 全成功才提交 } catch (SQLException e) { try (Connection conn DBUtil.getConnection()) { conn.rollback(); // 任何一条失败回滚全部 } catch (SQLException ex) { ex.printStackTrace(); } e.printStackTrace(); } }注意两点第一catch 块里不能再拿原来的 conn 调 rollback因为 try-with-resources 在 catch 前已经关闭了连接常见做法是 catch 里重新拿连接回滚或者干脆把 Connection 拿出来手工管理生命周期。为了篇幅清晰上面用重拿连接的方式做演示实际项目我更推荐把 Connection 生命周期放到业务层控制DAO 只负责单语句执行。第二addBatch() 攒够一批后还要在连接串开启 rewriteBatchedStatementstrue否则 MySQL 驱动会傻傻地逐条发送性能提升非常有限。这是批量插入性能优化的关键参数很多人加了批处理却没加这个数据多了照样慢。3.5 实验报告结构把设计过程写成答辩素材标题里带着“实验报告”所以报告怎么写也得说清楚。常见做法是七个部分需求分析、数据库设计、总体设计、详细设计、测试用例、总结、参考资料。老师重点看的是数据库设计和测试用例这两块最容易编出内容。这里给一个可以直接套用的报告结构章节写什么要多少字需求分析角色管理员/学生功能增删改查、统计排名、导出300-500数据库设计三张表结构、ER 图说明、外键和唯一键的取舍400-600总体设计分层图ui → service → dao → db每层职责一句话300详细设计关键代码DBUtil、PreparedStatement、事务、分页800-1200测试用例正常录入、重名/重复录入、删学生级联删成绩、特殊字符表格 10 行左右总结碰到的问题 解决过程比堆功能有说服力200测试用例那个表格是很多人偷懒的地方我建议你认真写用例编号、输入、预期结果、实际结果、是否通过。比如“输入学生姓名 张三预期插入成功且能正常查询”直接对应到 PreparedStatement 参数绑定这个用例丢到答辩现场几乎没人能挑出毛病。4. 避坑指南驱动、编码、提交与连接泄漏的五个必查项这一章写我把类似系统从“能跑”到“真正交给老师验收”之间踩过的五个坑。每一条按“现象 → 原因 → 解决”的顺序展开跑不动的时候逐个对照。4.1 驱动类名和连接串版本不匹配ClassNotFound 只是开场白现象运行时报 ClassNotFoundException: com.mysql.jdbc.Driver或者换成 MySQL 8 后地址栏写了 jdbc:mysql://localhost:3306/student_db 却报 Communications link failure。原因MySQL 5.x 时代的驱动类是 com.mysql.jdbc.DriverMySQL 8 之后改成 com.mysql.cj.jdbc.Driver。很多教程是五年前写的你复制过来直接把旧类名放进了 Class.forName。另外老版本驱动连接 MySQL 8 的认证插件、时区都有兼容问题单改类名还不够。解决确认用的是 mysql-connector-java 8.x类名写 com.mysql.cj.jdbc.Driver连接串里补 serverTimezoneAsia/Shanghai如果用的是 5.1.x 的 jar 包建议直接升级别在一个课程设计里跟老驱动的时区问题死磕。最简单的验证方法是先看 jar 包里到底有没有 cj 目录有就是 8.x没有就是旧版。4.2 数据库、连接串、控制台三层字符集都要对齐乱码排查清单现象插入中文后通过命令行查询显示正常但 Swing 表格里是乱码反过来界面录入中文后存进数据库变成 ??。原因乱码不是玄学是三层编码没对齐。它由三处组成数据库默认字符集、JDBC 连接串的 characterEncoding、控制台/界面读取时的编码。三层里任何一层不同步都会在某一端看到乱码。解决按顺序检查先固定数据库端再固定连接端最后看代码端。数据库建库时用 utf8mb4连接串加 characterEncodingutf8mb4以及确保 Java 文件本身保存为 UTF-8IDEA 右下角编码显示。如果控制台 System.out 还乱码在运行配置里加 -Dfile.encodingUTF-8。注意 MySQL 的 utf8 是残缺版最多 3 字节表情符号存不进去直接用 utf8mb4 一步到位。4.3 数据“消失”自动提交与事务边界的经典误用现象程序执行插入没有任何异常但打开 Navicat 或者命令行 SELECT * FROM student 一看记录不在。原因JDBC 默认 autoCommittrue理论上每句 SQL 执行后会自动提交。出现“没报错但数据没有”的情况多半是前面写了 conn.setAutoCommit(false) 后忘了在 finally 里恢复或者 catch 块里没有 rollback 就把资源关了。这个事务状态会一直挂在连接上连接释放回池后下一次从这个池里拿同一个连接自动提交还是 false导致后续所有操作都不提交。解决在 finally 中恢复 autoCommit 是一种常见做法更稳妥的是事务逻辑单独封装代码如下conn.setAutoCommit(false); try { // 多条 SQL 执行 conn.commit(); } catch (SQLException e) { conn.rollback(); throw e; } finally { conn.setAutoCommit(true); // 归还连接前必须恢复 conn.close(); }如果你用了连接池setAutoCommit(true) 这行尤其不能省否则下一个使用者会“继承”一个不开自动提交的连接然后一脸懵地查不到刚插入的数据。这是个非常隐蔽的坑排查时先检查连接池配置里的 autoCommit 默认值。4.4 连接泄漏等待超过数据库连接数后系统直接卡死现象系统跑一段时间后点击查询按钮长时间转圈数据库出现 Too many connectionsNavicat 也连不上了。原因代码里打开了 Connection 但某个分支没有关闭。最常见的写法是Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql); ResultSet rs ps.executeQuery(); // 忘记在 finally 里关闭 conn连接是数据库资源MySQL 默认最大连接数 151JDBC 每打开一个没关闭的连接就占一个名额跑几十次查询连接就被占满新连接全部排队等待。解决把所有数据库访问改成 try-with-resourcesJava 7 之后的语法会保证三个资源按声明顺序自动 close。如果你依赖的旧代码大量手工 close另一个修复路径是用连接池的 connectionTimeout 参数但这个参数只能让等待变短真正治本还是补上关闭逻辑。经验是每个 getConnection() 都必须能在源码里找到对应的 close 点找不到就是在泄漏。4.5 成绩排序选了字符串字段结果出现 89 9 的“字典序”错觉现象按成绩列排序期望 89 排在 9 前面结果 9 排到了 89 前面肉眼看着完全错乱。原因如果把成绩列在数据库设计成了 VARCHAR或者在 Java 实体里用 String 保存分数排序时按字符一位一位比较9 大于 8所以 9 排在了 89 前。这是数据库设计和实体类型双重不当造成的。解决数据库端把成绩字段定义成 DECIMAL(5,2)Java 实体里用 BigDecimal 承接排序走 SQL 的 ORDER BY score DESC或者用 Collections.sort 时按 BigDecimal 比较。如果你遇到的坑是“用冒泡排序自己写排名字相同的学生”那要注意对象排序必须实现 Comparable 接口并重写 compareTo直接对 List 排会走默认字典序同样踩坑。常见做法是让实体类实现 Comparable只按业务字段排序不要依赖 String 的自然顺序。5. 进阶用一段 main 方法给 DAO 层做验收再顺手留出替换 MyBatis 的口子这个项目的最后一公里不是写完美而是“你能证明它没问题”。很多同学演示时只跑正常路径删一条不存在的数据、插入一个超长姓名谁都没试过结果答辩现场一输入边界值就报错。我现在的习惯是给 DAO 层写一段不依赖 JUnit 的验证代码放在 main 里跑一遍就等于把关键接口都测了public static void main(String[] args) { StudentDAO dao new StudentDAO(); Student s new Student(); s.setName(边界测试_李四); s.setGender(女); s.setClassName(计科2102); assert dao.insert(s) 1; // 插入必须成功 assert s.getId() 0; // 自增主键必须回填 assert dao.getById(s.getId()) ! null; // 能查回 assert dao.listAll().size() 1; // 列表不为空 assert dao.deleteById(s.getId()) 1; // 能删除 System.out.println(DAO 层验证通过); }这里用到了 assert运行时需要加 -ea 参数开启断言否则 assert 默认为禁用整段验证形同虚设。如果不想依赖运行参数直接把 assert 换成 if 抛出 RuntimeException 更省事。把这段代码和输出截图贴进实验报告的“测试用例”部分比手打十行测试用例更有说服力。另一个值得做的进阶是把 DAO 层与 JDBC 解耦。我一般会在原项目里把 insert/update/delete 的 SQL 集中到一个接口这样以后想换成 MyBatis 或 MyBatis-Plus只需把 DAO 实现替换掉service 层完全不动。MyBatis-Plus 能根据实体类生成建表 SQL但那种“根据实体类生成建表语句”的玩法靠的是实体注解和表名映射前提是实体字段命名规范studentName 默认映射 student_name下划线和驼峰互转是原生支持的。这块不用在课程设计阶段做但知道挂载点在哪面试聊“我从 JDBC 迁移到 MyBatis-Plus”时才有话讲。最后说一句我自己的教训曾经把系统交给老师验收前没跑删除用例现场点删除一条已被外键关联的学生记录整个界面卡住老师在旁边看着我只能赶紧切到数据库用命令把外键约束临时关掉。那之后我养成了“交付前先跑一遍边界输入”的习惯。数据库操作这种一锤子买卖的事情早点把验证代码写好比临时抱佛脚可靠得多。希望帮到你。本文还有配套的精品资源点击获取
返回列表