news 2026/10/2 18:00:23

Java+Swing+MySQL餐厅点餐系统源码解析:从环境搭建到业务闭环

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java+Swing+MySQL餐厅点餐系统源码解析:从环境搭建到业务闭环

简介:这是一套面向Java初学者与课程设计学习者的餐厅点餐管理系统完整源码,基于Java Swing桌面界面与MySQL数据库开发,适合用于毕业设计、课程作业或Swing+JDBC综合练习。系统区分管理员与顾客两种角色,覆盖登录注册、套餐新增与管理、订单处理、顾客点餐与订单查询、个人信息修改等核心业务,功能闭环完整,便于理解桌面端分层结构与数据库交互流程。压缩包共170个文件,约2.19MB,包含18个java源文件、66个class编译文件、62个png与16个jpg界面素材、3个jar依赖、1个sql建表脚本及工程配置文件,源码与素材齐备,导入Eclipse或IDEA并配置MySQL 8.0以上环境即可运行调试。目前已有1532人学习下载,可帮助读者快速掌握Swing窗体设计、事件处理与数据库增删改查的整合思路,并在此基础上进行二次开发与功能扩展。

1. 从一份只有十个 class 文件的源码包说起:Java+Swing+MySQL 餐厅点餐系统能跑出什么

如果你手头正好有一份 Java 课程设计或者实训作业,要求做一个带图形界面的餐厅点餐管理系统,那这份基于 Java+Swing+MySQL 的源码包大概率能让你少熬两个通宵。它没有用 Spring Boot、没有前后端分离、没有 Vue 和 Element UI,就是最朴素的 JavaSE 桌面应用:Swing 画窗口,JDBC 连 MySQL,十个核心 class 文件把管理员和顾客两条业务线全部串起来。GoodsManageInterFrm 管套餐,DealOrderInterFrm 处理订单,CheckOrderInterFrm 查订单,OrderGoodsInterFrm 负责点餐,LogOnFrm 和 RegisterFrm 管登录注册,AccountModifyInterFrm 改密码,AdminFrm 和 UserOrderFrm 分别是管理员和顾客的主界面。这套东西适合谁?适合正在做 Java 课程设计、需要快速交出一个能演示、能答辩、能写进论文的系统原型的同学,也适合想拿一个完整 Swing 项目练手 JDBC 和面向对象编程的初学者。它不炫技,但业务闭环是完整的:注册、登录、点餐、下单、处理订单、管理套餐、改密码,一条龙全通。下面我按实际拆包和跑通的顺序,把这份资源从环境搭建到业务逻辑再到踩坑排查,完整过一遍。

2. 环境搭建与数据库初始化:MySQL 8.0 建库建表和 JDBC 连接配置

2.1 为什么是 MySQL 8.0 而不是 5.7

项目正文明确写了运行环境是 Eclipse 或 IDEA 加 MySQL 8.0 以上。这个版本要求不是随便写的。MySQL 8.0 默认的认证插件从mysql_native_password换成了caching_sha2_password,而老版本的 JDBC 驱动(比如mysql-connector-java-5.1.x)根本不认识这个插件,连接时会直接抛Unable to load authentication plugin 'caching_sha2_password'。所以如果你本地装的是 MySQL 5.7,要么升级到 8.0,要么在 JDBC URL 里显式指定useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true,否则时区报错和公钥检索报错会轮番上阵。常见做法是直接上 MySQL 8.0,驱动用mysql-connector-java-8.0.28或更高,省掉一堆兼容性玄学。

2.2 建库建表的 SQL 脚本

这份源码包一般会附带一个.sql文件,但如果你拿到的版本没有,或者你想自己重建一遍,下面这套建表语句可以直接抄。核心表就四张:用户表、套餐表、订单表、订单明细表。

