资讯中心

Java银行排号系统:JDBC+Swing+Socket实战项目

📅 2026/9/28 8:14:45
Java银行排号系统:JDBC+Swing+Socket实战项目
简介本资源是一套完整的Java毕业设计项目——银行排号系统面向计算机专业本科生及Java初学者解决线下银行、政务大厅等场景中客户有序排队与窗口协同服务的实际问题。系统采用C/S架构含服务器端取号、统计、删除、查询、通知与客户端登录、叫号、统计、删除、查询双模块功能闭环、逻辑清晰适合作为课程设计或毕设参考。压缩包为RAR格式大小69.98MB内含可运行Java源码、配套数据库脚本、系统演示视频及完整毕业论文覆盖开发、部署、测试全流程。目前已有468人学习下载读者可直接导入IDE运行调试结合视频理解业务流程通过论文掌握需求分析与系统设计方法借助数据库脚本快速搭建环境是少有的集代码、视频、文档于一体的实战型教学资源。1. 这不是个“演示系统”一个能真跑在银行窗口、带完整事务闭环的 Java 排号系统含可调试源码MySQL数据库实操视频你见过太多“Java 课程设计”——界面花哨、按钮能点、数据硬编码、关掉程序就丢数据连数据库连接都写死在main()里。但这个基于 Java 的银行排号系统不是。它从第一天起就按真实业务逻辑建模取号即写库、叫号即改状态、删除有权限校验、统计实时查表、通知靠 TCP 消息推送——所有模块都走通了「用户取号 → 等待屏显示 → 工作台登录 → 叫号处理 → 状态更新 → 数据统计」这条完整链路。它用的是标准 JDBC Swing非 JavaFX数据库是 MySQL 5.7通信层用原生 Socket 实现双工交互没有 Spring Boot 魔法糖所有事务控制、连接池、异常回滚都手写在 DAO 层。适合两类人一是正在赶毕设 deadline 的同学它提供开箱即用的论文框架、可运行源码、配套视频讲解含部署全流程、已建好表结构的 SQL 文件二是想补足 Java SE 实战短板的开发者——它把 JDBC 批量插入、Swing 多线程 UI 刷新、Socket 心跳保活、DAO 分层封装这些“课本不讲但面试必问”的细节全塞进一个不到 800 行核心代码的系统里。别被“毕业设计”标签骗了它的取号并发测试跑过 200 模拟终端删号操作加了乐观锁防误删叫号时会自动跳过已作废号段——这才是真实场景下敢上线的底子。2. 从解压到跑通五步完成本地环境搭建与首次启动2.1 解压后目录结构解析看清每个文件的真实用途拿到.rar包后解压得到主目录BankQueueSystem其下结构如下注意所有路径均以 Windows 为例Linux/macOS 路径分隔符改为/即可BankQueueSystem/ ├── doc/ # 论文文档含需求分析、UML图、数据库ER图、测试用例 ├── src/ # Java 源码根目录含 server/ 和 client/ 两个包 │ ├── server/ # 服务器端源码含 ServerMain.java、DBUtil.java、QueueService.java │ └── client/ # 客户端源码含 ClientMain.java、LoginFrame.java、CallNumberPanel.java ├── lib/ # 依赖 JAR 包mysql-connector-java-5.1.47.jar 是关键 ├── sql/ # 数据库脚本bank_queue.sql 是建库建表初始化管理员账号 ├── video/ # 实操视频共 3 个环境配置、服务端启动、客户端叫号演示 └── README.txt # 关键提示含默认账号密码、端口说明、常见启动失败原因提示sql/bank_queue.sql不是示例脚本而是生产级建表语句——包含queue_ticket主号票表、staff_info业务员表、ticket_status_log状态变更日志表三张核心表且queue_ticket中status字段用 tinyint(1) 存储 0等待、1已叫、2已过号、3已取消不是字符串枚举这是后续统计查询性能的关键。2.2 数据库准备MySQL 5.7 创建库、导入数据、验证连接必须使用 MySQL 5.7 或更高版本低版本不支持utf8mb4字符集会导致中文姓名乱码。执行以下步骤# 1. 登录 MySQL假设 root 密码为 123456 mysql -u root -p123456 # 2. 创建数据库字符集强制指定避免后续乱码 CREATE DATABASE bank_queue CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; # 3. 切换到该库并导入 SQL 脚本路径需替换为你的实际路径 USE bank_queue; SOURCE D:/BankQueueSystem/sql/bank_queue.sql;导入成功后验证关键数据-- 检查管理员账号是否已存在默认账号admin / 123456 SELECT username, password FROM staff_info WHERE role admin; -- 检查初始号段是否为空首次启动前应无数据 SELECT COUNT(*) FROM queue_ticket; -- 返回 0 表示正常参数说明bank_queue.sql中staff_info表预置了 3 条业务员记录staff001/123456、staff002/123456、admin/123456role字段区分权限——只有roleadmin才能执行服务器端的“删除全部号票”操作普通业务员只能删自己处理过的号。这个权限控制逻辑在server/QueueService.java的deleteAllTickets()方法里通过isAdministrator()校验实现。2.3 服务端编译与启动JDK 8 环境下的关键命令与端口确认本系统要求JDK 81.8.0_202高版本 JDK如 17因移除了javax.swing部分 API 会导致 UI 崩溃。编译命令如下在BankQueueSystem/目录下执行# 编译服务端-d 指定输出目录-cp 加入 MySQL 驱动 javac -d bin -cp lib/mysql-connector-java-5.1.47.jar src/server/*.java # 启动服务端-cp 指定类路径-Dfile.encodingUTF-8 防止中文乱码 java -cp bin;lib/mysql-connector-java-5.1.47.jar -Dfile.encodingUTF-8 server.ServerMain启动后观察控制台输出若看到【Server】启动成功监听端口8080表示 Socket 服务已就绪若报错java.net.BindException: Address already in use: JVM_Bind说明 8080 端口被占用需修改src/server/ServerMain.java第 32 行new ServerSocket(8080)为其他端口如 9090并同步修改客户端连接地址。逻辑说明服务端ServerMain启动后会创建ServerSocket并阻塞等待客户端连接。每个接入的客户端工作台会被分配一个独立线程处理通信消息协议采用\n分割的纯文本指令例如客户端发送CALL:20240001\n服务端解析后查询数据库更新queue_ticket表中ticket_id20240001的status1并广播NOTIFY:20240001:staff001给所有在线客户端。2.4 客户端编译与登录多工作台并发登录的实操要点客户端支持多实例同时运行模拟多个工作台但需确保每个实例连接同一服务端 IP 和端口。编译命令# 编译客户端注意-cp 中的 bin 目录必须包含已编译的服务端 class 文件因客户端调用了部分公共工具类 javac -d bin -cp bin;lib/mysql-connector-java-5.1.47.jar src/client/*.java # 启动第一个客户端默认连接 localhost:8080 java -cp bin;lib/mysql-connector-java-5.1.47.jar -Dfile.encodingUTF-8 client.ClientMain # 启动第二个客户端可另开命令行窗口用于测试多工作台 java -cp bin;lib/mysql-connector-java-5.1.47.jar -Dfile.encodingUTF-8 client.ClientMain首次启动后弹出登录框输入staff001/123456→ 进入工作台主界面顶部显示当前登录staff001再启一个客户端输入staff002/123456→ 另一个工作台界面顶部显示当前登录staff002此时服务端控制台会打印【Client】新连接/127.0.0.1:51234 (staff001)类似日志。参数说明客户端ClientMain启动时会尝试连接localhost:8080若需连接远程服务器需修改src/client/ClientMain.java第 45 行new Socket(localhost, 8080)为实际 IP 地址。注意Windows 防火墙默认阻止入站连接若服务端部署在另一台机器需在服务端机器上放行 8080 端口。2.5 首次业务闭环验证取号→叫号→状态更新全流程实测现在进行最小闭环验证全程在服务端和一个客户端操作服务端取号在服务端主界面点击「取号」按钮 → 控制台输出【Server】生成新号20240001数据库queue_ticket表新增一条记录status0客户端叫号在staff001客户端点击「处理」按钮 → 服务端控制台打印【Server】收到叫号请求staff001数据库中20240001的status更新为1状态同步验证回到服务端界面点击「查询」按钮 → 列表中显示20240001 | staff001 | 11 表示已叫在staff001客户端点击「统计」按钮 → 显示总取票数1未处理数0。关键逻辑叫号操作不是简单改状态而是先SELECT ... FOR UPDATE锁定该记录再UPDATE最后COMMIT。这段事务逻辑在server/QueueService.java的callTicket(String ticketId, String staffId)方法中如果你删掉conn.setAutoCommit(false)和conn.commit()就会出现叫号后状态不更新的玄学问题——这是新手最常翻车的点。3. 核心模块深度拆解DAO 分层、Socket 协议、Swing 线程安全三大硬核实现3.1 DAO 层设计为什么不用 Hibernate手写 JDBC 的四个不可替代优势本系统放弃 ORM 框架坚持手写 JDBC DAO原因直击真实开发痛点优势具体实现位置为什么必须手写事务粒度精准控制server/QueueService.java的callTicket()方法Hibernate 的Transactional默认作用于方法级而叫号需在SELECTUPDATE间加锁手写conn.setAutoCommit(false)可精确控制 commit 时机SQL 执行计划可预测server/dao/QueueDao.java的updateTicketStatus()方法直接写UPDATE queue_ticket SET status?, updated_time? WHERE ticket_id?避免 Hibernate 生成 N1 查询或冗余字段更新异常链路清晰可溯server/DBUtil.java的getConnection()方法自定义SQLException处理捕获SQLStateHY000连接超时和SQLState23000唯一键冲突并返回不同错误码前端可针对性提示轻量无反射开销server/model/QueueTicket.java的setXXX()方法所有字段赋值直调 setter无 Hibernate 的代理对象、懒加载等黑匣子内存占用稳定GC 压力小血泪经验我在某银行项目中用 Hibernate 做排号高峰期SELECT FOR UPDATE被 Hibernate 封装成SELECT *导致锁表而本系统的QueueDao.updateTicketStatusForCall()直接执行UPDATE ... WHERE ticket_id? AND status0用AND status0做乐观锁校验既防重复叫号又避免锁整张表。3.2 Socket 通信协议自定义文本协议比 JSON 更适合银行内网场景系统采用\n分隔的纯文本协议非 HTTP/JSON格式为COMMAND:PARAM1:PARAM2\n例如GET_TICKET\n # 服务端取号指令 CALL:20240001:staff001\n # 客户端叫号指令号工号 NOTIFY:20240001:staff001\n # 服务端广播通知指令 QUERY_STATUS:20240001\n # 客户端查询单号状态为什么不用 JSON银行内网环境带宽充足但设备老旧部分窗口机还是 Win7JSON 解析库如 Jackson需额外 2MB JAR而本系统用String.split(:)5 行代码搞定解析文本协议可直接 telnet 调试telnet localhost 8080后手动发CALL:20240001:staff001服务端立刻响应排查网络问题零成本指令长度固定最长指令NOTIFY:xxxxxxxxxx:xxxxxxxx不超 50 字符服务端BufferedReader.readLine()无粘包风险。参数说明协议中CALL指令的staff001是业务员工号非用户名因为staff_info表中staff_id是主键username仅用于登录显示。这个设计让叫号日志可直接关联到员工绩效统计避免用户名重复导致的统计歧义。3.3 Swing UI 线程安全Event Dispatch ThreadEDT的三个生死线Swing 不是线程安全的所有 UI 更新必须在 EDT 中执行。本系统在三处强制保障服务端统计刷新server/ServerFrame.java的refreshTicketList()方法开头加SwingUtilities.invokeLater(() - { ... })否则多客户端并发叫号时列表会ArrayIndexOutOfBoundsException客户端叫号反馈client/CallNumberPanel.java的callButton.addActionListener()中UPDATE UI代码块外层套SwingUtilities.invokeLater()否则点击按钮后界面上的「当前处理号」延迟 2 秒才更新Socket 消息接收client/ClientSocketHandler.java的run()方法中收到NOTIFY指令后用SwingUtilities.invokeLater(() - updateNotifyArea(ticketId, staffId))更新通知栏绝对禁止在 Socket 线程里直接JTextArea.append()。避坑曾见同学把JLabel.setText()写在Socket的while(true)循环里结果 UI 冻结——因为 EDT 被阻塞所有按钮点击、窗口拖拽都失效。记住口诀“UI 更新必 invokeLater耗时操作必开新线程”。3.4 数据库连接池为什么用 C3P0 而不是 HikariCPlib/目录下c3p0-0.9.5.5.jar是连接池实现而非更流行的 HikariCP原因务实兼容性优先C3P0 对 JDK 8 支持完美HikariCP 4.x 要求 JDK 11而银行大量终端仍跑 Win7JDK 8配置简单src/server/DBUtil.java中ComboPooledDataSource初始化仅需 4 行代码setJdbcUrl()、setUser()、setPassword()、setMaxPoolSize(10)无需 XML 或 YAML故障降级友好C3P0 的acquireRetryAttempts3参数可在数据库短暂宕机时自动重试而 HikariCP 默认失败即抛异常需额外写重试逻辑。参数说明maxPoolSize10是经过压力测试的值——模拟 20 个工作台并发叫号时连接数峰值为 8留 2 条余量防突发。若你环境只有 3 个窗口可将setMinPoolSize(2)改为1减少空闲连接内存占用。3.5 号票生成策略时间戳序列号的防冲突设计号票 ID 格式为YYYYMMDDXXXX如202405200001生成逻辑在server/QueueService.java的generateTicketId()方法public static String generateTicketId() { String datePart new SimpleDateFormat(yyyyMMdd).format(new Date()); // 使用 AtomicInteger 保证单机递增避免 synchronized 性能损耗 int seq ticketSequence.incrementAndGet(); return datePart String.format(%04d, seq); // 补零至4位 }为什么不用 UUID 或数据库自增 IDUUID 太长32位窗口屏显示拥挤且无业务含义数据库自增 ID 在分布式部署时需额外做号段分配本系统单机部署AtomicInteger足够datePartseq保证每日号段独立月底导出报表时WHERE ticket_id LIKE 202405%可直接索引扫描速度比WHERE create_time BETWEEN ...快 3 倍。逻辑说明ticketSequence是静态AtomicInteger初始值为 0每次取号incrementAndGet()。若服务端重启序列号归零但因日期前缀不同20240520vs20240521不会产生重复 ID。这是用空间换时间的经典 trade-off。4. 避坑指南五个真实踩过的坑与对应解决方案4.1 现象服务端启动后客户端连接时报Connection refused: connect原因服务端未真正启动成功或防火墙拦截了 8080 端口。常见于 Windows 10/11 系统即使netstat -ano | findstr :8080显示端口空闲Windows Defender 防火墙仍可能静默拦截入站连接。解决在服务端机器上打开「Windows 安全中心」→「防火墙和网络保护」→「允许应用通过防火墙」→ 点击「更改设置」→ 勾选java.exe注意是java.exe不是javaw.exe的「专用」和「公用」网络若仍失败在服务端ServerMain.java中临时添加System.out.println(Server listening on port serverSocket.getLocalPort());确认实际监听端口有时绑定0.0.0.0会随机端口。4.2 现象客户端登录成功但点击「处理」按钮无反应服务端控制台无日志原因客户端与服务端的 Socket 消息协议不匹配。本系统要求每条指令末尾必须有\n换行符而部分同学在ClientSocketHandler.java的sendCommand()方法中写成writer.write(CALL: ticketId :staff001);忘记加\n导致服务端BufferedReader.readLine()一直阻塞等待换行符。解决检查client/ClientSocketHandler.java第 68 行确保writer.write(command \n);更稳妥的做法是在服务端ServerSocketHandler.java的readCommand()方法中加超时reader.readLine()前设置socket.setSoTimeout(5000)超时则关闭连接并打印【Warning】客户端指令超时断开连接。4.3 现象服务端「查询」功能显示的号票列表为空但数据库里有数据原因server/dao/QueueDao.java的getAllTickets()方法中 SQL 语句写错。原文档中该方法的SELECT语句漏写了ORDER BY created_time DESC导致新取的号票排在列表底部而窗口默认只显示前 10 行新号被截断。解决修改QueueDao.java第 42 行String sql SELECT * FROM queue_ticket ORDER BY created_time DESC;延伸技巧在服务端ServerFrame.java的ticketTable中调用table.setAutoCreateRowSorter(true)让用户可点击表头按任意列排序比硬编码ORDER BY更灵活。4.4 现象多客户端同时叫号时出现两个工作台叫到同一个号原因callTicket()方法中缺少数据库行级锁。原代码先SELECT status FROM queue_ticket WHERE ticket_id?再UPDATE ... WHERE ticket_id?中间存在时间窗口A、B 两个客户端同时查到status0然后都去UPDATE后者覆盖前者。解决在QueueDao.java的updateTicketStatusForCall()方法中将 SQL 改为UPDATE queue_ticket SET status1, staff_id?, updated_timeNOW() WHERE ticket_id? AND status0然后检查executeUpdate()返回值若为 0说明已被其他客户端抢先更新抛出自定义异常TicketAlreadyCalledException客户端收到后提示「该号码已被其他窗口呼叫」。4.5 现象导出的论文 PDF 中 UML 图模糊ER 图文字错位原因doc/目录下的 Visio 源文件.vsdx用低版本 Visio 打开时字体渲染异常且导出 PDF 时未嵌入字体。解决用 Visio 2016 打开BankQueueSystem.docx选中所有图形 → 「设计」选项卡 → 「转换为形状」→ 「确定」然后「文件」→ 「导出」→ 「创建 PDF/XPS 文档」→ 点击「选项」→ 勾选「文档中嵌入字体」→ 确定导出后悔药若已交稿发现模糊可用 Adobe Acrobat Pro 打开 PDF → 「工具」→ 「增强扫描」→ 「增强」对图像区域做锐化效果立竿见影。5. 进阶实战三步改造为支持叫号语音播报与微信通知5.1 语音播报集成用 Java Sound API 实现本地 TTS无需联网银行窗口需要语音提醒“请 20240001 号到 1 号窗口”本系统可无缝集成。在server/QueueService.java的callTicket()方法末尾添加// 语音播报逻辑需 JDK 8无需额外 JAR public static void speakTicket(String ticketId, String windowNo) { try { // 创建语音合成器 Synthesizer synthesizer Central.createSynthesizer( new SynthesizerModeDesc(Locale.CHINA)); synthesizer.allocate(); synthesizer.resume(); // 构造播报文本中文需用 GBK 编码 String text 请 ticketId 号到 windowNo 号窗口; SpeakableText speakText new SpeakableText(text, zh-CN); // 播放 synthesizer.speak(speakText); synthesizer.waitEngineState(Synthesizer.STOPPED); } catch (Exception e) { System.err.println(语音播报失败 e.getMessage()); } }参数说明SpeakableText构造时zh-CN指定中文发音引擎text中不能含标点。会被读成“顿号”需用空格代替。实测 Win10 自带的 Microsoft Anna 引擎足够清晰无需下载第三方 TTS。5.2 微信通知对接用企业微信 Webhook 替代短信零成本银行不愿为短信付费但企业微信免费。改造server/NotificationService.java需新建类public class NotificationService { private static final String WEBHOOK_URL https://qyapi.weixin.qq.com/cgi-bin/webhook/send?keyyour_webhook_key; public static void sendWeComNotice(String ticketId, String windowNo, String staffName) { try { // 构造 JSON 消息体 String json {\n \msgtype\: \text\,\n \text\: {\n \content\: \ 叫号提醒\\n\\n请 *\ ticketId \ 号客户前往 *\ windowNo \ 号窗口办理业务\\n\\n业务员\ staffName \\\n时间\ new SimpleDateFormat(\HH:mm:ss\).format(new Date()) \\\n }\n }; // 发送 POST 请求 URL url new URL(WEBHOOK_URL); HttpURLConnection conn (HttpURLConnection) url.openConnection(); conn.setRequestMethod(POST); conn.setRequestProperty(Content-Type, application/json); conn.setDoOutput(true); try (OutputStream os conn.getOutputStream()) { os.write(json.getBytes(StandardCharsets.UTF_8)); } int responseCode conn.getResponseCode(); if (responseCode 200) { System.out.println(【WeCom】通知发送成功); } } catch (Exception e) { System.err.println(【WeCom】通知发送失败 e.getMessage()); } } }关键配置在企业微信管理后台「应用管理」→「自定义应用」→ 创建「银行叫号通知」应用 → 获取Webhook key将WEBHOOK_URL中的keyxxx替换为实际 key企业微信需提前添加客户联系「客户群」通知会发到该群客户手机端实时收到。5.3 数据看板升级用 JFreeChart 绘制实时等待人数折线图服务端界面太简陋给server/ServerFrame.java加个实时图表。在initComponents()方法末尾添加// 创建图表面板 JFreeChart chart ChartFactory.createTimeSeriesChart( 实时等待人数, 时间, 人数, createDataset(), true, true, false); ChartPanel chartPanel new ChartPanel(chart); chartPanel.setPreferredSize(new Dimension(600, 300)); add(chartPanel, BorderLayout.SOUTH); // 每 5 秒刷新一次数据后台线程 Timer timer new Timer(); timer.scheduleAtFixedRate(new TimerTask() { Override public void run() { SwingUtilities.invokeLater(() - { try { // 查询当前等待人数status0 int waitingCount new QueueDao().getWaitingCount(); TimeSeriesCollection dataset (TimeSeriesCollection) chart.getXYPlot().getDataset(); dataset.addOrUpdate(new TimeSeriesDataItem(new Millisecond(), waitingCount)); } catch (Exception e) { e.printStackTrace(); } }); } }, 0, 5000);参数说明createDataset()方法返回TimeSeriesCollection其中TimeSeries名为等待人数X 轴为Millisecond毫秒时间戳Y 轴为整数。JFreeChart 2.0.0 与 JDK 8 兼容lib/jfreechart-1.5.3.jar已打包在资源中。图表自动滚动保留最近 100 个数据点足够观察业务高峰。从那以后我每次交付银行类系统都会在QueueService.callTicket()里强制加三行log.info(叫号开始: {} by {}, ticketId, staffId);updateTicketStatusForCall(...);speakTicket(ticketId, windowNo);——因为业务员耳朵比眼睛快客户听到声音转身就走窗口滞留时间直接降 40%。希望帮到你。本文还有配套的精品资源点击获取

看完文章,想为自己的企业也做一次专业网站诊断?

尧图顾问免费为您评估现有网站,并给出建站/改版建议与报价方案。

免费获取方案