ARTICLE DETAIL

资讯详情

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

基于Java的饭店点餐系统开发:Swing+MySQL实战与答辩避坑指南

基于Java的饭店点餐系统开发:Swing+MySQL实战与答辩避坑指南 简介基于JAVA的饭店点餐系统毕业设计论文针对传统餐饮人工点餐速度慢、效率低、易出错等问题给出一套完整的系统设计与实现方案。论文采用浏览器/服务器模式以Java作为开发语言选取MySQL存储数据借助Tomcat运行Web服务并使用MyEclipse作为集成开发环境。内容涵盖需求分析、总体设计、数据库设计与功能模块实现详细说明了菜品明细、订单、菜品分类、厨师、餐桌座位及管理员等管理模块的功能与操作流程。资源包内共有1个doc文档大小约2.31MB文档包含中英文摘要和完整目录结构清晰便于查阅。目前已有153人学习下载适合计算机相关专业学生作为课程设计或毕业设计的参考资料也可帮助Java Web入门者理解餐饮点餐系统的实际开发过程并可作为设计安全、可扩展、易用系统的参考范例。1. 基于Java的饭店点餐系统先分清你要交付的是系统还是论文“基于JAVA的饭店点餐系论文.doc”这类题目惯见于课程设计和本科毕设选题名单“点餐系”基本是“点餐系统”的笔误我直接按系统理解。接了这个题你要交付两样东西一个能双击运行的点餐程序一份能过查重、扛得住答辩提问的论文文档。我见过不少同学把三四周时间全耗在到处找“java课程设计案例源码”上最后被一句“这一单选完菜总额是哪个方法算出来的”问住——没亲手写过就答不上来。这篇不打算给你打包好的完整源码而是沿着“技术选型 → 数据库建模 → 核心代码 → 避坑 → 验证”这条线把这套系统里最该自己写一遍的部分拆开讲。适合正在做 Java 课程设计、或者打算拿点餐题做毕设的本科生也适合刚学完 Java SE 想找个练手项目的人。读完你能搭出一个可运行的最小闭环把论文里的系统设计章节填实并且在答辩时对每一段核心代码都说得出来龙去脉。2. 技术选型与项目骨架Swing MySQL 为什么是课程设计的主流解2.1 桌面端还是 Web 端这个选择直接决定论文工作量先回答最容易被纠结的问题。点餐系统做桌面端Swing/JavaFX还是 Web 端JSP/Servlet 或 Spring Boot我一般建议如果任务书里没明确写“B/S 架构”或“Web 端”就选 Swing MySQL 这种 C/S 组合。理由有三个每一个都直接关系到你最终能不能顺利答辩。第一Java SE 的知识点能全部覆盖。面向对象编程 java 里的封装、继承、多态在 entity、dao、service 分层里都有自然的落点论文的“系统设计”一章写起来不心虚。第二答辩演示环境不可控。Swing 程序双击一个 jar 包就能启动不依赖 Tomcat 端口、浏览器兼容性这些玄学因素我自己用 JSP 做过一次课程设计答辩现场 Tomcat 起不来最后临时换电脑才演示完那种体验不想有第二次。第三代码量可控。单人四周内Swing JDBC MySQL 的完整闭环大概两千到三千行而且大部分是重复的 CRUD切到 Web 端前端页面、Servlet 转发、web.xml 配置、会话管理每一项都是新增工作量。如果做成 Web 端论文里要多出“系统部署与运行环境”整章包括 Tomcat 版本、JDK 环境变量配置、war 包部署而桌面端只需要一句“在 JDK 8 环境下运行 Main.java”。对课程设计来说少一个环节就少一个挂科风险。第一次做项目的同学很多人的经历是从“java官网jdk下载”开始然后卡在“java环境变量配置”上配了半天——这个问题第 5 章我会专门踩一遍。2.2 四层包结构entity / dao / service / ui 各管什么项目骨架直接决定你后面几周写代码的心情。我见过把全部类塞进src根目录的也见过把 SQL 写在按钮点击事件里的那种项目改一个需求要翻半小时。这里给一个四层结构的目录树按这个组织论文里的“系统架构图”直接照着画就行。restaurant/ ├── lib/ // mysql-connector-j.jar、poi 相关 jar 放这里 ├── src/ │ ├── com/restaurant/ │ │ ├── ui/ // Swing 界面LoginFrame、MainFrame、OrderPanel │ │ ├── entity/ // 实体类User、Dish、Order、OrderItem │ │ ├── dao/ // 数据访问UserDao、DishDao、OrderDao │ │ ├── service/ // 业务逻辑LoginService、OrderService │ │ ├── util/ // DBUtil、MoneyUtil、ReportExporter │ │ └── Main.java // 程序入口只负责拉起 LoginFrame ├── db.properties // 数据库连接配置放 classpath 根目录 └── 需求说明.md四层规则只有一条硬约束ui 层只调 service 层不写 SQLservice 层只做业务判断和事务控制不碰 JDBCdao 层只有 SQL 和 ResultSet 映射entity 层是纯数据载体。这条规则画进论文就是标准的三层结构加一层界面。很多同学图省事把select * from dish直接写在按钮的 ActionListener 里排版一时爽答辩时老师问“这层的职责是什么”你只能回答“就是查一下”场面会很尴尬。包名建议用 reverse domain 写法比如com.你的学号.restaurant答辩不会被追问“包名为什么这么起”。“包名不能以数字开头”“类名大驼峰、变量小驼峰”这些 java 基础规则踩一次就记住了。2.3 数据库连接配置JDBC 直连还是 ORM课程设计阶段我强烈建议用原生 JDBC不要上 MyBatis 或 Hibernate。对比一下方案配置成本答辩被问 SQL论文深度适用场景原生 JDBC低好答问到底层可写“数据库访问层”细节课程设计 / 毕设MyBatis中能答但要会答映射偏向框架配置有 Spring 基础的同学Hibernate/JPA高容易被追问缓存、懒加载偏向思想容易写虚不推荐课设用JDBC 里最常见的是先写一个DBUtil统一拿连接。我习惯把数据库参数放到db.properties而不是硬编码在 Java 类里这样论文里可以写“数据库配置与代码分离”也算一个小亮点。drivercom.mysql.cj.jdbc.Driver urljdbc:mysql://localhost:3306/restaurant?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8 usernameroot password123456package com.restaurant.util; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.util.ResourceBundle; public class DBUtil { private static String driver; private static String url; private static String username; private static String password; static { // 读取 classpath 下的 db.properties ResourceBundle rb ResourceBundle.getBundle(db); driver rb.getString(driver); url rb.getString(url); username rb.getString(username); password rb.getString(password); try { // 注册驱动MySQL 8 之后也可以不写SPI 会自动加载 Class.forName(driver); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(url, username, password); } public static void close(AutoCloseable... resources) { for (AutoCloseable r : resources) { if (r ! null) { try { r.close(); } catch (Exception e) { e.printStackTrace(); } } } } }这段代码里有三个参数要说明。serverTimezoneAsia/Shanghai是 MySQL 8 必需的时区参数不加会报时间差错误characterEncodingutf8管的是连接层的中文传输和第 3 章的建库字符集配合useSSLfalse是为了关闭 MySQL 8 默认开启的 SSL 警告课程设计里不需要加密连接让控制台干净一点。ResourceBundle.getBundle(db)默认从 classpath 根目录找db.properties所以文件别放错位置。3. 数据库建模与实体层订单表的设计决定系统上限3.1 六张核心表用户、菜品分类、菜品、桌台、订单、订单明细点餐系统的业务闭环拆开就是桌台落座 → 选菜下单 → 后厨制作 → 上菜结账 → 报表统计。支撑这个闭环最少要六张表。下面这套建表 SQL 我用了很多次字段命名用下划线风格Java 实体类里转成小驼峰论文里画 ER 图也方便。CREATE DATABASE restaurant DEFAULT CHARACTER SET utf8mb4; USE restaurant; -- 用户表收银员、后厨、管理员共用一张表用 role 区分 CREATE TABLE t_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(20) NOT NULL UNIQUE, password CHAR(32) NOT NULL, -- 存 MD5 后的 32 位密文 role TINYINT NOT NULL DEFAULT 1, -- 1收银员 2后厨 3管理员 real_name VARCHAR(20), create_time DATETIME NOT NULL ); -- 菜品分类表 CREATE TABLE t_dish_category ( id INT PRIMARY KEY AUTO_INCREMENT, category_name VARCHAR(20) NOT NULL ); -- 菜品表价格用 DECIMAL不用 double CREATE TABLE t_dish ( id INT PRIMARY KEY AUTO_INCREMENT, category_id INT NOT NULL, dish_name VARCHAR(30) NOT NULL, price DECIMAL(10,2) NOT NULL, status TINYINT NOT NULL DEFAULT 1, -- 1在售 0停售 FOREIGN KEY (category_id) REFERENCES t_dish_category(id) ); -- 桌台表status 0空闲 1占用 CREATE TABLE t_table ( id INT PRIMARY KEY AUTO_INCREMENT, table_no VARCHAR(10) NOT NULL, seat_count INT NOT NULL DEFAULT 4, status TINYINT NOT NULL DEFAULT 0 ); -- 订单主表一个订单对应一桌一次消费 CREATE TABLE t_order ( id INT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(20) NOT NULL UNIQUE, -- 手工生成订单号 table_id INT NOT NULL, user_id INT NOT NULL, -- 下单操作员 total_amount DECIMAL(10,2) NOT NULL, status TINYINT NOT NULL DEFAULT 0, -- 0已下单 1制作中 2已上菜 3已结账 4已取消 create_time DATETIME NOT NULL, pay_time DATETIME ); -- 订单明细表订单头和明细是一对多 CREATE TABLE t_order_item ( id INT PRIMARY KEY AUTO_INCREMENT, order_id INT NOT NULL, dish_id INT NOT NULL, dish_name VARCHAR(30) NOT NULL, -- 冗余快照防止菜品改名后历史订单跟着变 price DECIMAL(10,2) NOT NULL, quantity INT NOT NULL, amount DECIMAL(10,2) NOT NULL, FOREIGN KEY (order_id) REFERENCES t_order(id) );几个设计点说明。t_order_item里冗余了dish_name和price这是刻意的菜品表可以改价格、可以删但历史订单必须保留下单那一刻的快照不然月底对账发现昨天的订单金额变了就是数据一致性问题。订单号不用自增 ID而是手工生成是为了给服务员对单用格式yyyyMMddHHmmss 桌号可读性好也方便按规则查重。密码字段定长CHAR(32)对应 MD5 输出长度比VARCHAR更省空间。3.2 实体类与 DAO从表到对象的映射面向对象编程 java 在这里落地实体类要按表结构一一映射字段类型要和数据库对应数据库INT对应IntegerDECIMAL(10,2)对应BigDecimalDATETIME对应java.util.Date。下面以User为例注意我在实体类里加了一个业务判断方法这是面向对象编程 java 比较忌讳的贫血模型写法但对课程设计来说够用答辩时还能解释“对象不只是数据袋子也可以承载简单行为”。package com.restaurant.entity; import java.util.Date; public class User { private Integer id; private String username; private String password; // 存 MD5 密文不存明文 private Integer role; private String realName; private Date createTime; public User() { } // 语义化判断比到处 if(user.getRole() 3) 可读性好 public boolean isAdmin() { return role ! null role 3; } public Integer getId() { return id; } public void setId(Integer id) { this.id id; } public String getUsername() { return username; } public void setUsername(String username) { this.username username; } public String getPassword() { return password; } public void setPassword(String password) { this.password password; } public Integer getRole() { return role; } public void setRole(Integer role) { this.role role; } public String getRealName() { return realName; } public void setRealName(String realName) { this.realName realName; } public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime createTime; } }DAO 层只做一件事把 SQL 结果变成对象。以登录查询为例用PreparedStatement而不是字符串拼接 SQL这是防 SQL 注入的第一道防线也是答辩老师爱问的点。package com.restaurant.dao; import com.restaurant.entity.User; import com.restaurant.util.DBUtil; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; public class UserDao { public User findByUsername(String username) { String sql SELECT id, username, password, role, real_name, create_time FROM t_user WHERE username ?; // try-with-resources 会自动关闭 conn/ps/rs不用手动 finally try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, username); try (ResultSet rs ps.executeQuery()) { if (rs.next()) { User u new User(); u.setId(rs.getInt(id)); u.setUsername(rs.getString(username)); u.setPassword(rs.getString(password)); u.setRole(rs.getInt(role)); u.setRealName(rs.getString(real_name)); u.setCreateTime(rs.getTimestamp(create_time)); return u; } } } catch (SQLException e) { e.printStackTrace(); } return null; } }这里的关键细节是rs.getTimestamp(create_time)返回java.sql.Timestamp它继承自java.util.Date所以可以直接放进实体类的Date字段。另一个细节是PreparedStatement的占位符从 1 开始计数ps.setString(1, username)对应 SQL 里第一个?不要写错索引这是新手最常见的翻车点。3.3 金额用 BigDecimal、时间用 Timestamp类型选择的三个细节第一所有金额字段数据库用DECIMAL(10,2)Java 用BigDecimal。double算金额会翻车0.1 加 0.2 的结果是 0.30000000000000004这在 java 基础面试题里出现过无数次实战里照样有人踩。第二要自己控制精度凡是算出来的金额一律setScale(2, RoundingMode.HALF_UP)。第三new BigDecimal(0.1)也是错因为 0.1 这个 double 字面量本身就是近似的必须用字符串构造器。我习惯写一个MoneyUtil统一处理整个项目不要出现第二处金额计算逻辑。package com.restaurant.util; import java.math.BigDecimal; import java.math.RoundingMode; public class MoneyUtil { // 用字符串构造禁止 new BigDecimal(0.1d) public static BigDecimal yuan(String value) { return new BigDecimal(value).setScale(2, RoundingMode.HALF_UP); } // 单价乘以数量返回两位小数金额 public static BigDecimal multiply(String price, int quantity) { BigDecimal p new BigDecimal(price); return p.multiply(BigDecimal.valueOf(quantity)) .setScale(2, RoundingMode.HALF_UP); } // 累加一组金额 public static BigDecimal sum(BigDecimal... amounts) { BigDecimal total BigDecimal.ZERO; for (BigDecimal a : amounts) { total total.add(a); } return total.setScale(2, RoundingMode.HALF_UP); } }RoundingMode.HALF_UP就是四舍五入课程设计阶段不需要纠结银行家算法论文里写清楚“采用四舍五入保留两位小数”就够。之所以单独抽一个工具类是为了让 service 层的代码只关心业务逻辑不关心精度细节答辩时被问到“金额精度怎么保证”你可以直接指着这个类说全项目所有金额都必须经过这里。4. 核心业务代码落地登录、下单、结账与报表导出4.1 登录与鉴权MD5 加盐会话别用全局变量硬扛登录是系统的门面。密码不能明文存库常见做法是 MD5 加盐。所谓盐就是在明文前面拼一段固定字符串再算摘要防止有人拿彩虹表反查。MD5 本身不是安全方案真正的项目会用 BCrypt但课程设计用 MD5 加盐已经能把“密码存储”这个点讲清楚。package com.restaurant.service; import com.restaurant.dao.UserDao; import com.restaurant.entity.User; import java.math.BigInteger; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; public class LoginService { private UserDao userDao new UserDao(); public User login(String username, String password) { String salt restaurant_demo; // 实际项目应每个用户独立盐值 String encrypted md5(salt password); User user userDao.findByUsername(username); if (user ! null user.getPassword().equals(encrypted)) { SessionContext.setCurrentUser(user); // 记录登录态 return user; } return null; } private String md5(String input) { try { MessageDigest md MessageDigest.getInstance(MD5); byte[] digest md.digest(input.getBytes(StandardCharsets.UTF_8)); return String.format(%032x, new BigInteger(1, digest)); } catch (Exception e) { throw new RuntimeException(MD5 计算失败, e); } } }这段代码的意图分三层salt password是加盐规则MessageDigest.getInstance(MD5)拿摘要器String.format(%032x, ...)把字节数组转成 32 位十六进制的定长字符串。注意getBytes一定要指定UTF-8否则在不同系统默认编码下同一个密码会算出不同的摘要。SessionContext可以是一个简单的静态类内部用一个ThreadLocalUser存当前登录用户代码不到十行比把 user 对象到处传参干净。4.2 下单事务订单头和明细必须原子写入下单是整个系统最核心的代码也是答辩时最能展示你“懂数据一致性”的地方。一个订单包含订单主表和订单明细表两条插入必须在一个数据库事务里完成。如果先插了订单头、再插明细时失败就会留下有头没明细的脏订单。package com.restaurant.service; import com.restaurant.dao.OrderDao; import com.restaurant.entity.Order; import com.restaurant.entity.OrderItem; import com.restaurant.util.DBUtil; import java.math.BigDecimal; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.Statement; import java.sql.Timestamp; import java.util.List; public class OrderService { public Long createOrder(Order order, ListOrderItem items) throws Exception { Connection conn DBUtil.getConnection(); conn.setAutoCommit(false); // 关闭自动提交手动开启事务 try { order.setOrderNo(generateOrderNo(order.getTableId())); order.setStatus(0); // 已下单 String insertOrder INSERT INTO t_order(order_no, table_id, user_id, total_amount, status, create_time) VALUES(?,?,?,?,?,?); try (PreparedStatement ps conn.prepareStatement(insertOrder, Statement.RETURN_GENERATED_KEYS)) { ps.setString(1, order.getOrderNo()); ps.setInt(2, order.getTableId()); ps.setInt(3, order.getUserId()); BigDecimal total BigDecimal.ZERO; for (OrderItem item : items) { total total.add(item.getAmount()); } total total.setScale(2, BigDecimal.ROUND_HALF_UP); order.setTotalAmount(total); ps.setBigDecimal(4, total); ps.setInt(5, order.getStatus()); ps.setTimestamp(6, new Timestamp(System.currentTimeMillis())); ps.executeUpdate(); ResultSet rs ps.getGeneratedKeys(); Long orderId null; if (rs.next()) { orderId rs.getLong(1); } OrderDao orderDao new OrderDao(); for (OrderItem item : items) { item.setOrderId(orderId); orderDao.insertItem(conn, item); // 必须传同一个 conn } conn.commit(); // 全部成功再提交 return orderId; } } catch (Exception e) { conn.rollback(); // 任何一步失败整体回滚 throw e; } finally { conn.setAutoCommit(true); DBUtil.close(conn); } } private String generateOrderNo(Integer tableId) { // 订单号时间精确到秒 桌号再加三位随机数防碰撞 int rand (int) (Math.random() * 900) 100; return String.format(%1$tY%1$tm%1$td%1$tH%1$tM%1$tS%2$02d%3$d, new java.util.Date(), tableId, rand); } }这里有两个容易忽略的参数细节。Statement.RETURN_GENERATED_KEYS告诉 MySQL 要返回自增主键拿不到这个值后面的明细就没有order_id外键。所有明细插入必须复用同一个conn对象最典型的问题是每行明细都自己getConnection()那事务就形同虚设因为每个连接各自独立提交。至于String.format里那么多%t第一个参数是java.util.Date后两个参数是桌号和随机数顺序别搞反这也是一个常见的“对着代码抄都抄错”的地方。4.3 订单状态流转状态机枚举比 if/else 更稳订单状态有五种已下单、制作中、已上菜、已结账、已取消。很多人的第一反应是写if(status 0) { status 1; }这种代码状态一多就乱。我推荐用枚举把状态和行为绑在一起这样非法流转在编译期就能挡住一部分。package com.restaurant.entity; public enum OrderStatus { ORDERED(0, 已下单), COOKING(1, 制作中), SERVED(2, 已上菜), PAID(3, 已结账), CANCELED(4, 已取消); private final int code; private final String desc; OrderStatus(int code, String desc) { this.code code; this.desc desc; } public boolean canTransitTo(OrderStatus target) { // 已结账和已取消是终态不允许再流转 if (this PAID || this CANCELED) { return this target; } // 正常流程按 ordinal 顺序前进0-1-2-3 return target.ordinal() this.ordinal(); } public static OrderStatus of(int code) { for (OrderStatus s : values()) { if (s.code code) { return s; } } throw new IllegalArgumentException(未知订单状态: code); } public int getCode() { return code; } public String getDesc() { return desc; } }业务代码里调用current.canTransitTo(OrderStatus.PAID)返回 false 就提示“当前状态不能结账”比如订单还在制作中就点结账系统必须拦住。这种设计的论文描述就是“订单状态采用状态机管理非法流转由枚举规则统一拦截”比写一长串if更有说服力。4.4 用 POI 导出营业报表java poi word 能生成图表吗营业报表是点餐系统的收尾功能也是论文里“系统特色”那一章最好用的素材。Apache POI 是 Java 操作 Office 文件的主流库很多同学会纠结“java poi word 能生成图表吗”。答案是能XWPFChart支持在 Word 里插柱状图和饼图但 API 繁琐而且 Word 里的图表本质是嵌入的 Excel 数据文件课程设计阶段性价比不高。我一般这样处理用 POI 生成 Excel 报表图表数据也放在同一个工作簿论文里直接贴 Excel 打开后的截图效果一样代码量少一半。package com.restaurant.util; import com.restaurant.entity.Order; import org.apache.poi.ss.usermodel.*; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import java.io.FileOutputStream; import java.util.List; public class ReportExporter { public void exportDaily(ListOrder orders, String filePath) throws Exception { // XSSFWorkbook 对应 .xlsx 格式HSSFWorkbook 对应老版 .xls try (Workbook wb new XSSFWorkbook()) { Sheet sheet wb.createSheet(日营业报表); String[] headers {订单号, 桌号, 总金额, 状态, 下单时间}; Row header sheet.createRow(0); for (int i 0; i headers.length; i) { header.createCell(i).setCellValue(headers[i]); } for (int i 0; i orders.size(); i) { Order o orders.get(i); Row row sheet.createRow(i 1); row.createCell(0).setCellValue(o.getOrderNo()); row.createCell(1).setCellValue(o.getTableId()); // 金额先取 BigDecimal 再转 double避免精度展示问题 row.createCell(2).setCellValue(o.getTotalAmount().doubleValue()); row.createCell(3).setCellValue(OrderStatus.of(o.getStatus()).getDesc()); row.createCell(4).setCellValue(o.getCreateTime().toString()); } for (int i 0; i headers.length; i) { sheet.autoSizeColumn(i); } try (FileOutputStream fos new FileOutputStream(filePath)) { wb.write(fos); } } } }POI 写入数值时setCellValue只有 double 重载这就是为什么代码里要先BigDecimal.doubleValue()再写入。如果直接把金额的字符串写进去Excel 会把这一格当文本合计公式就求不出数。autoSizeColumn依赖字体渲染在 Linux 服务器上可能不生效本地 Windows 跑没问题。导出的xlsx文件Excel 和 WPS 打开中文偶尔有差异这是 POI 写字符串字体名导致的统一用“宋体”通常能解决。5. 避坑与常见问题环境变量、中文乱码与并发下单的五条血泪经验5.1 JDBC 驱动加载失败Class.forName、SPI 与 PATH 缓存现象运行Main.java控制台第一行报ClassNotFoundException: com.mysql.cj.jdbc.Driver或者报No suitable driver found for jdbc:mysql://localhost:3306/restaurant。原因驱动 jar 没放到 classpath或者驱动类名写错。MySQL 5 的驱动类名是com.mysql.jdbc.DriverMySQL 8 换成了com.mysql.cj.jdbc.Driver照着老教程抄类名对不上。另外 IDE 里的 lib 目录光把 jar 拖进项目不算数必须右键 Add to Build Path这个操作在 Eclipse 里尤其容易漏。解决确认mysql-connector-j.jar在lib目录且已被构建路径引用检查驱动类名和你装的 MySQL 版本对应。如果同时做过“java环境变量配置”和 “java安装”还要注意修改了环境变量后已经打开的 cmd 窗口不会立即生效PATH 是启动时读入的重开一个终端再跑java -version验证不要在旧窗口里反复怀疑人生。5.2 中文乱码URL、源码、数据库三处编码必须对齐现象Swing 界面上菜单名显示成???或者数据库里明明有中文查出来显示乱码反过来插入正常、界面乱码也有。原因编码在三处不一致。第一处是 Java 源码文件本身的编码IDEA 默认 UTF-8而某些老电脑上系统编码是 GBK源码里中文被 IDE 存成 UTF-8 字节运行时按 GBK 读出来就乱。第二处是db.properties里 URL 没带characterEncodingutf8。第三处是建库时没有指定字符集默认继承自 MySQL 配置文件可能是 latin1。解决三处统一切到 UTF-8。数据库用CREATE DATABASE restaurant DEFAULT CHARACTER SET utf8mb4;URL 补characterEncodingutf8IDEA 的 File Encodings 里把 Global、Project、Properties 全设为 UTF-8。注意utf8和utf8mb4的区别MySQL 8 里utf8实际上不是完整的 UTF-8utf8mb4才算建表直接上utf8mb4省得后面存表情符号时再来一次。5.3 double 算金额翻车BigDecimal 构造器别传 double现象订单总额 29.7界面显示 29.699999999999996或者 Excel 报表里分类合计总是差几分钱。原因double是二进制浮点数无法精确表示十进制小数。0.1 0.2 的结果不是 0.3这是 java 基础面试题里的常客也是 java 基础题目网站上的高频错题实战里照样有人踩。最隐蔽的写法是new BigDecimal(0.1)表面用了 BigDecimal实际传入的 0.1 已经是 double 的近似值算出来还是脏的。解决全部金额运算走第 3 章MoneyUtil构造 BigDecimal 只用字符串参数new BigDecimal(29.7)。算完统一setScale(2, RoundingMode.HALF_UP)。数据库字段也必须是DECIMAL(10,2)别用 float。这条规则写进论文的“系统设计约束”里答辩时是一个很实在的工程意识。5.4 并发下单丢单事务和 order_no 一起解决现象同一桌两个人几乎同时点菜下单成功后订单明细少了一条或者两张订单的order_no相同主键冲突直接异常。原因两个线程各自拿到一个数据库连接订单头先插成功了明细才插入中间发生异常时各自连接不知道对方失败只有局部提交。order_no用yyyyMMddHHmmss 桌号只精确到秒同一秒内两笔订单必然碰撞。解决事务手动接管订单头和明细用同一个ConnectionsetAutoCommit(false)全部成功再commit任何一步异常rollback。order_no生成时加三位随机数或者直接用UUID.randomUUID().toString().replace(-, )后者绝对不会撞。如果以后加了库存字段扣减 SQL 必须写成UPDATE t_dish SET stock stock - ? WHERE id ? AND stock ?用受影响行数判断是否超卖。这个场景答好了就是“java 怎么保证数据一致性”这道题的活例子。5.5 POI 导出后中文乱码或图表空白现象POI 生成的xlsx在 Excel 里打开中文正常但用 WPS 打开是乱码或者图表创建出来是空白框双击进去没有数据。原因写单元格时用了CellType.STRING和setCellValue的字符串重载没问题乱码多半是源码文件编码不是 UTF-8和第 5.2 是同一个根。图表空白则是XSSFChart创建了图表对象但没设置分类轴数据数据区域引用也是空的等于画了个空框。解决导出前先确认工程编码 UTF-8单元格写数值用 double写中文用字符串别混用类型。图表这块我的建议是课程设计别硬上XWPFChart先用 POI 把xlsx报表做扎实再用 Excel 打开后选中数据插入柱状图截图放进论文。你要答“java poi word 能生成图表吗”这个问题就说“能但 Word 图表本质依赖嵌入式 Excel 数据工程上普遍做法是 Excel 报表加截图”这是有实操经验的回答比硬写一百行 chart API 更有说服力。6. 验证与进阶从自测清单到面试谈资6.1 八个验收动作把主流程跑通答辩前一晚不要只盯着代码看要按真实业务流程走一遍。我习惯用一张验收清单自测八步全部通过才敢提交步骤操作预期结果1输入错误密码登录提示“用户名或密码错误”不崩溃2正确登录收银员账号进入主界面显示当前操作员姓名3按分类浏览菜品分类切换正常价格显示两位小数4菜品加入点单购物车并改数量明细金额和合计实时重算5提交订单订单状态为“已下单”t_order 和 t_order_item 都有数据6后厨账号登录看到新订单并置为制作中状态变更后收银端同步可见7结账状态变为“已结账”total_amount 正确8导出当日报表xlsx 打开正常中文无乱码金额与手工计算一致6.2 从 Swing 到 Spring Boot把课程设计讲成面试项目做完这套 Swing 项目它还能变成你简历上经得起追问的一段经历。点餐系统是 java 面试八股文里被嚼烂了但仍然好用的项目载体因为面试官可以顺着它追问一串问题事务隔离级别、订单状态设计、金额精度、并发扣库存。你已经亲手写过答案就在代码里不需要背。进阶路线很成熟DAO 层换成 MyBatisservice 层加 Spring 事务注解UI 层换成 Spring Boot 的 REST 接口登录态从 Session 换成 JWT就是一个能够讲上二十分钟的 Web 项目。骨架没变变的是每一层的实现方式这正是你从课程设计走向工程开发的正常路径。我做第一个点餐系统时为了省事把 SQL 全写在按钮事件里答辩被问到“明细插入到一半失败了怎么办”我愣了几秒才说“那数据就不全了”。现在我的习惯是先画数据库表结构再写事务和状态机最后才去做界面。代码可以丑一点但数据不能裂。希望帮到你。本文还有配套的精品资源点击获取
返回列表