简介:面向高校数据库课程设计场景的学生宿舍管理系统完整实践资料包,适合正在完成数据库课程设计、需要参考建表思路与前后端功能实现的学生使用。资源围绕寝室长与宿管员两类角色展开,涵盖登录身份判断、寝室人员信息查看、报修提交与处理、学生住宿信息的按学号检索与增删、学生账号管理以及密码修改等模块,并给出寝室长、宿管员、报修、学生信息四张核心表单的字段设计,可作为需求分析与数据库建模的对照样本。压缩包共3个文件,包含1个rar源代码包、1个sql建库建表脚本和1个docx文档模板,整体约2.96MB,sql文件可直接导入数据库验证表结构与初始数据,Word模板可支撑课程设计报告的撰写。目前已有5057人学习下载,便于读者快速搭建可运行系统、理解权限划分逻辑并补齐设计文档,减少从零摸索的时间。
1. 答辩前一晚,老师只问了一句:学生退宿后床位记录为什么还在
去年这个时候,某高校的一位同学抱着笔记本来找我,系统能跑,增删改查都正常,界面也做得挺像回事。老师翻了两页报告,问了一句:“学生退宿之后,床位记录为什么还留着?”他当场卡住。这类学生宿舍管理系统课程设计,代码量其实不大,真正拉开分差的全在数据库那一层——表之间怎么约束、调宿怎么保证不会出现一张床住两个人、删学生时关联记录怎么处理、报修工单状态怎么流转。
这份资源包给的是三样东西:一份能直接执行的 sql 建表脚本、一套可运行的 Java Swing + MySQL 源代码、一份 Word 课程设计报告模板。功能覆盖宿舍楼、房间、床位、学生、入住记录、报修、访客、管理员八块。适合正在做数据库课程设计、想要一份能跑又能讲明白的底稿的同学,也适合代码写完但报告一个字憋不出来的人。下面按我实际拆这套代码的顺序讲,从目录结构一路讲到怎么改。
2. 骨架拆解:从 sql 脚本到三层代码的目录与依赖
拿到一份课程设计源码,我一般不看界面,先看目录和建表脚本。界面好看是加分项,表设计错了整个系统就是沙上建塔,改一处塌一片。这一章先把资源包的物理结构、数据库设计和代码分层讲透,后面两章才有东西可谈。
2.1 资源包目录结构与技术栈
解压之后目录大致是这样,我按真实项目习惯补了注释:
DormSystem/ ├── sql/ │ ├── 01_schema.sql -- 建库、建表、外键、索引,先执行这个 │ └── 02_data.sql -- 初始数据:管理员账号、几栋楼、若干房间床位 ├── src/ │ └── com/demo/dorm/ │ ├── Main.java -- 程序入口,启动登录窗体 │ ├── entity/ -- 实体类,和表一一对应 │ ├── dao/ -- 数据访问层,只写 SQL 和结果集映射 │ ├── service/ -- 业务层,事务、校验、状态流转都在这 │ ├── ui/ -- Swing 窗体,只管展示和收参数 │ └── util/ │ ├── DBUtil.java -- 连接池/连接获取,全项目唯一入口 │ └── ExcelUtil.java -- 统计结果导出 ├── lib/ │ └── mysql-connector-j-8.0.33.jar ├── doc/ │ └── 课程设计报告模板.docx └── README.md -- 环境版本、执行顺序、默认账号技术栈不复杂:JDK 8 或以上、MySQL 8.0、JDBC 直连、Swing 做界面,没上 Spring 那一套。有人会问为什么不用框架——课程设计的评分点是数据库设计能力和 SQL 编写能力,套上 ORM 之后 SQL 全被框架藏起来,答辩时老师让你现场写一个多表连接查询,反而写不出来。我一般也建议这类作业保持原生 JDBC,把PreparedStatement、事务、连接释放这几件事自己写一遍,比背注解有用。
需要引的外部依赖只有 MySQL 驱动一个 jar。如果你用 IDEA,把lib目录右键 Add as Library 就行;用 Eclipse 就加到 Build Path 里。别去 Maven 中央仓库拉最新版驱动,8.0.33 这个版本和 MySQL 8.0 服务端匹配得比较稳,新手换版本最容易在第一晚就翻车。
2.2 建表脚本:八张表怎么划出来
建表脚本是整个资源包里最值钱的部分,报告里的 E-R 图、表结构说明、数据字典全靠它。八张表的关系链是:宿舍楼包含房间,房间包含床位,学生通过入住记录占用床位,报修和访客分别挂在学生和房间上。先看楼、房、床这三张:
-- 宿舍楼:最顶层,先有楼才有房间 CREATE TABLE building ( building_id INT AUTO_INCREMENT PRIMARY KEY, building_name VARCHAR(32) NOT NULL, -- 如"1号楼",不写真实校名 floor_count TINYINT NOT NULL DEFAULT 6, manager VARCHAR(32), -- 楼管姓名 remark VARCHAR(128) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 房间:挂在楼下,一栋楼多个房间 CREATE TABLE room ( room_id INT AUTO_INCREMENT PRIMARY KEY, building_id INT NOT NULL, room_no VARCHAR(16) NOT NULL, -- 如 301 capacity TINYINT NOT NULL DEFAULT 4, -- 可住人数 gender_limit TINYINT NOT NULL DEFAULT 0, -- 0 不限 1 男 2 女 CONSTRAINT fk_room_building FOREIGN KEY (building_id) REFERENCES building(building_id), UNIQUE KEY uk_building_room (building_id, room_no) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 床位:真正被"占用"的对象,调宿的本质是换床位 CREATE TABLE bed ( bed_id INT AUTO_INCREMENT PRIMARY KEY, room_id INT NOT NULL, bed_no VARCHAR(8) NOT NULL, -- 房间内编号 1/2/3/4 status TINYINT NOT NULL DEFAULT 0, -- 0 空闲 1 已住 2 维修 CONSTRAINT fk_bed_room FOREIGN KEY (room_id) REFERENCES room(room_id), UNIQUE KEY uk_room_bed (room_id, bed_no) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;这三张表的关键设计点在bed上。很多同学的作业把“住几个人”直接写在room表里用一个current_num字段记,看着简单,一调宿就出错——两个人同时退宿,current_num减两次变成负数。把床位单独抽成一张表,每一行代表一张实实在在的床,谁住哪张床一目了然,统计入住率也只是count(status=1) / count(*),报告里画 E-R 图时层次也更清楚。
再看学生和入住记录这两张,它们是整个系统的业务核心:
-- 学生:只存基本信息,不存房间号 CREATE TABLE student ( student_id VARCHAR(20) PRIMARY KEY, -- 学号做主键,业务上唯一 name VARCHAR(32) NOT NULL, gender TINYINT NOT NULL, -- 1 男 2 女 college VARCHAR(64), -- 学院,报告里用通用名称 phone VARCHAR(20), status TINYINT NOT NULL DEFAULT 1 -- 1 在校 0 已离校 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 入住记录:学生和床位的历史关系表,退宿不删记录只写结束时间 CREATE TABLE checkin ( checkin_id INT AUTO_INCREMENT PRIMARY KEY, student_id VARCHAR(20) NOT NULL, bed_id INT NOT NULL, start_date DATE NOT NULL, end_date DATE DEFAULT NULL, -- 为空表示当前仍在住 operator VARCHAR(32), -- 办理人,审计用 CONSTRAINT fk_checkin_student FOREIGN KEY (student_id) REFERENCES student(student_id), CONSTRAINT fk_checkin_bed FOREIGN KEY (bed_id) REFERENCES bed(bed_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;checkin表用end_date是否为空来区分“在住”和“已退宿”,这个设计直接决定了第 1 章那个问题的答案:退宿不是删记录,是把end_date填上、把床位status改回 0。历史记录保留下来,以后查“这个学生大二住哪间”还能查得到,报告里写“数据不做物理删除,保留历史轨迹”这句话,老师通常会点头。
提示:
student_id用学号做主键而不是自增 ID,答辩时容易被追问。理由要讲清楚——学号在业务上天然唯一且稳定,用它做主键能让checkin表少一次关联查询;如果学校存在学号复用的情况,那就老老实实加自增主键、把学号设为唯一索引。
剩下的报修、访客、管理员三张表结构类似,repair表要带status状态字段和create_time、finish_time两个时间戳,visitor表要带visit_time和leave_time,admin表存账号密码和角色。建表脚本末尾记得加索引:
-- 高频查询走索引:按学生查入住历史、按状态查待处理报修 CREATE INDEX idx_checkin_student ON checkin(student_id); CREATE INDEX idx_repair_status ON repair(status); CREATE INDEX idx_bed_status ON bed(status);索引不用多,这三个是实际查询里一定会用到的。写报告的数据字典章节时,把每张表的字段类型、是否为空、默认值、说明列成表,评分表里“表结构设计”那一项基本就稳了。
2.3 DAO/Service/UI 三层与登录会话
代码分层的意义在课程设计里经常被忽略,很多人一个MainFrame.java写到两千行,所有 SQL 都塞在按钮事件里。这套代码分了三层,虽然简单但边界清晰:ui只负责收参数和弹窗,service负责校验和事务,dao只负责执行 SQL。
DBUtil是全项目唯一的连接入口,我一般会把它写得稍微讲究一点:
public class DBUtil { // 连接串三个参数都不能省,后面避坑章节会讲为什么 private static final String URL = "jdbc:mysql://127.0.0.1:3306/dorm_db" + "?useUnicode=true&characterEncoding=utf8" + "&serverTimezone=Asia/Shanghai&useSSL=false"; private static final String USER = "root"; private static final String PWD = "123456"; static { try { Class.forName("com.mysql.cj.jdbc.Driver"); // 8.x 驱动的类名,别写成 5.x 的 } catch (ClassNotFoundException e) { throw new RuntimeException("驱动没加载上,检查 lib 目录", e); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PWD); } // 关闭顺序必须和打开相反,否则连接池会泄漏 public static void close(Connection c, Statement s, ResultSet r) { try { if (r != null) r.close(); } catch (SQLException ignored) {} try { if (s != null) s.close(); } catch (SQLException ignored) {} try { if (c != null) c.close(); } catch (SQLException ignored) {} } }参数说明:characterEncoding=utf8解决中文写入变问号,serverTimezone=Asia/Shanghai解决 8.0 驱动启动即报时区异常,useSSL=false是本地开发省掉证书校验。这三项少一个,第一天就跑不起来,我见过不止一个同学在这卡三小时。
DAO 层统一用PreparedStatement,一是防注入,二是带参数时不用自己拼引号:
public List<Bed> listFreeBeds(Connection conn, int buildingId) throws SQLException { String sql = "SELECT b.bed_id, b.bed_no, r.room_no, bl.building_name " + "FROM bed b " + "JOIN room r ON b.room_id = r.room_id " + "JOIN building bl ON r.building_id = bl.building_id " + "WHERE b.status = 0 AND r.building_id = ? " + "ORDER BY r.room_no, b.bed_no"; List<Bed> list = new ArrayList<>(); try (PreparedStatement ps = conn.prepareStatement(sql)) { ps.setInt(1, buildingId); // 参数下标从 1 开始,不是 0 try (ResultSet rs = ps.executeQuery()) { while (rs.next()) { Bed bed = new Bed(); bed.setBedId(rs.getInt("bed_id")); bed.setBedNo(rs.getString("bed_no")); bed.setRoomNo(rs.getString("room_no")); bed.setBuildingName(rs.getString("building_name")); list.add(bed); } } } return list; }这段查询是三表连接,报告里“多表查询”章节可以直接引用。注意try-with-resources里传进去的conn不会被关闭,它由调用方service层统一管理生命周期,这是分层之后必须守住的约定——DAO 不许关别人传进来的连接,否则外层事务直接就断了。
登录这块用一个Session静态类存当前用户,登录成功后写入adminId和role,主窗体根据role决定菜单项是否可见。简单但够用,比把用户名塞进System.setProperty之类的野路子干净得多。
3. 核心模块实战:调宿事务、报修流转与统计查询
界面能点通不算本事,课程设计真正会被追问的是三个地方:调宿会不会把数据搞乱、报修状态能不能乱跳、统计数字对不对。这一章按业务链路拆三段代码,每一段都能直接抄进你的作业里,也能直接对应到报告的功能实现章节。
3.1 宿舍调换:一个必须走事务的操作
调宿看起来只是“换个房间”,实际上要动三张表:旧入住记录写结束日期、新入住记录插入、两个床位的状态互转。这四步任何一步失败都会留下脏数据,所以必须放在一个事务里。更麻烦的是并发——两个管理员同时把同一个学生往同一张空床上调,不锁的话两边都判断“床位空闲”,最后一张床两条在住记录。
public String changeBed(String studentId, int toBedId, String operator) { Connection conn = null; try { conn = DBUtil.getConnection(); conn.setAutoCommit(false); // 关自动提交,后面几步要么全成要么全败 // 1. 锁目标床位,FOR UPDATE 让并发请求在这里排队 Bed target = bedDao.selectForUpdate(conn, toBedId); if (target == null) { conn.rollback(); return "目标床位不存在"; } if (target.getStatus() != 0) { conn.rollback(); return "该床位已被占用或处于维修状态"; } // 2. 找当前在住记录,没有说明这学生还没入住 Checkin current = checkinDao.findActiveByStudent(conn, studentId); if (current == null) { conn.rollback(); return "该学生当前没有在住记录,请先办理入住"; } if (current.getBedId() == toBedId) { conn.rollback(); return "目标床位就是当前床位"; } // 3. 旧记录写结束日期,学生表状态不变 checkinDao.closeCheckin(conn, current.getCheckinId(), new Date()); // 4. 插入新入住记录 checkinDao.insert(conn, studentId, toBedId, new Date(), operator); // 5. 两个床位状态互换,这一步最容易漏 bedDao.updateStatus(conn, current.getBedId(), 0); bedDao.updateStatus(conn, toBedId, 1); conn.commit(); return "OK"; } catch (SQLException e) { try { if (conn != null) conn.rollback(); } catch (SQLException ignored) {} return "调宿失败:" + e.getMessage(); } finally { DBUtil.close(conn, null, null); } }逐段说明:第 1 步的selectForUpdate对应 SQL 是SELECT ... FROM bed WHERE bed_id = ? FOR UPDATE,它在 InnoDB 里加的是一把行锁,事务提交前别的连接查同一行会阻塞,这正是我们要的效果。第 3 步只填end_date不删记录,历史可追溯。第 5 步把旧床位改回 0、新床位改成 1,很多人只做了前三步,界面上看着调过去了,一统计入住率就发现总数对不上,这就是第 1 章那个问题的翻版。
参数方面,operator传当前登录管理员的名字,写进checkin表做审计;toBedId必须是用户从空闲床位列表里选出来的,不能手输,否则会出现往一个不存在的床位上调。返回值用字符串而不是布尔,是为了让界面能弹出具体原因,答辩演示时体验差别很大。
有个细节值得在报告里单独写一段:为什么不用数据库触发器自动维护床位状态。答案是触发器里拿不到“操作人”,而且调试困难,出问题只能看日志。业务逻辑放service层,SQL 只做数据搬运,这是我这几年越来越坚持的划分方式。
3.2 报修工单:状态机的四步流转
报修模块的价值在于状态流转,不是简单的增删改查。工单有四个状态:0 待受理、1 处理中、2 已完成、3 已取消。合法的跳转只有 0→1、1→2、0→3、1→3 四种,随便改状态就是业务漏洞。
// 合法的状态迁移表,用二维数组比一长串 if-else 好维护 private static final boolean[][] ALLOWED = { // 到: 0 1 2 3 /* 0 */ {false, true, false, true}, /* 1 */ {false, false, true, true}, /* 2 */ {false, false, false, false}, /* 3 */ {false, false, false, false} }; public String updateStatus(int repairId, int from, int to, String handler) { if (from < 0 || from > 3 || to < 0 || to > 3 || !ALLOWED[from][to]) { return "不允许从状态 " + from + " 直接变为 " + to; } String sql = "UPDATE repair SET status = ?, handler = ?, " + "finish_time = CASE WHEN ? = 2 THEN NOW() ELSE finish_time END " + "WHERE repair_id = ? AND status = ?"; try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { ps.setInt(1, to); ps.setString(2, handler); ps.setInt(3, to); ps.setInt(4, repairId); ps.setInt(5, from); // 带上原状态做乐观锁 int rows = ps.executeUpdate(); return rows == 1 ? "OK" : "工单状态已被他人修改,请刷新后重试"; } catch (SQLException e) { return "更新失败:" + e.getMessage(); } }关键在WHERE repair_id = ? AND status = ?这个条件。它做的是乐观锁:如果两个人同时打开同一条待受理工单,一个点了“受理”,另一个点了“取消”,第二个人的UPDATE因为status已经变了而影响 0 行,返回提示让他刷新。这种写法不用加锁也不用事务,比FOR UPDATE轻,适合这种冲突概率低的场景。
finish_time用CASE WHEN在 SQL 里判断,省一次额外更新。报告里可以把状态迁移图画成有向图(Word 里用形状画,别用文字描述),配一句“非法跳转在业务层拦截,不依赖前端按钮置灰”,这一句写在测试章节前面,能加分。
界面上报修列表默认按status升序、create_time倒序排,待受理的永远在最上面。这个小细节在演示时很讨巧,老师点进去第一眼看到的就是需要处理的工单。
3.3 统计查询与 Excel 导出
统计是课程设计里最容易出彩也最容易翻车的部分。楼栋入住率、男女生分布、报修处理及时率,这三个指标够用了。
-- 每栋楼的床位使用情况,一条 SQL 出结果 SELECT bl.building_name, COUNT(b.bed_id) AS total_beds, SUM(CASE WHEN b.status = 1 THEN 1 ELSE 0 END) AS used_beds, ROUND(SUM(CASE WHEN b.status = 1 THEN 1 ELSE 0 END) / COUNT(b.bed_id) * 100, 2) AS usage_rate FROM building bl JOIN room r ON r.building_id = bl.building_id JOIN bed b ON b.room_id = r.room_id GROUP BY bl.building_id, bl.building_name ORDER BY usage_rate DESC;这条 SQL 用了CASE WHEN做条件计数,比写两个子查询快,也是数据库课里“聚合函数 + 分组”的标准考点。ROUND(..., 2)保留两位小数,避免界面上出现一长串浮点数。注意GROUP BY里带了building_id,因为building_name理论上可能重名,按主键分组更稳。
导出走ExcelUtil,用 POI 把ResultSet逐行写进Workbook。这里有个坑:ResultSet不能直接转成表格,得先定义一个List<Map<String, Object>>中间结构,DAO 查出来、Service 组装、Util 只负责写文件,三层各干各的。报告里写“导出模块与查询模块解耦,查询结果可复用于界面表格和 Excel 两种输出”,这句话能体现设计意识。
统计结果我一般还会在界面上加一句口径说明,比如“入住率 = 已住床位 / 总床位,维修中床位计入总数”,答辩时老师问数字怎么来的,指着这行字就行,比口头解释靠谱。
4. 运行与避坑排查:从驱动 jar 到中文乱码
代码看懂是一回事,跑起来是另一回事。这一章按真实环境走一遍,然后把我见过的、自己踩过的五个坑摆出来,每个都按“现象—原因—解决”写,对着排就行。
4.1 三步启动:建库、导数据、跑主类
第一步,装 MySQL 8.0,装的时候字符集选utf8mb4,排序规则选utf8mb4_general_ci。装完用命令行连一下,确认SELECT VERSION();能出结果。
第二步,执行脚本。用 Navicat 或者命令行都行,顺序不能反:
# 先建库建表,再灌初始数据 mysql -uroot -p123456 < sql/01_schema.sql mysql -uroot -p123456 < sql/02_data.sql # 确认数据进去了:应该有 1 个管理员、3 栋楼、若干房间床位 mysql -uroot -p123456 -e "USE dorm_db; SELECT COUNT(*) FROM bed;"第三步,IDEA 里把lib/mysql-connector-j-8.0.33.jar加进项目依赖,改DBUtil里的账号密码,运行Main.java。默认管理员账号在doc或者README里写着,登录进去先点“宿舍管理”看楼栋列表有没有数据,有数据说明连接和脚本都通了。
提示:
01_schema.sql开头一般有DROP DATABASE IF EXISTS dorm_db;和CREATE DATABASE dorm_db DEFAULT CHARSET utf8mb4;,重复执行不会报错。但02_data.sql里的INSERT如果重复跑会撞主键,改数据之前先确认一下。
4.2 五条踩坑记录,每条都能让你少熬一晚
坑一:java.lang.ClassNotFoundException: com.mysql.cj.jdbc.Driver
现象是程序一启动就抛异常,控制台第一行就是这个。原因是驱动 jar 没加进 classpath,或者加了但版本是 5.x,类名应该是com.mysql.jdbc.Driver。解决办法:确认lib目录下有mysql-connector-j-8.0.33.jar,IDEA 里 File → Project Structure → Modules → Dependencies 加上;Eclipse 右键项目 → Build Path → Add JARs。别把 jar 只复制到目录里就以为加上了,这一步很多人翻车。
坑二:写入的中文变成问号???
现象是界面上输入“张三”,存进数据库查出来是三个问号。原因有两层:连接串少了characterEncoding=utf8,或者建库时用了latin1。解决:连接串补上useUnicode=true&characterEncoding=utf8,然后SHOW CREATE DATABASE dorm_db;看字符集,不是utf8mb4就重建库,把01_schema.sql里的建库语句改成DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_general_ci后重跑。已经写坏的数据改不回来,只能重灌。
坑三:启动报The server time zone value '...' is unrecognized
现象是第一次拿连接就失败,异常信息里带时区。原因是 MySQL 8.0 驱动要求显式指定时区,服务端又是系统默认时区。解决:连接串加serverTimezone=Asia/Shanghai,或者serverTimezone=GMT%2B8。这是一行常量的事,但不加就是跑不起来。
坑四:调宿之后入住率对不上,总数少一张床
现象是调宿功能点了没报错,但统计页面显示已住床位比学生数少一个。原因基本是只改了入住记录、没同步更新bed.status,旧床位还停在 1,新床位也是 1,两边都算“已住”,但实际只有一个人。解决办法:把换状态那两行补进同一个事务里,然后写一条校对 SQL 定期跑:
-- 找出"状态是已住但没有任何在住记录"的床位,正常应为空 SELECT b.bed_id, b.room_id, b.bed_no FROM bed b LEFT JOIN checkin c ON c.bed_id = b.bed_id AND c.end_date IS NULL WHERE b.status = 1 AND c.checkin_id IS NULL;这条校对 SQL 我建议直接写进报告的“数据一致性测试”小节,比干说“系统保证一致性”有说服力得多。
坑五:删学生时报外键约束错误
现象是Cannot delete or update a parent row: a foreign key constraint fails。原因是checkin表还引用着这个学生。解决思路有两个:一是软删除,把student.status改成 0 表示离校,不做物理删除;二是先删关联记录再删学生,但这会丢历史。课程设计里选第一种,并在报告里写明“学生数据不做物理删除,通过状态字段标记离校,保留入住历史”。老师问起来,这就是标准答案。
5. 二次开发:加一个字段要走完的四步,和报告怎么写才不虚
这套代码最常见的二次开发需求是“加个空调字段”“加个辅导员联系方式”“按学院筛选”。加字段不是改完数据库就完事,得按顺序走四步,漏一步界面就不显示。以给room表加has_ac(是否有空调)为例:
-- 第一步:改表结构,给默认值,避免存量数据为 NULL ALTER TABLE room ADD COLUMN has_ac TINYINT NOT NULL DEFAULT 0 COMMENT '0 无空调 1 有空调' AFTER gender_limit;第二步改Room.java实体类,加private Integer hasAc;和 getter/setter;第三步改RoomDao的insert、update、list三个方法,SQL 里补上字段和占位符,ResultSet里补rs.getInt("has_ac");第四步在RoomPanel的表格模型里加一列,编辑弹窗里加一个复选框。四步走完,列表、新增、修改三处同时生效。
改完之后一定要重新跑一遍那条校对 SQL,再点一遍调宿流程。我自己的习惯是每加一个字段,就把“新增房间 → 分配学生 → 调宿 → 退宿 → 统计”这条链路手动走一遍,五分钟的事,能挡掉八成回归问题。血泪经验是:只测新增不测修改,改完字段名对不上,ResultSet抛Column not found,现场演示直接黑匣子。
报告模板这部分,我说几个实际写起来容易写空的地方。需求分析别抄百度,把系统里真实存在的八个功能模块列成表,每个模块写一句“输入什么、处理什么、输出什么”。E-R 图用 Word 自带的形状画,实体用矩形、属性用椭圆、关系用菱形,标上 1:N 和 M:N,别用截图糊弄。表结构章节直接把建表脚本里的COMMENT抄进去,那是现成的数据字典。测试章节别写“测试通过”,写具体用例:输入不存在的学号调宿,期望提示“该学生当前没有在住记录”,实际也是这句,这才叫用例。
最后说个答辩技巧。老师最爱问的三个问题是:为什么床位要单独建表、调宿为什么要用事务、非法状态跳转怎么拦。这三个问题的答案分别在 2.2、3.1、3.2,把这三段代码记住,现场指着屏幕讲,比背 PPT 强。我当年第一次做这类课设,图省事把房间和床位合成一张表,调宿调了三天没调明白;从那以后我每次设计表结构,都先把“谁被占用、谁被引用”这两个问题写清楚再动手建表。希望帮到你。
本文还有配套的精品资源,点击获取