ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

网上书店数据库课程设计:从ER图到JDBC实现全流程解析

网上书店数据库课程设计:从ER图到JDBC实现全流程解析 简介《网上书店管理信息系统-数据库课程设计报告》是一份面向数据库课程设计场景的完整参考文档适合计算机相关专业学生或需要完成类似管理系统设计的学习者借鉴。报告以网上书店为业务背景系统梳理了需求分析、功能模块划分、数据结构定义、概念结构设计E-R图、逻辑结构设计关系模式转换以及系统实现与测试等全过程并重点展示了图书信息、用户信息、管理员信息和订单表四类核心数据表的结构及相互关联。在实现层面描述了使用JDBC连接数据库、创建数据库及操作数据表的方法同时涵盖主界面、添加、修改、删除、查询、显示等功能的界面与逻辑有助于读者掌握从业务建模到代码落地的完整思路。资源包内包含1个doc文档压缩包大小136KB内容章节清晰、图表丰富可直接作为课程设计报告撰写的模板或参考已有662人学习下载。1. 网上书店管理信息系统数据库课程设计到底在交付什么很多同学拿到“网上书店管理信息系统-数据库课程设计报告.doc”这个题目第一反应是写文档把功能列表、界面截图、代码贴满几十页结果答辩时被老师问一句“订单金额和明细对得上吗”“并发下单库存会不会变负数”就卡住。数据库课程设计的交付物从来不止是一份 doc而是一套从需求分析、ER 图、关系模式、建库建表、SQL 实现到程序联调都能自洽的完整小系统。这篇笔记按数据库课程设计的完整路径来讲先定表和关系再用事务把下单流程串起来最后用 JDBC 连成一个能演示的网页应用并把最容易翻车的细节单独拎出来讲。适合正在选题、准备开题或赶进度交付的同学也适合助教拿去做验收对照。2. 先画 ER 图再建表网上书店的核心表与关系模式设计2.1 需求边界先圈出最小可用系统再谈扩展课程设计最怕功能堆太多。网上书店听起来可以加购物车、优惠券、秒杀、评论、推荐、后台报表但答辩只有十分钟功能再多跟数据库设计的关系不大反而容易暴露设计缺陷。我一般会先把范围锁在一条主业务链上用户注册登录按分类或关键字查书加入购物车生成订单订单状态流转库存随订单扣减。也就是“浏览—加购—下单—减库存”这一条闭环先把它跑通再谈扩展。这一步不要急着写 SQL先在纸上画 ER 图。网上书店的实体至少有六个用户、图书、库存、购物车、订单、订单明细。其中用户和图书是多对多关系通过购物车体现订单和图书也是多对多通过订单明细体现用户和订单是一对多图书和库存在一家店的场景下是一对一。把实体和联系画清楚关系模式就自然出来了。这里有个常见的概念问题“管理员”到底建不建表。很多课程设计把管理员和用户混在一张表里用 role 字段区分。如果后台管理界面只是登录这个做法没问题如果要演示管理员上架图书、处理订单建议单独建一张 admin 表避免 user 表里混入两类角色的逻辑。2.2 从 ER 图到关系模式实体、联系与候选码的取舍ER 图转关系模式规则不复杂实体变成表联系变成外键。具体到网上书店关系模式可以定成这样useruid, username, password_hash, email, phone, reg_time bookbid, title, author, publisher, isbn, price, category, publish_date stockbid, quantity, version cartid, uid, bid, quantity, create_time ordersoid, uid, order_no, total_amount, status, create_time, pay_time order_itemid, oid, bid, price, quantity主键设计要提前想清楚。user 表有 username 这个自然候选码但用户名将来可能允许修改用自增 uid 做主键更稳username 加 UNIQUE 约束作为备用码orders 表的 order_no 是业务单号必须加 UNIQUE将来对接物流、支付时不会出现歧义order_item 用自增 id 做主键真实企业里常用“订单号 行号”做联合主键课程设计里自增 id 更直观。外键策略上我建议不要用 ON DELETE CASCADE。网上书店的订单明细属于历史数据一旦用户删了一个订单CASCADE 会把 order_item 一并删掉财务报表就断了。更合理的做法是逻辑删除订单表加 status 字段把“已取消”作为一种状态而不是物理 DELETE。这一点在写建表 SQL 之前就要定下来否则后面改表结构很痛苦。2.3 建库建表 SQL类型、约束与索引这样定确定关系模式后落到 MySQL 建库建表。MySQL 8.x 是比较稳妥的选择字符集统一用 utf8mb4不要用 utf8mb3 或 latin1否则中文没问题生僻字和 Emoji 会直接报错或变乱码。CREATE DATABASE book_shop DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE book_shop; -- 用户表注册时间由数据库写入避免应用层时区不一致 CREATE TABLE user ( uid INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50) NOT NULL UNIQUE, password_hash VARCHAR(255) NOT NULL, email VARCHAR(100), phone VARCHAR(20), reg_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB;user加反引号是因为它在 MySQL 里是保留字。如果不想每次写 SQL 都带反引号直接建表名users更省事。密码字段用 password_hash 而不是 password因为课程设计里也会被问到“密码怎么存”用 SHA2 或 bcrypt 至少比明文有交代。-- 图书表价格用 DECIMAL禁止用 FLOAT CREATE TABLE book ( bid INT AUTO_INCREMENT PRIMARY KEY, title VARCHAR(200) NOT NULL, author VARCHAR(50), publisher VARCHAR(100), isbn VARCHAR(20) UNIQUE, price DECIMAL(10,2) NOT NULL, category VARCHAR(50), publish_date DATE, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, KEY idx_category (category) ) ENGINEInnoDB; -- 库存表version 字段留作乐观锁扩展 CREATE TABLE stock ( bid INT PRIMARY KEY, quantity INT NOT NULL DEFAULT 0, version INT NOT NULL DEFAULT 0, CONSTRAINT fk_stock_book FOREIGN KEY (bid) REFERENCES book(bid) ) ENGINEInnoDB; -- 购物车联合唯一键保证同一用户同一本书只占一行 CREATE TABLE cart ( id INT AUTO_INCREMENT PRIMARY KEY, uid INT NOT NULL, bid INT NOT NULL, quantity INT NOT NULL DEFAULT 1, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_cart_user_book (uid, bid), CONSTRAINT fk_cart_user FOREIGN KEY (uid) REFERENCES user(uid), CONSTRAINT fk_cart_book FOREIGN KEY (bid) REFERENCES book(bid) ) ENGINEInnoDB; -- 订单主表status 用 TINYINT 存状态码不作为字符串 CREATE TABLE orders ( oid INT AUTO_INCREMENT PRIMARY KEY, uid INT NOT NULL, order_no VARCHAR(32) NOT NULL UNIQUE, total_amount DECIMAL(10,2) NOT NULL, status TINYINT NOT NULL DEFAULT 0 COMMENT 0待付款 1已付款 2已发货 3已完成 4已取消, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, pay_time DATETIME, CONSTRAINT fk_order_user FOREIGN KEY (uid) REFERENCES user(uid) ) ENGINEInnoDB; -- 订单明细price 是下单时的快照 CREATE TABLE order_item ( id INT AUTO_INCREMENT PRIMARY KEY, oid INT NOT NULL, bid INT NOT NULL, price DECIMAL(10,2) NOT NULL, quantity INT NOT NULL, CONSTRAINT fk_item_order FOREIGN KEY (oid) REFERENCES orders(oid), CONSTRAINT fk_item_book FOREIGN KEY (bid) REFERENCES book(bid) ) ENGINEInnoDB;几个参数说明答辩时经常被追问。价格用 DECIMAL(10,2)不用 FLOAT因为浮点数在比较和累加时会出精度误差比如 0.1 加 0.2 不等于 0.3订单明细里要单独存一个 price 快照因为图书表里的价格以后会改历史订单必须按下单时的价格算不能去关联 book 当前价格。status 用 TINYINT便宜且好扩展配合 COMMENT 就能在工具里看清含义比 VARCHAR 状态名更规范。索引方面title、category 是查询高频条件cart 表的联合唯一键同时承担去重和按用户查购物车的功能。3. 用 SQL 跑通下单全流程增删改查、事务与并发锁3.1 基础增删改查注册、查询、购物车写入数据库课程设计评分时增删改查四项操作是硬指标不能只写 SELECT。每项操作都要对应一个真实业务场景而不是空泛地“对某表插入一条记录”。用户注册是 INSERT 的典型场景INSERT INTO user (username, password_hash, email, phone) VALUES (zhangsan, SHA2(123456, 256), zsexample.com, 13800138000);密码用 SHA2 而不是 MD5是给答辩留个解释空间。如果指导书范例用了 MD5也不是不行但报告里要写明“MD5 已不适合存储密码课程设计仅作演示生产环境应加盐并改用 bcrypt 或 argon2”。这样一句话比代码本身更值钱。图书查询是 SELECT 加条件过滤的常见场景SELECT b.bid, b.title, b.author, b.price, s.quantity AS stock_quantity FROM book b LEFT JOIN stock s ON s.bid b.bid WHERE b.category 数据库 AND b.price BETWEEN 20 AND 100 ORDER BY b.price DESC LIMIT 20;这里用 LEFT JOIN 而不是 JOIN是为了把没有库存记录的图书也查出来。库存表只在图书上架时插入数据如果刚上架还没初始化库存INNER JOIN 会把这本书漏掉页面上就“凭空消失”了。购物车加购要处理重复添加的问题INSERT INTO cart (uid, bid, quantity) VALUES (1, 3, 1) ON DUPLICATE KEY UPDATE quantity quantity 1;ON DUPLICATE KEY UPDATE 配合 uk_cart_user_book 联合唯一键第一次添加是插入第二次添加变成数量加一不会出现同一个人把同一本书加两行的情况。这是课程设计里最容易忽略的小细节但对体验影响很大。修改和删除也要有对应场景比如修改图书价格、删除购物车某一行UPDATE book SET price 79.80 WHERE bid 3; DELETE FROM cart WHERE uid 1 AND bid 3;这两条 SQL 的 WHERE 是底线。没有 WHERE 的 UPDATE/DELETE 是课程设计事故的高发区哪怕是本地库也会让你瞬间丢光数据。写进报告时可以在 DELETE 语句前面加一句注释“删除操作必须带用户维度和图书维度条件”。3.2 事务与并发锁库存扣减不能靠运气下单是整门课程设计里最核心的业务也是最容易被追问的。简单做法是三步插入订单、插入明细、扣减库存。但三条 SQL 独立执行时任何一步失败都会让数据不一致。把三步包进一个事务里保证要么全部成功要么全部回滚这是数据库基础知识里最该在项目里体现出来的部分。下面这段是下单核心 SQL-- 以用户 1 为例把购物车里所有书生成订单并扣减库存 START TRANSACTION; INSERT INTO orders (uid, order_no, total_amount, status) VALUES (1, 202501160001, 0, 0); SET oid LAST_INSERT_ID(); INSERT INTO order_item (oid, bid, price, quantity) SELECT oid, b.bid, b.price, c.quantity FROM cart c JOIN book b ON b.bid c.bid WHERE c.uid 1; UPDATE stock s INNER JOIN order_item oi ON oi.bid s.bid SET s.quantity s.quantity - oi.quantity WHERE oi.oid oid AND s.quantity oi.quantity; -- 如果库存不足上面的 UPDATE 不会更新对应行需要检查缺货情况 SELECT COUNT(*) AS short_count FROM order_item oi LEFT JOIN stock s ON s.bid oi.bid WHERE oi.oid oid AND s.quantity oi.quantity; UPDATE orders SET total_amount ( SELECT IFNULL(SUM(price * quantity), 0) FROM order_item WHERE oid oid ) WHERE oid oid; DELETE FROM cart WHERE uid 1; COMMIT;注意几个细节。order_no 不要直接用自增 oid 当单号业务单号应该单独生成比如“日期 流水号”这样将来对接支付、物流时不会暴露自增规律。LAST_INSERT_ID() 在事务里能安全取到刚插入的 oid不需要额外查询。库存扣减用s.quantity oi.quantity作为条件这是防超卖的第一道防线库存不够时这一行不被更新随后通过缺货检查决定是否回滚整个事务。关于数据库并发锁这里要理解 InnoDB 的行锁机制。两个用户同时买同一本书时第二条 UPDATE 会等第一条事务提交或回滚后才执行这是行锁在起作用。如果库存只剩 1 本而两个用户各要 2 本第一个事务成功第二个事务在执行 UPDATE 时发现条件不满足最终在缺货检查阶段回滚。所以前面那段“检查 short_count”的 SQL 不是摆设是跟并发锁配合的完整逻辑。如果程序层出现数据库死锁常见原因是两个事务以不同顺序更新多张表。比如事务 A 先更新 book 再更新 stock事务 B 先更新 stock 再更新 book两边互相持有对方需要的行锁。规避方式是在程序里固定更新顺序或者先按 bid 排序再执行更新让所有事务都走同一条加锁路径。3.3 视图与存储过程把订单业务和报表固定下来视图适合把跨多表查询的固定报表封装起来。比如销售排行每次查都要 JOIN 图书、订单明细、订单三张表不如建一个视图CREATE VIEW v_book_sales AS SELECT b.bid, b.title, IFNULL(SUM(oi.quantity), 0) AS sale_count, IFNULL(SUM(oi.price * oi.quantity), 0) AS sale_amount FROM book b LEFT JOIN order_item oi ON oi.bid b.bid LEFT JOIN orders o ON o.oid oi.oid AND o.status IN (1,2,3) GROUP BY b.bid, b.title;这里的 LEFT JOIN 有讲究。图书可能一单都没卖出去用 LEFT JOIN 才能保证这种书也出现在报表里销量显示 0 而不是被过滤掉。订单状态筛选用status IN (1,2,3)对应已付款、已发货、已完成待付款和已取消的订单不算入销售额。视图建好后业务侧直接SELECT * FROM v_book_sales ORDER BY sale_amount DESC LIMIT 10就能出排行。存储过程的价值在于把下单逻辑固化到数据库里程序只需要传用户 ID 和订单号DELIMITER // CREATE PROCEDURE sp_create_order(IN p_uid INT, IN p_order_no VARCHAR(32)) BEGIN DECLARE v_oid INT; DECLARE v_short_count INT; START TRANSACTION; INSERT INTO orders (uid, order_no, total_amount, status) VALUES (p_uid, p_order_no, 0, 0); SET v_oid LAST_INSERT_ID(); INSERT INTO order_item (oid, bid, price, quantity) SELECT v_oid, b.bid, b.price, c.quantity FROM cart c JOIN book b ON b.bid c.bid WHERE c.uid p_uid; UPDATE stock s INNER JOIN order_item oi ON oi.bid s.bid SET s.quantity s.quantity - oi.quantity WHERE oi.oid v_oid AND s.quantity oi.quantity; SELECT COUNT(*) INTO v_short_count FROM order_item oi LEFT JOIN stock s ON s.bid oi.bid WHERE oi.oid v_oid AND s.quantity oi.quantity; IF v_short_count 0 THEN ROLLBACK; ELSE UPDATE orders SET total_amount ( SELECT IFNULL(SUM(price * quantity), 0) FROM order_item WHERE oid v_oid ) WHERE oid v_oid; DELETE FROM cart WHERE uid p_uid; COMMIT; END IF; END // DELIMITER ;调用时CALL sp_create_order(1, 202501160002)就能完成整单操作。存储过程的好处是把事务边界放在数据库层程序端不容易漏写 COMMIT缺点是调试不如程序代码直观。课程设计里建议两者都体现程序端演示 JDBC 写法数据库端放存储过程作为另一种实现方案答辩时能多讲一层设计取舍。4. 让网页连上数据库JDBC 连接池、参数与联调步骤4.1 连接参数选型JDBC URL、驱动与连接池数据库建好、SQL 跑通后下一步是让网页程序连上来。常见做法是 Java JDBCServlet 做接口JSP 或简单 HTML 做页面。第一步先把数据库连接参数吃透这里翻车的人最多。String url jdbc:mysql://localhost:3306/book_shop ?useUnicodetruecharacterEncodingutf8 serverTimezoneAsia/ShanghaiuseSSLfalse; String user root; String password 123456; try (Connection conn DriverManager.getConnection(url, user, password); PreparedStatement ps conn.prepareStatement( SELECT bid, title, price FROM book WHERE category ?)) { ps.setString(1, 数据库); try (ResultSet rs ps.executeQuery()) { while (rs.next()) { System.out.printf(%d\t%s\t%.2f%n, rs.getInt(bid), rs.getString(title), rs.getDouble(price)); } } } catch (SQLException e) { System.err.println(查询失败: e.getMessage()); }JDBC URL 里的三个参数必须有characterEncodingutf8 解决中文乱码serverTimezone 解决 MySQL 8 时区报错useSSLfalse 避免本地开发出现 SSL 证书告警。PreparedStatement 是必须的不能把用户输入拼进 SQL 字符串这是 SQL 注入的入口。课程设计报告里写一句“使用预编译语句防止 SQL 注入”是明确的加分项。但课程设计不要用 DriverManager 直连方式扛住所有页面。每个请求都新建物理连接数据库会很快被打爆所以要用连接池。HikariCP 是最常见的连接池HikariConfig cfg new HikariConfig(); cfg.setJdbcUrl(url); cfg.setUsername(root); cfg.setPassword(123456); cfg.setDriverClassName(com.mysql.cj.jdbc.Driver); cfg.setMaximumPoolSize(10); cfg.setMinimumIdle(2); cfg.setConnectionTimeout(30000); cfg.setIdleTimeout(600000); cfg.setMaxLifetime(1800000); DataSource dataSource new HikariDataSource(cfg);连接池的三个参数值得在报告里解释。maximumPoolSize 不是越大越好要小于等于 MySQL 的 max_connections默认配置下设 10 到 20 是安全的connectionTimeout 是获取连接的最大等待时间设 30 秒可以避免线程无限等下去maxLifetime 要略小于 MySQL 的 wait_timeout否则池里的连接会被数据库端回收程序拿到的连接已经失效。4.2 页面联调的最小闭环从图书列表到提交订单连接池配好后做一个最小闭环验证。网上书店的首页是图书列表搜索框提交关键字Servlet 接收参数后查库这就是一个完整的前后端交互。// 假设这是一个 BookListServlet 的 doGet 方法 String kw request.getParameter(kw); String sql SELECT bid, title, author, price FROM book WHERE title LIKE ?; try (Connection conn dataSource.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, % kw %); try (ResultSet rs ps.executeQuery()) { // 把结果封装为 ListBook转发给 JSP 渲染 } }这段代码的逻辑很简单但要注意 LIKE 查询的索引问题。LIKE %关键词这种写法无法走索引数据量小没事课程设计的数据量通常只有几十条所以可以接受如果在报告里写了“对 title 建索引优化查询”那就要知道这个边界。提交订单的页面动作对应到 JDBC 层关键是把自动提交关掉手工控制事务边界try (Connection conn dataSource.getConnection()) { conn.setAutoCommit(false); try (PreparedStatement ps conn.prepareStatement( UPDATE stock SET quantity quantity - ? WHERE bid ? AND quantity ?)) { ps.setInt(1, qty); ps.setInt(2, bid); ps.setInt(3, qty); int rows ps.executeUpdate(); if (rows ! 1) { conn.rollback(); throw new IllegalStateException(库存不足); } } conn.commit(); } catch (SQLException e) { throw new RuntimeException(下单失败已回滚, e); }这里用rows ! 1判断库存是否扣减成功比先 SELECT 再 UPDATE 更可靠因为 SELECT 查到的数量在并发下可能已经过期。rollback 放在业务异常分支而不是只打印异常才能避免事务悬挂。联调时建议按“列表页可见 → 详情页可查 → 加购成功 → 下单成功 → 库存减少 → 购物车清空”的顺序逐步验证每一步都在数据库里执行对应 SELECT 确认结果不要等到页面全做完再一起调试那样出错时很难定位是 SQL 问题还是页面问题。4.3 数据库优化与备份答辩时能讲清楚的加分项课程设计做到能跑只能算合格想在答辩中出彩要在数据库优化和运维上有一两个能讲清楚的点。第一是慢查询定位。MySQL 开启慢查询日志后超过阈值的 SQL 会被记录下来。课程设计数据量小不一定能触发慢查询但你要会说“如果订单表数据量长大先开慢查询日志看哪条 SQL 慢了再用 EXPLAIN 看执行计划”。实际操作可以演示 EXPLAINEXPLAIN SELECT * FROM order_item WHERE oid 1;关注 type 字段是不是 range 或 refkey 字段有没有用到索引。如果发现全表扫描就该在 order_item 的 oid 上建索引。这个操作简单但能证明你理解索引的作用而不是只会建表。第二是备份策略。这是课程设计报告里最容易被忽略的部分但又是数据库管理的基础能力mysqldump -u root -p book_shop book_shop_backup.sql上面全量备份配合 MySQL 的 binlog 可以做增量恢复。报告里写一段“每天凌晨全量备份 每半小时 binlog 增量备份 恢复演练”的运维方案会让老师觉得你考虑到了生产环境的问题。这里要注意mysqldump 备份的是 SQL 语句恢复时执行mysql -u root -p book_shop book_shop_backup.sql即可。建议在答辩前实际跑一次备份和恢复确认生成的备份文件能成功导回否则报告里写的方案就是空话。第三是索引使用边界。前面 LIKE 查询和排序字段的索引问题不需要写得很深但要在设计说明里交代“哪些字段适合建索引、哪些不适合”。性别、状态这类低区分度字段建索引收益不大title、isbn 这类高区分度字段才有价值。这些点讲出来比堆砌“数据库优化十大技巧”有用得多。5. 网上书店课程设计避坑5 个让数据库翻车的细节5.1 保留字当表名导致建表失败现象执行建表 SQL 时报语法错误提示 order 附近有语法问题但 SQL 看起来没毛病。原因order 是 MySQL 的保留字直接当表名会被解析成排序关键字。不只是 ordergroup、user、desc 这类词都有类似风险。解决表名改成 orders、users或者在每个引用处加反引号。更推荐前者少打反引号且避免将来手误。提交报告前全局搜一遍建表脚本确保没有裸用保留字。5.2 字符集不统一页面全是乱码现象数据库里中文正常网页上显示问号或“锟斤拷”或者图书标题在 MySQL 客户端正常通过 JDBC 查出来是乱码。原因建库时用了 latin1 或 utf8mb3而 JDBC 连接串没指定 characterEncoding程序端默认用系统编码解码两边对不上。解决建库统一 utf8mb4JDBC URL 加useUnicodetruecharacterEncodingutf8页面和 Servlet 的编码过滤器也统一 UTF-8。字符集问题通常是一处没设对就全线乱码最稳的办法是在建库 SQL、连接串、配置文件里全局搜索“latin1”和“GBK”全部替换掉。5.3 物理删除订单历史数据直接断裂现象删除一个测试订单后销售报表数据变少或者删了图书关联订单明细查询报错或出现空关联。原因程序里对订单表执行了 DELETE把历史数据物理删掉了网上书店的订单和订单明细是业务凭证不能像清理日志一样删。解决用逻辑删除。订单表已经有 status 字段把取消状态设为 4前端不再显示但数据保留。图书表的删除也建议加 is_deleted 字段而不是 DELETE FROM book。只有购物车这种临时数据可以物理删除。5.4 事务没提交与死锁并发问题现象下单后订单出现了库存没扣或者数据库报“Deadlock found when trying to get lock”程序里某个操作随机失败。原因前者是 JDBC 里 setAutoCommit 被改成 false 后执行完 SQL 忘了 commit 或 rollback事务一直悬着后者是两个事务以不同顺序更新 book 和 stock互相持有对方需要的行锁InnoDB 检测到死锁后回滚其中一个事务。解决事务要么在 try 里正常 commit要么在 catch 里 rollback必要时在 finally 里再判断一次状态多表更新时固定顺序比如所有事务都先操作 book 再操作 stock。死锁回滚后程序要做重试不能直接抛出异常让用户看到 500。5.5 连接没释放导致数据库拒绝连接现象系统跑一段时间后页面报错“Too many connections”重启数据库才能恢复。原因代码里用 DriverManager.getConnection 获取连接后没有关闭或者连接池配置了但池里的连接泄漏。数据库的连接数被占满新请求无法进入。解决JDBC 代码用 try-with-resources 写法Connection、Statement、ResultSet 都会自动关闭排查时执行SHOW PROCESSLIST看到大量 Sleep 状态的连接基本就是泄漏。连接池的 maximumPoolSize 不要超过数据库 max_connections否则即使没有泄漏多实例也会把数据库压垮。6. 验收阶段报告定稿、答辩追问与自查 SQL6.1 报告结构老师真正会看的内容课程设计报告不用面面俱到但要抓重点。我的经验是老师看报告的时间不超过十分钟注意力集中在需求分析的 ER 图、逻辑设计的关系模式、以及 SQL 实现有没有真跑过。一般的结构是这样章节老师重点看什么需求分析用例图、业务流程别写长篇文字概念设计ER 图实体和联系是否完整有没有 M:N逻辑设计关系模式、主外键、范式等级别物理实现建库 SQL、索引、事务、存储过程测试测试用例有没有覆盖库存不足、重复加购总结一句话说清你做了什么别写流水账6.2 答辩追问清单数据库面试题式问题怎么答答辩追问基本围绕设计决策提前准备这些问题比背概念有用。为什么用 InnoDB 而不是 MyISAM答InnoDB 支持事务、行级锁和外键MyISAM 只有表锁且不支持事务网上书店的下单流程必须保证订单和库存一致。图书价格为什么用 DECIMAL 不用 FLOAT答浮点数有精度误差金额累加会出现分币差DECIMAL 是定点数专为金额设计。订单明细为什么要存 price 快照答图书价格会变动历史订单必须按下单时的价格结算不能关联当前价格。并发下单时怎么防止超卖答UPDATE stock 时带上quantity ?条件受影响行数为 0 则回滚配合 InnoDB 行锁保证同一本书的扣减是串行的。死锁怎么处理答固定多表更新顺序捕获死锁异常后重试并限制重试次数。6.3 交卷前自查 SQL证明你的数据是好的答辩演示前我会在数据库里跑一遍自查 SQL确认数据完整性没有问题SELECT 负库存 AS check_item, COUNT(*) AS bad_count FROM stock WHERE quantity 0 UNION ALL SELECT 订单金额不一致, COUNT(*) FROM orders o WHERE total_amount ( SELECT IFNULL(SUM(price * quantity), 0) FROM order_item i WHERE i.oid o.oid ) UNION ALL SELECT 孤儿订单明细, COUNT(*) FROM order_item i WHERE NOT EXISTS ( SELECT 1 FROM orders o WHERE o.oid i.oid );三条查询分别检查库存是否为负、订单金额与明细是否对得上、明细是否有找不到主表的孤儿记录。全部返回 0 就能放心演示。这个习惯我从第一次做课设开始延续到现在后来接手别人项目第一件事也是跑这类完整性检查少吃了不少哑巴亏。最后说一个收尾技巧把自查 SQL 的结果截图放进报告测试章节比贴十页异常日志更能说明你验证过数据。演示前导出一个全新数据库用备份文件恢复一遍再跑确保报告里的每一步都能复现你才能在答辩现场站得住。希望帮到你。本文还有配套的精品资源点击获取
返回列表