
简介数据库课程设计的关键不只是写代码而是交付一份能跑、能查、能答辩的完整系统。从关系型数据库的选型逻辑出发MySQL凭借免费、轻量、资料丰富成为默认选择但版本差异、驱动兼容和连接参数往往决定项目能否顺利启动。理解JDBC连接、PreparedStatement参数绑定以及连接池的配置是工程实践的基础能力。事务的边界控制与索引设计则直接体现对数据一致性和查询性能的把握。本文以图书管理系统为例梳理从E-R图设计、三大范式落地到并发避坑的完整路径帮助学生在答辩现场从容应对。1. 数据库课程设计.doc一份要能跑、能写、能答辩的完整交付拿到一份《数据库课程设计.doc》时大多数人的第一反应是打开模板往里面填需求分析和运行截图。但数据库课程设计这门课真正卡人的地方是老师会在验收现场点开你的系统、对着数据库查数据、随机挑一条 SQL 让你讲执行过程。这门课要求你把关系模型、范式、事务、索引这些抽象概念编译成一个能点开的界面再把全过程的证据沉淀进 doc 里。它适合正在赶课程设计的学生、带课助教以及在企业里想快速搭一套可演示增删改查系统的工程师。先想清楚交付物边界能跑的代码、能查的数据、能讲清的设计缺一个都算没做完。2. 选型先行数据库与开发栈怎么定才不会做到一半返工课程设计里最贵的时间开销不是写代码是中途换数据库。选型这一步花半小时想清楚后面能省出两天重写连接代码和建表脚本的时间。选型的核心原则只有一条以验收环境为准而不是以技术时髦度为准。下面拆成数据库、开发栈、连接参数三层讲。2.1 关系型数据库选型MySQL 是课程设计的默认答案常见做法是默认选 MySQL 5.7 或 8.0因为绝大多数高校机房和教师验收机器上都装了 MySQLNavicat、命令行工具随手可用遇到报错也总能搜到同款复现。课程设计的数据规模一般在 5 到 15 张表、几万条记录以内MySQL 免费、安装包小、远程连接方便性能绰绰有余。Oracle 不是不好而是安装重量级、内存占用大、验收机上未必有实例你用 Windows 连远程 Oracle 服务器时光配一个 SQL*Plus 客户端就得折腾半天这不是课程设计该花时间的地方。达梦、人大金仓这类国产数据库只有在题目明确指定时才碰。它们的思路和 MySQL 大体一致但驱动不通用、连接工具要单独配比如用 Navicat 连接达梦数据库时需要先下载达梦专属 JDBC 驱动这个步骤在答辩现场一旦卡住整个演示就没了。SQLite 单文件零配置适合个人工具但课程设计要求的多用户并发和事务演示SQLite 的锁粒度扛不住。选型的优先级应该是题目指定库 机房已装库 MySQL 8.0。数据库选型的另一个坑是版本区分。MySQL 8.0 把默认认证插件改成了 caching_sha2_password老一代驱动 5.1.x 连 8.0 会直接报认证失败反过来8.0 的驱动连 5.7 一般没问题。如果机房装 5.7驱动用 5.1.49如果装 8.0驱动用 8.0.33。这个对应关系在写进设计文档之前先确认一下你本机装的是哪个版本。数据库课程设计的优势容易踩的坑MySQL 5.7/8.0免费、资料多、默认端口 33068.0 认证插件与旧驱动不兼容Oracle企业级功能全面试常问安装重、验收环境难复现达梦/金仓国产化题目可能指定工具链不通用驱动需单独下载SQLite单文件、零配置并发写入弱事务演示效果差2.2 开发栈怎么选Swing、JSP 还是 Spring Boot界面层的选择直接决定了答辩现场能不能在一分钟内跑起来。常见路线有三条Java Swing 桌面端、JSP/Servlet 网页端、Spring Boot 加前端框架的前后端分离。我一般按年级和题目难度来定大二大三的数据库课程设计Swing 或 JSP 足够老师能在教室机器上快速部署毕业设计级别的综合系统才值得上 Spring Boot。Swing 的优势是零部署打了 jar 包双击就能运行不依赖 Tomcat也不碰 Maven 拉依赖的问题。缺点是界面确实旧但课程设计评分看的是功能完整度、数据库设计是否合理、并发和事务有没有处理界面只是加分项。JSP/Servlet 是经典课设形态在浏览器里演示更像一个系统但需要配置 Tomcat 和打包 war现场环境一旦缺 JDK 版本不对启动时报错很随机。Spring Boot 加 Vue 这类前后端分离技术含量最高但需要联网拉依赖、前端要 Node 环境答辩现场断网或者镜像源抽风项目连编译都过不了。还有一个决策项数据库访问层必须独立于界面层。无论选哪条路线都先把 DAO 层写好界面层的按钮只负责调用 DAO 方法。这样万一选题阶段拿不准界面技术数据库表结构和 DAO 代码完全不用返工后端代码可以无缝切到另一个界面框架。这算课程设计里的一个后悔药设计把变的部分和不变的部分切开。2.3 字符集、驱动与连接串第一轮就要定死的三个参数选型阶段就应该把三个字符串参数钉在设计文档里后面所有连接代码都从文档抄而不是每次重新凭记忆写。第一个是字符集库、表、连接串三处统一用 utf8mb4第二个是 JDBC 驱动 jar 和驱动类名对应 2.1 的 MySQL 版本第三个是连接串里的附属参数。参数推荐值作用characterEncodingutf8mb4解决中文存储乱码serverTimezoneAsia/ShanghaiMySQL 8.0 连接时必加否则时区报错useSSLfalse本地演示关闭 SSL 握手减少警告与延迟useUnicodetrue与 characterEncoding 配合生效String url jdbc:mysql://localhost:3306/book_db ?useUnicodetrue characterEncodingutf8mb4 serverTimezoneAsia/Shanghai useSSLfalse; String user root; String password 123456; Connection conn DriverManager.getConnection(url, user, password);这段代码里的四个参数逐个说useUnicode 和 characterEncoding 是成对出现的只写一个有时不生效这是中文乱码的第一道防线serverTimezone 在 MySQL 8.0 下必须显式指定否则驱动拿系统时区和服务器时区对比时会抛 SQLExceptionuseSSLfalse 在本地演示只是为了省去证书握手那一层无意义的开销。这些参数不属于业务代码写一次就不动它。需要一并定死的是驱动类名。MySQL 5.7 配 mysql-connector-java 5.1.49驱动类名用 com.mysql.jdbc.DriverMySQL 8.0 配 8.0.33驱动类名用 com.mysql.cj.jdbc.Driver。类名写错是课程设计里最高频的启动报错之一后面避坑章节会专门讲现场。3. 把需求拆成库表E-R 设计与三大范式在课程设计里的落地课程设计的题目描述往往只有一句话比如设计一个图书管理系统实现读者管理、图书管理和借还书功能。这一句话里藏着实体、联系、约束和业务规则。数据库设计部分的得分点全看你有没有能力把这句话翻译成规范的表结构。3.1 从题目到实体先画 E-R 图再写建表语句以图书管理系统为例。先列自然语言里的名词读者、图书、借阅记录、管理员。再圈出业务动词读者借书、读者还书、管理员维护图书和读者信息。可以确定的实体有三个读者、图书、借阅记录。管理员在课程设计级别通常可以并入借阅记录的操作字段或者单独建一张极简管理员表我一般建议先不做权限把篇幅留给借阅核心流程答辩时再说明权限部分可以通过字段扩展。借阅记录是读者和图书之间多对多联系的载体在关系模式里体现为第三个表。三段关系模式如下读者读者ID读者编号姓名读者类型联系电话图书图书IDISBN书名作者出版社价格库存数量借阅记录记录ID读者ID图书ID借书日期应还日期实际还书日期E-R 图要画进 Word 文档里但不能只画一个矩形加连线就完事。要在每个实体旁标出主键和关键属性在联系上标出基数1:n 还是 m:n这是老师看设计文档时最先扫的位置。关系模式转成建表语句后再回过去检查一句话每个表的主外键在 E-R 图里是否能对应上。对应不上说明前面的分析漏了实体或联系这时候改成本最低。3.2 字段类型与约束主外键、唯一键、默认值怎么给字段类型是数据库课程设计里最容易被扣分也最好补的环节。常见误用是把所有字符字段都建成 VARCHAR(255)把学号、ISBN 这些业务编号建成自增数字。学号和借书证号本质上不是数字不需要参与运算用 VARCHAR(20) 加唯一索引更合理ISBN 固定 13 位用 CHAR(13) 或者 VARCHAR(20) 都行金额用 DECIMAL(10,2)不能用 FLOAT浮点算金额会出精度问题日期统一用 DATE不要用 VARCHAR否则日期区间查询和逾期天数的计算都会非常别扭。业务字段类型推荐类型理由学号 / 证号VARCHAR(20)不参与算术运算业务编号不应自增ISBNCHAR(13) / VARCHAR(20)定长标识但需兼容带连字符的写法价格DECIMAL(10,2)避免浮点精度误差日期DATE支持区间查询可算日期差状态标记TINYINT比字符串省空间可扩展枚举CREATE TABLE reader ( reader_id BIGINT AUTO_INCREMENT COMMENT 读者ID表内主键, reader_no VARCHAR(20) NOT NULL COMMENT 学号或借书证号, reader_name VARCHAR(50) NOT NULL COMMENT 姓名, reader_type TINYINT DEFAULT 1 COMMENT 1学生 2教师, phone VARCHAR(20) DEFAULT NULL, PRIMARY KEY (reader_id), UNIQUE KEY uk_reader_no (reader_no) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT读者表; CREATE TABLE borrow ( borrow_id BIGINT AUTO_INCREMENT, reader_id BIGINT NOT NULL, book_id BIGINT NOT NULL, borrow_date DATE NOT NULL, due_date DATE NOT NULL, return_date DATE DEFAULT NULL, PRIMARY KEY (borrow_id), KEY idx_reader (reader_id), KEY idx_book (book_id), CONSTRAINT fk_borrow_reader FOREIGN KEY (reader_id) REFERENCES reader (reader_id), CONSTRAINT fk_borrow_book FOREIGN KEY (book_id) REFERENCES book (book_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT借阅记录表;上面两张表的参数说明要讲清楚reader_id 设计成自增主键是表内部的代理键对外不可见读者编号 reader_no 加唯一索引保证同一个学号不能被插入两次这是唯一键和主键的分工。borrow 表的外键约束指向 reader 和 book 的主键保证借阅记录不会指向不存在的读者或图书。引擎选 InnoDB 而不是 MyISAM是为了支持外键约束和后面第 4 章要讲的事务。字符集统一写在建表语句里避免继承库级默认值带来乱码。3.3 索引设计每个表的高频查询路径索引不是装饰要体现为什么建这个索引。课程设计里的索引一般来自三个查询需求按读者编号查读者信息、按 ISBN 查图书、按借阅记录查某个读者的全部借阅历史。前两个用唯一索引或普通索引覆盖第三个是高频查询借阅表要为外键列建索引否则每次查询都走全表扫描。以借阅记录表为例reader_id 和 book_id 是两个外键列建表语句里的 KEY idx_reader 和 KEY idx_book 就是给它们建普通索引。理由是外键约束本身不会自动建索引InnoDB 在更新父表或删除父表时要在子表上按外键列查找对应记录没有索引就会全表扫既慢又容易触发死锁。借阅历史查询的典型 SQL 是 WHERE reader_id ?这个条件命中 idx_reader访问类型能从全表扫描变成索引查找。建议在文档里留一张小表说明每个索引服务的查询场景主键索引服务按 reader_id 的等值查找唯一索引 uk_reader_no 服务按学号的登录校验普通索引 idx_reader 服务借阅历史列表。答辩问为什么给这个列加索引时答因为这个列出现在 WHERE 高频查询条件里比答老师说要建索引要扎实得多。同时注意不是每个列都要建索引写操作频繁的列索引过多会拖慢插入和更新课程设计规模下每个表最多 2 到 3 个索引就足够了。3.4 从三范式到反范式课程设计该守到什么程度老师爱问的一句话是你的表满足第几范式。默认答案是好设计按第三范式来但要能解释清楚每个表为什么满足也要能说清哪里做了取舍。第三范式的要求是非主属性不依赖其他非主属性落到借阅系统里就是借阅记录表里只存 reader_id不存读者姓名只存 book_id不存书名。查询时需要姓名和书名通过 JOIN 去读者表和图书表取这是标准做法。有些课程设计为了页面展示方便在借阅记录表里冗余了书名和读者姓名这就是反范式设计。反范式不是错但要有代价意识如果读者改名字所有历史借阅记录里的冗余姓名都要同步改漏掉一条就出现数据不一致。我的判断标准很简单如果冗余能减少一次高频 JOIN且同步更新逻辑在代码里能写到同一个事务内可以冗余如果只是为了少写一行 JOIN 语句不值得。答辩时主动说借阅表里没有冗余书名因为这个查询是等值 JOIN成本可控比无脑反范式更能体现设计意识。4. 代码连库从 JDBC 到连接池的最小可运行路径功能代码千差万别连接数据库的路径只有几条。课程设计里最常见的卡点是不管 MySQL 还是别的库连接都跑不通。这一章把从裸 JDBC 到连接池的路径完整走一遍代码可以直接抄进 DAO 层。4.1 JDBC 六步DriverManager 时代的最小连接代码JDBC 连接数据库的六步是固定的加载驱动、获取连接、创建语句、执行 SQL、处理结果、关闭资源。第一步和最后一步最容易丢。加载驱动的经典写法是 Class.forName虽然 JDBC 4 之后驱动 jar 可以自动注册但课程设计里保留这一行能第一时间发现驱动缺失报错也直观。public ListReader queryByReaderNo(String readerNo) { String url jdbc:mysql://localhost:3306/book_db ?useUnicodetruecharacterEncodingutf8mb4 serverTimezoneAsia/ShanghaiuseSSLfalse; String sql SELECT reader_id, reader_no, reader_name FROM reader WHERE reader_no ?; try (Connection conn DriverManager.getConnection(url, root, 123456); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, readerNo); try (ResultSet rs ps.executeQuery()) { ListReader list new ArrayList(); while (rs.next()) { Reader r new Reader(); r.setReaderId(rs.getLong(reader_id)); r.setReaderNo(rs.getString(reader_no)); r.setReaderName(rs.getString(reader_name)); list.add(r); } return list; } } catch (SQLException e) { e.printStackTrace(); return Collections.emptyList(); } }这段代码用 try-with-resources 自动关闭 Connection、PreparedStatement 和 ResultSet注意关闭顺序是 ResultSet 先关然后是 Statement最后是 Connection。如果不用 try-with-resources 而手动关闭必须在 finally 里按这个反序写否则先关连接再关结果集会报错。参数占位符 ? 从 1 开始编号ps.setString 的第一个参数是占位符下标不是列序号这是新手最容易搞混的地方。裸 JDBC 的问题在于每次 DriverManager.getConnection 都建立一条物理连接课程设计单人演示完全够用但答辩老师很可能追问100 个人同时借书会不会崩。这个问题用连接池回答代码改动只有一行。4.2 用连接池改造Druid 的配置与三个必调参数连接池的常见选择是 Druid 或 HikariCP。课程设计里 Druid 更常见因为它的监控页面可以额外当展示点。引入依赖后写一个 druid.properties 配置文件再在 DAO 层用 DruidDataSource 初始化一次之后所有方法都从 dataSource.getConnection() 拿连接资源复用后性能表现完全不同。jdbc.driverClassNamecom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/book_db?useUnicodetruecharacterEncodingutf8mb4serverTimezoneAsia/ShanghaiuseSSLfalse jdbc.usernameroot jdbc.password123456 initialSize5 maxActive20 maxWait5000 poolPreparedStatementstrue最需要调的是三个参数。initialSize 是启动时预建的连接数设 5 就够课程设计用maxActive 是最大活跃连接数设 20 意味着同时最多 20 个连接在工作超过就排队maxWait 是拿不到连接时的等待毫秒数设 5000 表示等待 5 秒后抛异常。maxWait 太短演示时稍微慢一点就报获取连接超时太长程序看起来像卡死。poolPreparedStatements 开启后 PreparedStatement 被缓存复用增删改查频繁的场景能明显降低解析开销。Properties props new Properties(); try (InputStream in Files.newInputStream(Paths.get(druid.properties))) { props.load(in); } DataSource dataSource DruidDataSourceFactory.createDataSource(props); Connection conn dataSource.getConnection();从裸 JDBC 切换到连接池DAO 层只需要把获取连接的一行换掉原来是 DriverManager.getConnection(url, user, pwd)现在是 dataSource.getConnection()。sql 语句和结果集处理完全不变。这个切换过程在课程设计文档的系统设计章节写一句使用数据库连接池复用连接避免频繁建立物理连接比堆一页概念更能说明你理解了连接管理的意义。4.3 增删改查闭环PreparedStatement 与结果集映射增删改查四个操作里查询用 executeQuery 返回 ResultSet插入、更新、删除都用 executeUpdate 返回受影响行数。课程设计里最容易忽略的不是 SQL 写法而是对返回值的处理插入图书后应该判断返回值是否为 1是 0 说明没插入成功这时候还继续往下走就会产生逻辑缺口。public boolean addBook(Book book) { String sql INSERT INTO book (isbn, book_name, author, price, stock) VALUES (?, ?, ?, ?, ?); try (Connection conn dataSource.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, book.getIsbn()); ps.setString(2, book.getBookName()); ps.setString(3, book.getAuthor()); ps.setBigDecimal(4, book.getPrice()); ps.setInt(5, book.getStock()); return ps.executeUpdate() 1; } catch (SQLException e) { e.printStackTrace(); return false; } }参数说明setBigDecimal 对应数据库里的 DECIMAL(10,2)用 BigDecimal 而不是 double 才能保住精度setInt 对应 INT 和 BIGINT 都可以日期字段用 setObject 传 LocalDate 配合 JDBC 4.2避免手动转 java.sql.Date 的麻烦。PreparedStatement 用 ? 占位符绑定参数而不是拼字符串这是防止 SQL 注入的基本要求课程设计里必须用这种写法而不是 Statement 拼 SQL。使用连接池后有一个新坑ResultSet 和 Statement 不关闭的话连接不会真正归还给池时间一长 maxActive 会被占满后续请求全部超时。裸 JDBC 时代连接泄漏的后果是进程连带崩溃连接池时代只是慢慢变慢更难发现。所以 DAO 里必须保证每一个连接都在 finally 或 try-with-resources 中被释放。4.4 事务边界借书操作的两个 SQL 必须一起成功借书操作在数据库层包含两步往借阅记录表插入一条记录再把图书表的库存减一。这两步要么都成功要么都失败否则会出现借阅记录存在但库存没减或反过来库存减了但查不到借阅记录的数据不一致。事务的边界就画在两步之间。public boolean borrowBook(long readerId, long bookId) { String insertBorrow INSERT INTO borrow (reader_id, book_id, borrow_date, due_date) VALUES (?, ?, CURDATE(), DATE_ADD(CURDATE(), INTERVAL 30 DAY)); String updateStock UPDATE book SET stock stock - 1 WHERE book_id ? AND stock 0; try (Connection conn dataSource.getConnection()) { conn.setAutoCommit(false); try (PreparedStatement ps1 conn.prepareStatement(insertBorrow)) { ps1.setLong(1, readerId); ps1.setLong(2, bookId); ps1.executeUpdate(); } try (PreparedStatement ps2 conn.prepareStatement(updateStock)) { ps2.setLong(1, bookId); int rows ps2.executeUpdate(); if (rows 0) { throw new SQLException(库存不足或图书不存在); } } conn.commit(); return true; } catch (SQLException e) { try { conn.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } e.printStackTrace(); return false; } finally { try { conn.setAutoCommit(true); } catch (SQLException e) { e.printStackTrace(); } } }这段代码的逻辑说明setAutoCommit(false) 之后当前连接上的所有 SQL 都不会自动提交直到显式调用 commit 才一并生效任何一个环节抛异常进入 catch 分支执行 rollback把前面插入的记录和库存更新一起撤销finally 里把 autoCommit 复位成 true是因为连接要归还给连接池下一个使用者不知道这个连接的 commit 模式已被改过不复位会引发难以追踪的 bug。第三个细节是 updateStock 里加了 AND stock 0 和受影响行数判断。这是把库存不足的业务规则下沉到 SQL 层比先查库存再更新的方式更安全因为两个操作之间不会被并发插入打破。课程设计把这两行写出来事务和并发意识就都有了。另外注意事务里不要夹杂 UI 弹窗或耗时操作事务时间越长持有锁的时间越长第 5 章的卡死现场就是这么来的。5. 数据库课程设计避坑清单从连不上库到死锁的五个现场这一章的五条记录全部来自课设验收前的高频现场按现象、原因、解决的节奏排查可以省去在教室当众翻车的尴尬。5.1 现象启动报“No suitable driver”或“ClassNotFoundException”现象是程序一启动控制台抛 java.sql.SQLException: No suitable driver found for jdbc:mysql://localhost:3306/...或者直接 ClassNotFoundException。原因一般是三个驱动 jar 没有放进 classpathMySQL 版本换了但驱动 jar 没换驱动类名写错5.x 的 com.mysql.jdbc.Driver 和 8.x 的 com.mysql.cj.jdbc.Driver 混用。解决时先确认三件事jar 是否在构建路径里用 jar tf 检查驱动包内有没有对应的 Driver 类连接串是否以 jdbc:mysql:// 开头类名是否和驱动包版本匹配。Web 项目里还要确认 jar 放在 WEB-INF/lib 而不是只在 IDE 的 classpath 里Tomcat 运行时只看前者。5.2 现象中文写入变成问号现象是界面输入中文保存后数据库里看到的是 ? 或乱码。原因几乎都是三层字符集不一致库默认字符集是 latin1表继承了 latin1连接串没带 characterEncoding。解决是从上到下统一成 utf8mb4。建库时用 CREATE DATABASE book_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci建表语句里的 CHARSETutf8mb4 也不能省连接串第 2 章里已经加过 characterEncodingutf8mb4。这里有个血泪教训用 ALTER TABLE ... CHARSETutf8mb4 之后旧数据如果是已经按 latin1 存储的乱码并不会自动恢复最干净的做法是导出数据、重建库表、再导入。所以最好在设计文档的第一步就定死字符集后面不改。5.3 现象两个窗口操作同一张表程序卡死现象是界面点保存后一直转圈数据库命令窗口执行同一条 UPDATE 也卡住。原因大多是某个事务开启了却没提交行锁被第一个连接占着另一个更隐蔽的原因是两个事务按相反顺序更新同一批数据互相等对方的锁形成死锁。解决办法分三步先在命令行查 SHOW ENGINE INNODB STATUS 看最后一段 LOCK WAIT 或 DEADLOCK 信息代码里给事务设 innodb_lock_wait_timeout5超过 5 秒直接超时报错至少能让程序弹错而不是假死最后从根上改代码把多张表的更新顺序固定下来比如先更新 book 表再更新借阅表全项目统一这个顺序交叉等待就不会出现。课程设计里最常见的是在一个长方法开头开了事务中间弹了对话框等用户输入事务一直不提交锁就这么一直悬着。5.4 现象SQL 在客户端能跑通程序查不出数据现象是同样的 WHERE 条件在 Navicat 里查得到数据程序运行结果却是空列表。原因最常见的是连的不是同一个库连接串里的库名写错、大小写不一致或者本机有多个 MySQL 实例其次是 autocommit 被关掉后插入没提交事务里的查询看不到未提交数据还有一种坑是查询条件里字符串和数字类型不匹配比如把 VARCHAR 条件数字当数字传MySQL 会做隐式转换索引失效但不报错。解决时先打印 dataSource.getConnection().getCatalog() 确认当前连的是哪个库然后把插入方法的事务提交改为自动提交验证一次最后删掉 WHERE 条件的分号或者换参数类型。这类问题不报错全靠对照检查。5.5 现象答辩时发现 doc 里的表结构和代码不一致现象是文档里 E-R 图有三个表代码里实际建了四个文档里 book 表有 stock 字段数据库里没有。原因很直白开发时为了加需求改了表文档没同步毕竟没人喜欢返工写文档。解决办法是在交稿前做一次从零复现删掉整个库按 doc 里的建表语句重建再用 doc 里的步骤运行程序。如果程序在这一步跑不通那就说明文档和代码对不上提交前还有机会改如果跑通了这份文档就真的能指导另一个人部署。这是课程设计里唯一靠谱的后悔药能在发现问题时永远来得及。6. 答辩前必查验证清单、性能演示与文档闭环6.1 五类必测用例答辩前至少用五类用例把系统过一遍正常增删改查、重复主键插入、空值提交、超长字符串输入、并发借书。每一类对应一个扣分点正常路径保证能演示完所有功能重复主键验证唯一约束和异常处理空值验证 NOT NULL 约束和页面校验超长字符串验证字段长度是否与表结构一致并发借书在开两个客户端窗口同时借同一本书时验证库存不会变负数。用例操作期望结果正常新增添加一本图书列表刷新后出现新记录重复主键插入相同 reader_no抛出唯一键冲突并被提示空值不填姓名保存被 NOT NULL 或页面校验拦截并发借书两个窗口借同一本书库存不为负借阅记录各一条6.2 性能演示用一万条数据证明索引不是装饰课程设计数据量小索引效果不明显。答辩前可以造一万条测试数据用存储过程循环插入再执行 EXPLAIN SELECT * FROM borrow WHERE reader_id 10看访问类型和扫描行数。有索引时是 ref扫描行数是个位数去掉索引后是全表扫描扫描行数是全表条数。这一条对比图放进 doc 的数据库优化部分比写一百字关于索引原理的文字更有说服力。6.3 文档闭环数据字典、E-R 图和代码注释对得上交稿前的最后动作是数据字典与 SHOW CREATE TABLE 输出逐行比对E-R 图实体属性与 DAO 层的 VO 字段逐个核对。我自己的习惯是提交前把电脑借给同学让他只照着 doc 从零部署一次能跑通才提交。这门课真正收获不是把系统交上去而是让另一个人能顺着文档把系统完整跑起来。希望帮到你。本文还有配套的精品资源点击获取