拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Java课程设计餐厅点菜系统:MySQL表设计与JDBC事务实践

简介基于Java Eclipse设计实现的餐厅点餐系统课程设计源码与配套说明文档适合Java初学者、软件工程课程设计学生以及需要快速搭建点餐原型的开发者。资料围绕课程设计任务展开涵盖系统可行性分析、需求建模、订单流程与用户界面设计并配有完整可运行的Eclipse工程导入后即可启动体验。压缩包共47个文件包括9个java源代码文件、10个class编译文件、20张jpg图片用于菜品展示与界面素材以及docx说明文档、project/classpath等Eclipse配置和XML元数据整包仅1.4MB结构紧凑清晰。已有1784人学习下载。借助源码与文档读者既能理解点餐系统中菜单浏览、订单提交、结算管理等模块的具体编码也能梳理从需求分析到测试验证的完整开发流程可作为课程设计参考也方便在此基础上进行二次开发与功能扩展。1. 餐厅点菜系统在 Java 课程设计里的正确打开方式一套基于 Java 和 Eclipse 的餐厅点菜系统交付形态往往是「源码 说明文档 数据库脚本」打成一个压缩包。多数人拿到这个题目时会先去找一份能跑的源码但真正决定成绩的往往是这代码换一台电脑能不能跑起来说明文档里有没有如实写清楚表设计和测试过程。系统的典型范围是服务员选桌台、看菜品、加菜、退菜、结账管理员维护菜品跑通一次从选菜到记账的闭环。对刚学完 Java 基础的人它是对象、集合、JDBC、Swing 的一次组合练习对要交课程设计的人来说它是一套能拆开讲的分层代码也是一份能撑住答辩的文档底稿。2. 餐厅点菜系统的数据模型与 MySQL 建表点菜系统在画界面之前应该先把数据模型定住。一个最小可用的模型只需要回答三个问题有哪些菜品、哪些桌台、一次点菜写进哪张表。把这三件事用 SQL 落在 MySQL 里后面所有 Java 代码都是围绕这几张表转的表结构不稳界面写得再好看也会在联调时反复返工。2.1 功能边界先定成一个简洁闭环一套能做到「值班」的餐厅点菜系统标准模块大概是四个菜品管理、桌台管理、点菜下单、退菜与结账。角色默认两种服务员负责点菜和结账管理员负责维护菜单。这个边界不大不小正好覆盖一轮完整的增删改查与多表关联也能把事务、外键、关联查询这些数据库考点全部带出来。库存、会员、外卖、厨打这些模块不建议做。不是做不到而是会把课程设计拖成无限期项目还容易让代码量集中在和点菜无关的地方。比较体面的做法是这些模块先不做但说明文档里写一句「因时间关系未实现后续可扩展」。这句话的价值在于让答辩老师明确知道边界是你主动划的而不是根本没考虑过。提示功能范围直接决定数据模型。先把模块列表写进说明文档的引言部分再开始建表。2.2 订单主表和订单明细表为什么要拆两层一次消费对应一张订单一张订单里有多个菜品每个菜品又有数量。如果所有信息塞进一行退菜、改数量、按菜品统计都会变成灾难。最稳妥的做法是拆成订单主表和订单明细表这也是绝大多数餐饮系统的通用形态。表名职责关键字段tb_dish菜谱静态数据dish_id, dish_name, pricetb_table桌台及其当前状态table_id, table_name, statustb_order一次用餐的订单头order_id, order_no, table_id, total_amount, pay_status, create_timetb_order_detail订单里的每一道菜detail_id, order_id, dish_id, count, amounttb_order 与 tb_order_detail 是一对多关系tb_table 的 status 从「空闲」变为「已点餐」结账后再回到「空闲」。这套关系写进说明文档时建议配一段纯文字版关联说明比贴一张 ER 图更好排版答辩时照着念也不容易卡壳。2.3 建表脚本直接放进说明文档附录CREATE DATABASE IF NOT EXISTS restaurant DEFAULT CHARACTER SET utf8mb4; USE restaurant; CREATE TABLE tb_dish ( dish_id INT PRIMARY KEY AUTO_INCREMENT COMMENT 菜品编号, dish_name VARCHAR(50) NOT NULL COMMENT 菜品名称, price DECIMAL(10,2) NOT NULL COMMENT 单价单位元 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT菜品表; CREATE TABLE tb_table ( table_id INT PRIMARY KEY AUTO_INCREMENT COMMENT 桌台编号, table_name VARCHAR(20) NOT NULL COMMENT 桌台名称如A01, status TINYINT NOT NULL DEFAULT 0 COMMENT 0空闲 1已点餐 2已结账 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT桌台表; CREATE TABLE tb_order ( order_id INT PRIMARY KEY AUTO_INCREMENT COMMENT 订单主键, order_no VARCHAR(30) NOT NULL UNIQUE COMMENT 业务订单号, table_id INT NOT NULL COMMENT 桌台编号, total_amount DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT 订单总额, pay_status TINYINT NOT NULL DEFAULT 0 COMMENT 0未结账 1已结账, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 下单时间, CONSTRAINT fk_order_table FOREIGN KEY (table_id) REFERENCES tb_table(table_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT订单主表; CREATE TABLE tb_order_detail ( detail_id INT PRIMARY KEY AUTO_INCREMENT COMMENT 明细编号, order_id INT NOT NULL, dish_id INT NOT NULL, count INT NOT NULL COMMENT 数量, amount DECIMAL(10,2) NOT NULL COMMENT 该行小计, CONSTRAINT fk_detail_order FOREIGN KEY (order_id) REFERENCES tb_order(order_id), CONSTRAINT fk_detail_dish FOREIGN KEY (dish_id) REFERENCES tb_dish(dish_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT订单明细表;脚本里几个细节值得说明。字符集用 utf8mb4 而不是 utf8因为菜品名或备注里万一出现生僻字不会乱码金额全部用 DECIMAL(10,2)10 表示整数和小数一共 10 位2 表示两位小数最大能存 99999999.99对餐厅营收足够外键约束建议保留它能防止明细表里出现不存在的订单号。外键在数据量大以后会成为写入瓶颈但课程设计的规模完全不需要担心网上说「外键能不用就不用」是针对高并发互联网业务的结论别套在这里。2.4 金额字段为什么不能用 double 存如果表里写 price DOUBLEJava 里再用 double 做加减用户点三份菜时会看到 114.00000000000001 这种数字。double 是二进制浮点0.1 在二进制里本来就是无限循环小数运算误差躲不掉。数据库层面用 DECIMALJava 代码里对应的字段要么用 BigDecimal要么读出后转成分来计算。这一点在答辩时经常被问到能把误差来源讲清楚的人通常比只说「老师让我用 BigDecimal」的人拿分高。3. 用 Java Swing 和 JDBC 把点菜主流程串起来数据模型定好之后最核心的工作是让「选菜 → 落单 → 改桌台状态」这一串动作在代码里跑通。这一步用 Java Swing 做界面、JDBC 访问 MySQL不引入 Spring也能在 Eclipse 里顺畅编译。3.1 三层结构课程设计里最省事的写法界面、业务、数据访问各放一个包是这类源码最稳妥的分层。常见分包方式com.restaurant.ui 放 JFrame 和面板com.restaurant.service 放点菜、结账逻辑com.restaurant.dao 放 SQL 语句com.restaurant.util 放数据库工具与常量。省事的地方在于UI 层只调 service 方法不直接写 SQLDAO 只做数据读写不做业务判断。代码出了问题按包定位比从头到尾读一遍快得多。对 5 年以上的人来说这个结构很简单但正是这种简单让它适合做教学基线后续想加 Spring 或 MyBatis 也只需要替换 DAO 层UI 和 service 可以原样保留。3.2 JdbcUtils连接参数是最容易抄错的地方package com.restaurant.util; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class JdbcUtils { private static final String URL jdbc:mysql://localhost:3306/restaurant ?useUnicodetruecharacterEncodingutf8 useSSLfalseserverTimezoneAsia/Shanghai; private static final String USER root; private static final String PASSWORD 123456; static { try { Class.forName(com.mysql.cj.jdbc.Driver); } catch (ClassNotFoundException e) { throw new ExceptionInInitializerError(缺少MySQL驱动请检查Build Path); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } public static void close(ResultSet rs, Statement stmt, Connection conn) { 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) {} } } }连接串里的参数逐个解释useUnicodetrue 配合 characterEncodingutf8 保证中文写入数据库不乱码这条不写Windows 默认 GBK 环境下中文很容易变问号serverTimezoneAsia/Shanghai 是 MySQL 8.x 的强校验项不写会在连库时直接报时区错误useSSLfalse 关掉本地开发用不上的安全连接能少一堆警告日志。用户名和密码直接写在常量里课程设计可以接受但提交代码前要确认不是别人机器的密码。3.3 JTable 展示菜单按钮负责把菜加入订单菜品列表用 JTable 和 DefaultTableModel 最直观两列是菜名和价格选中一行点按钮就把菜加进购物车。DefaultTableModel model new DefaultTableModel( new String[]{菜品编号, 菜名, 单价(元)}, 0) { Override public boolean isCellEditable(int row, int column) { return false; } }; JTable dishTable new JTable(model); btnAdd.addActionListener(e - { int row dishTable.getSelectedRow(); if (row 0) { JOptionPane.showMessageDialog(frame, 请先在列表中选中一道菜); return; } int dishId Integer.parseInt(model.getValueAt(row, 0).toString()); String dishName model.getValueAt(row, 1).toString(); double price Double.parseDouble(model.getValueAt(row, 2).toString()); Cart cart CartFactory.getCurrentCart(); cart.add(new CartItem(dishId, dishName, price, 1)); JOptionPane.showMessageDialog(frame, 已加入 dishName); });这里有一个小设计购物车用单例持有菜品先加进内存等用户点「确认下单」再一次性写入数据库。model.getValueAt(row, 0) 拿到的是表格单元格的 Object必须先 toString 再转 intisCellEditable 返回 false 是为了防止用户直接在表格里改价格。把这些行为写进说明文档比只贴界面截图更有说服力。Swing 组件用途要点JTable展示菜品和订单明细配合 DefaultTableModel关闭单元格编辑JComboBox选择桌台数据来自 tb_table 表JButton触发加菜、下单、结账监听器里只调 service不写 SQLJLabel显示合计金额购物车每次变化时刷新文本3.4 下单事务主表和明细表必须同生共死点「确认下单」时代码要同时做三件事写 tb_order、逐条写 tb_order_detail、更新桌台状态。任何一步失败前面写好的数据都得撤销否则会出现一张只有订单头没有菜品明细的脏数据。public int createOrder(int tableId, ListCartItem items) throws SQLException { Connection conn null; PreparedStatement psOrder null; PreparedStatement psDetail null; try { conn JdbcUtils.getConnection(); conn.setAutoCommit(false); String orderNo OrderNoGenerator.next(); // cartTotal 内部用 BigDecimal 遍历累加 psOrder conn.prepareStatement( INSERT INTO tb_order(order_no, table_id, total_amount) VALUES(?,?,?), PreparedStatement.RETURN_GENERATED_KEYS); psOrder.setString(1, orderNo); psOrder.setInt(2, tableId); psOrder.setBigDecimal(3, cartTotal(items)); psOrder.executeUpdate(); ResultSet keys psOrder.getGeneratedKeys(); int orderId 0; if (keys.next()) { orderId keys.getInt(1); } psDetail conn.prepareStatement( INSERT INTO tb_order_detail(order_id, dish_id, count, amount) VALUES(?,?,?,?)); for (CartItem item : items) { psDetail.setInt(1, orderId); psDetail.setInt(2, item.getDishId()); psDetail.setInt(3, item.getCount()); psDetail.setBigDecimal(4, item.getSubtotal()); psDetail.addBatch(); } psDetail.executeBatch(); conn.commit(); return orderId; } catch (SQLException e) { if (conn ! null) { conn.rollback(); } throw e; } finally { JdbcUtils.close(null, psDetail, null); JdbcUtils.close(null, psOrder, null); if (conn ! null) { try { conn.setAutoCommit(true); } catch (SQLException ignored) {} try { conn.close(); } catch (SQLException ignored) {} } } }conn.setAutoCommit(false) 之后所有 SQL 都在同一个事务里跑getGeneratedKeys 取回数据库自动生成的自增主键否则明细表拿不到 order_idexecuteBatch 把多条明细一次性发给 MySQL比在循环里一条条 executeUpdate 少很多网络往返。finally 里先恢复 autoCommit 再关闭连接是防止连接在关闭时仍带着事务状态这个顺序写反了连接池复用时会出现奇怪的脏读。4. Eclipse 导入运行排错让源码在另一台电脑上也能跑课程设计最常见的翻车点不是代码逻辑而是换一台电脑后 Eclipse 里各种报错。源码本身没问题但导入姿势、JDK 版本、字符集、驱动路径任何一个不对都跑不起来。这一章按检查顺序来排。4.1 导入源码的两种方式差别很大拿到压缩包后先解压并确认里面有 .project 和 .classpath 文件。Eclipse 用 File Import General Existing Projects into Workspace选择解压后的目录让列表自动识别出项目这是最省事的方式Eclipse 会带着原有的编译输出目录和类库配置一起导入。如果源码没带这两个文件习惯做法是新建一个 Java Project把 .java 文件复制进 src 目录再在 Properties Java Build Path 里补 JDK 和依赖 jar工作量会大一些。判断导入是否成功看 Package Explorer 里项目图标是否带一个小的 J 字没有就说明这个项目还没被当作 Java 项目识别。4.2 运行前按顺序检查 JDK、字符集和驱动三个检查点按固定顺序过一遍能排除一半以上的报错。第一环境变量。命令行执行 java -version确认 JDK 已装且版本和项目编译级别兼容。Eclipse 自带 JRE但建议用本机 JDK路径写在系统变量 JAVA_HOME 和 Path 里。第二字符集。Window Preferences General Workspace 里的 Text file encoding 改为 UTF-8否则从压缩包导入的中文注释和界面字符串全变乱码这个坑在中文 Windows 上几乎是必踩。第三驱动和 MySQL。项目里必须能看到 mysql-connector 的 jarMySQL 服务要处于运行状态数据库名、用户名、密码和 JdbcUtils 里的一致。提示这条路径是纯 Java 桌面项目的检查顺序。如果在 Eclipse 里配置 Tomcat 后也出现「找不到或无法加载主类」那是 Web 部署的问题检查 servlet-api 版本和 Deployment Assembly方向完全不同别混在一起查。4.3 「找不到或无法加载主类」多半不是代码问题这个报错在 Eclipse 里出现频率极高真正的原因通常是 Eclipse 没有把 .java 重新编译成 .class 文件。先看项目 bin 目录下有没有对应的 .class没有就先 Project Clean... 选项目强制重编译这一招能解决一大半问题。还报错就看 main 方法签名必须是标准的 public static void main(String[] args)写成了别的形式 Eclipse 也找不到入口。另一个隐蔽原因是类名和文件名不一致Java 要求一个 public 类对应一个同名 .java 文件从压缩包复制粘贴文件时经常把类名改乱。运行时的入口配置在 Run Configurations 里也能手动指定但课程设计阶段不建议动它保持默认让 Eclipse 自己找最稳。4.4 Eclipse 运行常见错误对照表报错现象大概率原因处理动作java.lang.ClassNotFoundException: com.mysql.cj.jdbc.Drivermysql-connector 没进 Build Path右键项目 Build Path Add JARsAccess denied for user rootlocalhostJdbcUtils 密码与 MySQL 不一致修改代码或数据库密码保持一致Unknown database restaurant建表脚本没执行在命令行或图形客户端执行建表 SQL表格中文显示成问号连接串少字符集参数URL 补 useUnicodetruecharacterEncodingutf8Cannot find the main class编译产物缺失或 main 签名错误Project Clean再 Run As Java Application这张表直接搬进说明文档的「常见问题」一节答辩时老师问「遇到什么问题怎么解决的」这就是现成的素材。4.5 把驱动 jar 收进项目内的 lib 目录驱动 jar 不要用 Add External JARs 加进项目这个方式记录的是本机绝对路径压缩包换一台电脑解压路径一变就失效。正确做法是在项目根目录新建 lib 文件夹把 mysql-connector-java-x.jar 复制进去再右键它 Build Path Add to Build Path。提交源码时整个 lib 目录跟着打包别人拿到后导入项目不用单独找驱动。5. 验收自查与三个收尾技巧5.1 一份能十分钟跑完的验收自查表序号验收动作预期结果1启动程序无异常主窗口打开2下拉框选桌台桌台列表来自数据库3选中菜品点「加菜」购物车数量和合计金额变化4点「确认下单」tb_order 和 tb_order_detail 同时有数据5重新打开该桌台能看到未结账订单6点「结账」金额正确桌台状态恢复为空闲7查数据库三张表数据一致无孤立记录验收重点看第 4 步和第 7 步。订单主表和明细表是否成对出现是判断事务是否生效的最直接证据。5.2 金额换算成分再运算public static long toFen(BigDecimal yuan) { return yuan.movePointRight(2).longValueExact(); } public static BigDecimal toYuan(long fen) { return BigDecimal.valueOf(fen).movePointLeft(2); }Java 里算总价和找零都先用 long 单位「分」做运算最后再转回 BigDecimal 显示成元。这样 0.1 加 0.2 的浮点误差在业务层就被截断了数据库里依然是 DECIMAL(10,2)两层数据能对上。5.3 桌台状态用整数状态机public static final int TABLE_FREE 0; public static final int TABLE_ORDERED 1; public static final int TABLE_CHECKED 2; public boolean canOrder(int status) { return status TABLE_FREE; }桌台状态如果散落在代码里到处比较字符串「空闲」「已点餐」以后改状态名就要全局搜索替换。收成一个常量类后只有 canOrder 和 canPay 两个方法判定流转后续想加「已预订」这种状态也只需要加一个常量和一个判断方法。5.4 订单号别直接用自增 IDString orderNo LocalDateTime.now() .format(DateTimeFormatter.ofPattern(yyyyMMddHHmmss)) String.format(%03d, seq);自增 order_id 适合做外键但直接暴露给服务员会泄露当天订单量放在结账小票上也不方便念给顾客核对。用时间戳加三位流水生成 order_no内存里维护一个当天自增的 seq跨天重置足够处理课程设计场景。把生成逻辑放进 OrderNoGenerator 类并在说明文档里补一句生成规则整套系统的完成度就上来了。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门