-- 创建数据库,字符集用 utf8mb4 防止中文乱码 CREATE DATABASE IF NOT EXISTS restaurant_db DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_general_ci; USE restaurant_db; -- 用户表:同时存管理员和顾客,用 user_type 区分 CREATE TABLE t_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(50) NOT NULL, user_type INT NOT NULL DEFAULT 0, -- 0 顾客,1 管理员 phone VARCHAR(20), address VARCHAR(200) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 套餐表 CREATE TABLE t_goods ( id INT PRIMARY KEY AUTO_INCREMENT, goods_name VARCHAR(100) NOT NULL, price DECIMAL(10,2) NOT NULL DEFAULT 0.00, description VARCHAR(500), stock INT NOT NULL DEFAULT 0 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 订单表 CREATE TABLE t_order ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, order_time DATETIME DEFAULT CURRENT_TIMESTAMP, total_price DECIMAL(10,2) DEFAULT 0.00, status INT DEFAULT 0, -- 0 待处理,1 已完成 FOREIGN KEY (user_id) REFERENCES t_user(id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 订单明细表 CREATE TABLE t_order_detail ( id INT PRIMARY KEY AUTO_INCREMENT, order_id INT NOT NULL, goods_id INT NOT NULL, quantity INT NOT NULL DEFAULT 1, subtotal DECIMAL(10,2) NOT NULL DEFAULT 0.00, FOREIGN KEY (order_id) REFERENCES t_order(id), FOREIGN KEY (goods_id) REFERENCES t_goods(id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 插入一个默认管理员账号,密码 123456 INSERT INTO t_user (username, password, user_type) VALUES ('admin', '123456', 1);

这段 SQL 的逻辑很直白:t_user用user_type字段把管理员和顾客揉在一张表里,登录时根据这个字段决定跳转到AdminFrm还是UserOrderFrm。t_order和t_order_detail拆成两张表是标准做法,一个订单对应多条明细,避免在订单表里塞 JSON 或者逗号分隔的字符串。t_goods里的stock字段在点餐时会做扣减,虽然源码里可能只是简单更新,但表结构先留好。

注意:utf8mb4和utf8在 MySQL 里不是一回事,用utf8存中文没问题,但存 emoji 会炸。课程设计一般用不到 emoji,但养成utf8mb4的习惯没坏处。

2.3 JDBC 连接配置与驱动引入

在 Eclipse 或 IDEA 里新建 Java 项目后,第一件事是把mysql-connector-java-8.0.x.jar加到 Build Path 或者 Maven 依赖里。如果你用 Maven,pom.xml里加这一段:

<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.28</version> </dependency>

然后写一个DBUtil工具类,把连接参数集中管理。源码包里可能直接在每个InterFrm里写DriverManager.getConnection,但那样改数据库密码的时候你会想砸键盘。我一般会抽一个工具类出来:

import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class DBUtil { // MySQL 8.0 驱动类名 private static final String DRIVER = "com.mysql.cj.jdbc.Driver"; // 关键参数:useSSL=false 关掉 SSL 警告,serverTimezone 解决时区差 8 小时问题 private static final String URL = "jdbc:mysql://localhost:3306/restaurant_db" + "?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true" + "&characterEncoding=utf8"; private static final String USER = "root"; private static final String PASSWORD = "你的密码"; static { try { Class.forName(DRIVER); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } public static void close(Connection conn) { if (conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } }

Class.forName在 JDBC 4.0 之后其实可以省略,但很多老教程和源码包都保留着,留着也不碍事。URL 里三个参数各有用途:useSSL=false是关掉 SSL 连接警告,本地开发用不上加密;serverTimezone=Asia/Shanghai是必须的,不写的话 MySQL 8.0 驱动会按 UTC 时区解析时间,你存进去的2024-01-01 12:00:00读出来可能变成2024-01-01 04:00:00;allowPublicKeyRetrieval=true是配合caching_sha2_password插件用的,不写的话第一次连接可能被拒绝。characterEncoding=utf8保证中文不乱码。

2.4 在 IDE 里跑起来的完整步骤

把源码导入 Eclipse 或 IDEA 之后,按这个顺序操作:

  1. 确认项目 JDK 版本在 1.8 以上,Swing 在 JDK 11 之后被移出了标准库,但 JDK 8 和 JDK 11 都还能用,JDK 17 以上需要额外加--add-modules java.desktop。
  2. 把mysql-connector-java的 jar 包加到项目的 Libraries 里,或者用 Maven 依赖。
  3. 修改DBUtil里的数据库密码为你本地的密码。
  4. 在 MySQL 里执行上面的建表 SQL。
  5. 找到LogOnFrm.java,右键 Run As Java Application。
  6. 用admin / 123456登录管理员,或者先注册一个顾客账号再登录。

如果启动时报java.lang.ClassNotFoundException: com.mysql.cj.jdbc.Driver,说明驱动没加对;如果报Access denied for user 'root'@'localhost',说明密码错了;如果报Unknown database 'restaurant_db',说明建库那步没执行。这三个错误覆盖了 90% 的启动失败场景。

3. 管理员模块拆解:套餐管理、订单处理与 GoodsManageInterFrm 的实现逻辑

3.1 管理员主界面 AdminFrm 的导航结构

AdminFrm是整个管理员端的入口,登录成功后从LogOnFrm跳过来。它一般是一个JFrame,上面挂几个按钮或者一个JTabbedPane,分别对应套餐管理、订单处理、修改密码。源码包里GoodsManageInterFrm、DealOrderInterFrm、CheckOrderInterFrm、AccountModifyInterFrm这几个类都是独立的JFrame或JInternalFrame,由AdminFrm里的按钮事件new出来再setVisible(true)。这种写法在 Swing 里很常见,好处是每个功能模块互不干扰,坏处是窗口开多了任务栏会爆炸。如果你想让界面看起来稍微专业一点,可以把JFrame换成JDesktopPane加JInternalFrame,让子窗口在主窗口内部浮动,但源码包大概率没做这层封装,直接弹新窗口。

3.2 GoodsManageInterFrm:套餐的增删改查

GoodsManageInterFrm负责套餐管理,核心是一个JTable加几个JButton。表格的数据来自t_goods表的SELECT *查询,用DefaultTableModel填充。新增套餐的按钮会弹出一个GoodsAddInterFrm,里面是几个JTextField对应套餐名称、价格、描述、库存,点保存时执行INSERT INTO t_goods。修改和删除则是先选中表格某一行,拿到id,再执行UPDATE或DELETE。

这里有一个新手很容易翻车的地方:JTable的getSelectedRow()返回的是视图行号,如果你对表格做了排序(RowSorter),视图行号和模型行号会对不上。源码包里一般没做排序,所以直接table.getSelectedRow()再table.getValueAt(row, 0)拿 id 是没问题的。但如果你自己加了排序功能,就得用table.convertRowIndexToModel(row)转换一下,否则删错数据是分分钟的事。

// 删除套餐的典型写法 int row = goodsTable.getSelectedRow(); if (row == -1) { JOptionPane.showMessageDialog(this, "请先选中一行"); return; } // 如果加了 RowSorter,这行必须加 // row = goodsTable.convertRowIndexToModel(row); int goodsId = Integer.parseInt(goodsTable.getValueAt(row, 0).toString()); String sql = "DELETE FROM t_goods WHERE id = ?"; try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { ps.setInt(1, goodsId); int affected = ps.executeUpdate(); if (affected > 0) { JOptionPane.showMessageDialog(this, "删除成功"); refreshTable(); // 重新查询并刷新表格 } } catch (SQLException e) { e.printStackTrace(); JOptionPane.showMessageDialog(this, "删除失败:" + e.getMessage()); }

用PreparedStatement而不是Statement拼接字符串,除了防 SQL 注入,还能避免套餐名称里带单引号导致语法错误。refreshTable()方法一般是重新执行SELECT然后用tableModel.setRowCount(0)清空再逐行addRow,不要直接new一个新的DefaultTableModel塞给JTable,那样会丢失已有的监听器。

3.3 DealOrderInterFrm 与 CheckOrderInterFrm:订单处理与查询

DealOrderInterFrm是管理员处理订单的界面,通常左边一个订单列表,右边一个明细列表,选中某个订单后右边显示这个订单点了哪些套餐、数量多少、小计多少。数据来自t_order和t_order_detail的联表查询:

SELECT o.id, u.username, o.order_time, o.total_price, o.status FROM t_order o JOIN t_user u ON o.user_id = u.id WHERE o.status = 0 ORDER BY o.order_time DESC;

status = 0表示待处理,管理员点“处理完成”按钮后执行UPDATE t_order SET status = 1 WHERE id = ?。CheckOrderInterFrm则是查所有订单,不加status条件,可能还带一个按用户名或日期筛选的查询框。这两个界面的技术难点不在 SQL,而在 Swing 的事件监听和表格刷新。常见做法是给JTable加一个ListSelectionListener,当选中行变化时,从t_order_detail里查对应order_id的明细,填充右边的表格。

注意:ListSelectionListener会触发两次(一次是旧行取消选中,一次是新行选中),如果你在监听器里直接查数据库,会多跑一次无用查询。可以在监听器开头加if (!e.getValueIsAdjusting())判断,只在最终选中时执行。

3.4 AccountModifyInterFrm:改密码的边界条件

AccountModifyInterFrm的逻辑很简单:三个密码框(旧密码、新密码、确认新密码),点确认后先查t_user表验证旧密码是否正确,再UPDATE新密码。这里有两个边界条件源码包可能没处理:一是新密码和确认密码不一致时要拦截,二是新密码不能为空。如果没做这两层校验,用户点确认后要么报 SQL 错误,要么把密码改成空字符串,下次登录直接进不去。我一般会在按钮事件里先做前端校验,通过了再走数据库。

String oldPwd = new String(oldPasswordField.getPassword()); String newPwd = new String(newPasswordField.getPassword()); String confirmPwd = new String(confirmPasswordField.getPassword()); if (newPwd.isEmpty()) { JOptionPane.showMessageDialog(this, "新密码不能为空"); return; } if (!newPwd.equals(confirmPwd)) { JOptionPane.showMessageDialog(this, "两次输入的新密码不一致"); return; } // 再走数据库校验旧密码和更新

JPasswordField.getPassword()返回的是char[],用new String()转一下再用,不要用getText(),那个方法在JPasswordField里已经废弃了,虽然还能用但会报警告。

4. 顾客模块拆解:点餐流程、订单查看与 UserOrderFrm 的数据流转

4.1 UserOrderFrm 的界面布局与点餐逻辑

UserOrderFrm是顾客登录后的主界面,一般分三块:左边是套餐列表(JTable或JList),中间是数量选择(JSpinner或JTextField),右边是已选购物车(另一个JTable)。顾客选中一个套餐,输入数量,点“加入购物车”,数据先存在内存里(比如一个List<OrderItem>),点“提交订单”时才批量写入t_order和t_order_detail。

这个“先内存后数据库”的设计是有道理的:如果每加一个菜就写一次数据库,顾客改主意想删掉就得再写一次删除,事务管理会很麻烦。放在内存里,最后一次性提交,用Connection.setAutoCommit(false)开事务,要么全成功要么全回滚。

// 提交订单的核心逻辑 Connection conn = null; try { conn = DBUtil.getConnection(); conn.setAutoCommit(false); // 开启事务 // 1. 插入订单主表 String orderSql = "INSERT INTO t_order (user_id, total_price, status) VALUES (?, ?, 0)"; PreparedStatement orderPs = conn.prepareStatement(orderSql, Statement.RETURN_GENERATED_KEYS); orderPs.setInt(1, currentUserId); orderPs.setBigDecimal(2, totalPrice); orderPs.executeUpdate(); // 2. 拿到刚插入的订单 id ResultSet rs = orderPs.getGeneratedKeys(); int orderId = 0; if (rs.next()) { orderId = rs.getInt(1); } // 3. 批量插入订单明细 String detailSql = "INSERT INTO t_order_detail (order_id, goods_id, quantity, subtotal) VALUES (?, ?, ?, ?)"; PreparedStatement detailPs = conn.prepareStatement(detailSql); for (OrderItem item : cartList) { detailPs.setInt(1, orderId); detailPs.setInt(2, item.getGoodsId()); detailPs.setInt(3, item.getQuantity()); detailPs.setBigDecimal(4, item.getSubtotal()); detailPs.addBatch(); } detailPs.executeBatch(); conn.commit(); // 提交事务 JOptionPane.showMessageDialog(this, "下单成功"); } catch (SQLException e) { if (conn != null) { try { conn.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } } e.printStackTrace(); JOptionPane.showMessageDialog(this, "下单失败:" + e.getMessage()); } finally { if (conn != null) { try { conn.setAutoCommit(true); conn.close(); } catch (SQLException e) { e.printStackTrace(); } } }

Statement.RETURN_GENERATED_KEYS是 JDBC 里拿自增主键的标准做法,比再查一次SELECT MAX(id)靠谱得多,并发场景下不会拿错。addBatch()和executeBatch()是批量插入的优化,购物车里东西多的时候比一条条executeUpdate快一个数量级。

4.2 订单查看与状态流转

顾客查看订单的界面一般叫CheckOrderInterFrm或者直接在UserOrderFrm里加一个标签页。查询语句按user_id过滤:

SELECT o.id, o.order_time, o.total_price, o.status FROM t_order o WHERE o.user_id = ? ORDER BY o.order_time DESC;

status字段在顾客端显示为“待处理”或“已完成”,管理员在DealOrderInterFrm里改的就是这个字段。这里有一个数据一致性的小坑:如果管理员正在处理订单的同时顾客又下了一单,两边看到的列表可能不同步。Swing 桌面应用没有 WebSocket 那种实时推送,常见做法是顾客端加一个“刷新”按钮,手动重新查询。课程设计层面这样够用了,不用上定时器轮询。

4.3 注册与登录的数据校验

RegisterFrm负责顾客注册,核心是往t_user表插一条user_type = 0的记录。这里必须做用户名唯一性校验,否则两个顾客注册同一个用户名,登录时SELECT会返回两条记录,ResultSet.next()只取第一条,另一个人的密码就永远对不上。校验方式有两种:一是先SELECT COUNT(*) FROM t_user WHERE username = ?,返回 0 才允许插入;二是直接依赖数据库的UNIQUE约束,插入时捕获SQLIntegrityConstraintViolationException。我一般两种都做,前端查一次给用户友好提示,数据库约束兜底防并发。

LogOnFrm登录时根据user_type决定跳转:

String sql = "SELECT id, user_type FROM t_user WHERE username = ? AND password = ?"; PreparedStatement ps = conn.prepareStatement(sql); ps.setString(1, username); ps.setString(2, password); ResultSet rs = ps.executeQuery(); if (rs.next()) { int userId = rs.getInt("id"); int userType = rs.getInt("user_type"); if (userType == 1) { new AdminFrm().setVisible(true); } else { new UserOrderFrm(userId).setVisible(true); } this.dispose(); // 关掉登录窗口 } else { JOptionPane.showMessageDialog(this, "用户名或密码错误"); }

this.dispose()只释放当前窗口的资源,不会退出整个 JVM,因为新开的AdminFrm或UserOrderFrm还在事件调度线程里跑着。如果你用System.exit(0),整个程序直接退出,新窗口也活不了。

5. 避坑与排查:从驱动报错到中文乱码的五个血泪现场

5.1 现象:启动时报java.lang.ClassNotFoundException: com.mysql.cj.jdbc.Driver

原因:项目没有引入 MySQL 8.0 的 JDBC 驱动 jar 包,或者引入的是老版本的com.mysql.jdbc.Driver。MySQL 8.0 的驱动类名从com.mysql.jdbc.Driver变成了com.mysql.cj.jdbc.Driver,老驱动连 8.0 的库会直接报认证插件错误。

解决:在项目 Libraries 里添加mysql-connector-java-8.0.28.jar,或者 Maven 里加对应依赖。如果已经加了还报错,检查 Build Path 里 jar 包是不是只加到了 Modulepath 而不是 Classpath,Eclipse 里这两个路径的类加载器不一样。

5.2 现象:连接数据库时报The server time zone value '???ú±ê×??±' is unrecognized

原因:JDBC URL 里没指定serverTimezone,MySQL 8.0 驱动尝试从服务器读取时区,但服务器时区是中文乱码或者不认识的格式,驱动解析失败。

解决:在 URL 里显式加serverTimezone=Asia/Shanghai。如果加了还报错,检查 MySQL 的my.ini或my.cnf里default-time-zone配置,或者直接在 MySQL 命令行执行SET GLOBAL time_zone = '+8:00';。

5.3 现象:插入中文数据后,在 MySQL 里查出来是问号???

原因:数据库、表、连接三层的字符集不一致。常见情况是数据库建的时候用了latin1,或者 JDBC URL 里没加characterEncoding=utf8。

解决:建库时指定DEFAULT CHARACTER SET utf8mb4,JDBC URL 里加characterEncoding=utf8,MySQL 配置文件里character-set-server=utf8mb4。三层都对齐之后,中文乱码基本绝迹。

5.4 现象:点餐提交订单后,t_order里有记录但t_order_detail是空的

原因:插入订单主表后没有正确获取自增主键,orderId是 0 或者默认值,导致明细插入时外键关联失败,但代码里没开事务,主表插入成功了,明细插入报错被吞掉。

解决:用Statement.RETURN_GENERATED_KEYS拿自增 id,并且把主表和明细的插入放在同一个事务里,任何一步失败都rollback。检查t_order_detail的外键约束是否生效,如果外键没建,明细插入不会报错但数据是脏的。

5.5 现象:管理员处理订单后,顾客端刷新还是显示“待处理”

原因:顾客端的JTable数据是登录时查一次就缓存住了,没有重新查询数据库。Swing 的JTable不会自动感知数据库变化。

解决:在顾客端加一个“刷新”按钮,点击时重新执行SELECT并fireTableDataChanged()。如果不想加按钮,可以在窗口获得焦点时(WindowFocusListener)自动刷新,但课程设计层面手动刷新就够了。

6. 进阶技巧:用 DBUtil 单例加连接池把 JDBC 代码从重复劳动里捞出来

源码包里最常见的写法是每个InterFrm里都写一遍DriverManager.getConnection,改数据库密码的时候要全局搜索替换,漏一个就报错。我一般会做两层封装:第一层是前面写的DBUtil工具类,第二层是引入一个轻量连接池,比如 HikariCP 或者 Druid。课程设计不强制用连接池,但用了之后代码会干净很多,而且面试的时候被问到“MySQL 的数据库连接池”也有东西可讲。

HikariCP 的引入很简单,Maven 加依赖:

<dependency> <groupId>com.zaxxer</groupId> <artifactId>HikariCP</artifactId> <version>5.0.1</version> </dependency>

然后把DBUtil改成从连接池取连接:

import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; import javax.sql.DataSource; import java.sql.Connection; import java.sql.SQLException; public class DBUtil { private static final DataSource DATA_SOURCE; static { HikariConfig config = new HikariConfig(); config.setJdbcUrl("jdbc:mysql://localhost:3306/restaurant_db" + "?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true" + "&characterEncoding=utf8"); config.setUsername("root"); config.setPassword("你的密码"); config.setMaximumPoolSize(10); // 最大连接数 config.setMinimumIdle(2); // 最小空闲连接 config.setConnectionTimeout(30000); // 连接超时 30 秒 config.setIdleTimeout(600000); // 空闲回收 10 分钟 DATA_SOURCE = new HikariDataSource(config); } public static Connection getConnection() throws SQLException { return DATA_SOURCE.getConnection(); } }

maximumPoolSize设 10 对桌面应用来说绰绰有余,Swing 是单线程事件调度模型,同一时刻不会有几十个并发查询。connectionTimeout设 30 秒是防止数据库挂了之后界面卡死,超时后抛异常,JOptionPane弹个提示框比无限等待友好得多。idleTimeout控制空闲连接多久被回收,桌面应用访问频率低,设 10 分钟比较合理。

用上连接池之后,每个InterFrm里的DBUtil.getConnection()调用方式不变,但底层从每次新建物理连接变成了从池里借,用完close()是归还而不是真正关闭。这里有一个血泪经验:如果你在代码里手动conn.close()之后又拿同一个conn去执行查询,连接池会报Connection is closed,因为归还后的连接可能已经被分配给别的线程了。所以close()之后不要再碰那个Connection对象,这是铁律。

验证连接池是否生效,可以在 MySQL 命令行执行SHOW STATUS LIKE 'Threads_connected';,程序启动后这个值应该稳定在minimumIdle附近,而不是每次查询都涨。如果每次操作都涨,说明连接池没配好,可能还在用DriverManager。

从那以后我每次拿到一个 JDBC 项目,第一件事就是把DBUtil抽出来,第二件事就是看有没有连接池。没有的话,哪怕课程设计不要求,我也会顺手加上,因为后面调试的时候少受很多罪。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/2 17:59:12

基于QT的GIS源码改造:从编译环境搭建到坐标转换与地图渲染实践

简介&#xff1a;一套基于Qt开发的GIS地理信息系统完整源码&#xff0c;面向GIS初学者、C/Qt开发者以及需要搭建桌面地图应用的工程师&#xff0c;是理解GIS底层机制与工程实践的直观样例。项目运用Qt的信号与槽、模型/视图架构&#xff0c;实现了从地图渲染到空间分析的一整套…

作者头像 李华
网站建设 2026/10/2 17:58:41

RabbitMQ Connection 与 Channel 底层原理深度解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/2 17:58:35

UFS3.1协议实战解析:WB、HPB与E2EDP三大增强机制详解

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/2 17:58:28

MetaSKILL 与 SKILL:多视角深度综述与 TaoToken 统一接入实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/2 17:53:00

tlb invlpgb_kernel_range_flush

invlpgb_kernel_range_flush 是 AMD 广播 TLB 失效&#xff08;INVLPGB&#xff09;补丁集中&#xff0c;用于刷新内核地址空间一段范围 TLB 条目的专用函数。它通过硬件广播指令替代传统的 IPI 风暴&#xff0c;显著降低了内核 TLB 刷新的开销。核心作用&#xff1a;内核范围的…

作者头像 李华