简介:这份资源面向计算机专业学生与Java初学者,提供一套基于C/S架构与Socket网络编程的银行排号系统完整实现方案,可用于课程设计、毕业设计或Java网络编程练手。压缩包为rar格式,约69.98MB,内含源码、LW论文文档、PPT演示文稿、数据库脚本及讲解视频,覆盖从需求分析到编码落地的全流程。论文部分详细论述了业务流程图、系统用例图、功能结构图、数据流程图与数据库E-R图,并围绕TCP/IP通信连接、Java线程编程等关键技术展开设计说明,帮助读者理解客户端与服务器端实时通信、取号与叫号互动的实现思路。源码可直接运行调试,配合讲解视频便于对照理解各模块职责与调用关系,数据库脚本则支撑排队信息的存储与查询。目前已有151人学习,适合需要完整项目案例、希望掌握Socket通信与多线程编程的读者参考借鉴。
1. 从一次银行网点排队说起:这套 Java 排号系统到底能跑出什么效果
上周去网点办业务,取号机吐出一张 A037,抬头一看叫号屏还停在 A012,中间隔了二十多个人,大厅里坐满了低头刷手机的客户。这种场景对做 Java 课程设计或者想练手桌面应用的人来说,其实就是一个现成的需求模型:取号、排队、叫号、窗口状态、队列刷新,一整套逻辑闭环。这套基于 Java 实现的银行排号系统,核心就是用 Swing 做界面、用数据库做持久化,把线下网点的排队流程搬到桌面上跑起来。它适合三类人:一是正在找 Java 课程设计案例的学生,二是想补一个完整 CRUD + 队列调度项目的初级开发者,三是需要一套能演示、能讲、能改的教学素材。资源包里除了源码,还带了 LW 文档、PPT、数据库脚本和讲解视频,基本覆盖了从写代码到答辩的全流程。下面我按实际拆包和跑通的顺序,把这份资源讲清楚。
2. 拆开资源包先看什么:模块划分与数据库表结构
2.1 源码目录里到底有几个可运行入口
拿到压缩包解压后,第一件事不是急着导入 IDE,而是先看目录结构。常见做法是打开src目录,确认包名层级。这套系统一般会分成com.bank.queue这样的根包,下面再拆ui、service、dao、entity、util几个子包。ui放 Swing 窗体,service写业务逻辑,dao负责数据库增删改查,entity是实体类,util放数据库连接工具。入口类通常叫MainFrame或者AppStart,里面有一个main方法,直接new一个主窗体然后setVisible(true)。
先别管界面好不好看,先确认这个入口能不能编译。如果用的是 Eclipse 或者 IntelliJ IDEA,导入时选「Existing Maven Project」还是「Java Project」取决于包里有没有pom.xml。很多课程设计资源是纯 Java Project,没有 Maven,那就手动把lib目录下的 jar 包加到 Build Path。常见依赖就两个:数据库驱动(比如mysql-connector-java)和可能用到的 UI 主题包。这一步翻车的点在于驱动版本和数据库版本对不上,后面排错章节会细说。
2.2 数据库表结构决定了排号逻辑的上限
排号系统的核心表一般不超过五张,但每张表的字段设计直接决定了业务能不能跑通。我拆过的同类项目里,最常见的表是ticket(号票表)、window(窗口表)、queue_record(排队记录表)、user(操作员表)、counter_config(窗口配置表)。下面这张表是我根据资源包里常见的 SQL 脚本整理出来的字段对照,实际字段名可能略有差异,但逻辑一致。
| 表名 | 关键字段 | 作用 | 注意点 |
|---|---|---|---|
| ticket | id, ticket_no, status, create_time | 存储取号信息 | status 用 0/1/2 表示等待/叫号/完成 |
| window | id, window_no, status, current_ticket | 窗口状态与当前叫号 | status 区分空闲/忙碌/暂停 |
| queue_record | id, ticket_id, window_id, call_time | 叫号历史 | 用于统计和回查 |
| user | id, username, password, role | 登录与权限 | 密码一般明文或简单 MD5 |
| counter_config | id, window_count, prefix | 窗口数量和号票前缀 | 影响取号格式如 A001 |
建表语句一般在sql目录下的.sql文件里,用 Navicat 或者命令行source执行即可。这里有个细节:如果脚本里写了DROP TABLE IF EXISTS,第一次执行没问题,但如果你已经跑过一次并且改了数据,再执行就会清空。我一般会先备份一份空库,或者把DROP改成CREATE TABLE IF NOT EXISTS。
2.3 从取号到叫号的完整数据流
理解数据流比看懂单个类更重要。取号时,TicketService会先查ticket表当天最大号,然后加一生成新号,插入一条status=0的记录,同时刷新界面上的等待列表。叫号时,WindowService根据当前窗口号去ticket表里找status=0且create_time最早的那条,把status改成1,更新window表的current_ticket,再往queue_record插一条历史。完成业务后,把status改成2,窗口置为空闲。
这个流程里最容易出问题的是并发。Swing 是单线程事件模型,但如果你开了多个窗口同时叫号,数据库层面没有加锁的话,两个窗口可能叫到同一个号。常见做法是在SELECT的时候加FOR UPDATE,或者用synchronized包住叫号方法。课程设计级别通常不会压测,但答辩时老师如果问一句「多窗口同时叫号怎么办」,你得能答上来。
3. 把环境跑起来:JDK、数据库与 Swing 界面初始化
3.1 JDK 版本与编码格式的坑
这套系统大概率是用 JDK 8 写的,因为 Swing 项目在 JDK 8 上最稳。如果你本地装的是 JDK 17 或 21,编译时可能遇到javax.swing相关 API 没变,但sun.*包被移除的问题。常见做法是直接装一个 JDK 8,在 IDE 里把项目 SDK 切过去。如果不想换 JDK,至少把编译级别设成 8,并且注意--add-opens参数,不过 Swing 一般不需要。
编码格式是另一个高频翻车点。源码里的中文注释和界面标签如果是 GBK 编码,而你的 IDE 默认 UTF-8,打开就是乱码。解决方式:在 IDEA 右下角把文件编码切成 GBK,或者用file命令确认原文件编码后批量转换。我一般会在导入前先看README或者LW文档里有没有写编码说明,没有的话就按 GBK 试一次。
3.2 数据库连接配置改哪几个参数
数据库配置通常在一个db.properties或者DBUtil.java里。如果是 properties 文件,长这样:
# 数据库连接配置 driver=com.mysql.cj.jdbc.Driver url=jdbc:mysql://localhost:3306/bank_queue?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8 username=root password=123456如果是写死在 Java 代码里,就找DriverManager.getConnection那一行。参数说明:bank_queue是数据库名,要和建表脚本里CREATE DATABASE的名字一致;serverTimezone不写的话,MySQL 8 会报时区错误;characterEncoding=utf8防止中文乱码。改完这些,先写一个main方法测试连接:
public class TestConn { public static void main(String[] args) { try (Connection conn = DBUtil.getConnection()) { System.out.println("数据库连接成功:" + conn.getCatalog()); } catch (Exception e) { e.printStackTrace(); } } }这段代码只做一件事:拿到连接并打印当前库名。如果报ClassNotFoundException,说明驱动 jar 没加;如果报Access denied,说明用户名密码不对;如果报Unknown database,说明库没建。按报错信息逐个排,别一上来就改代码。
3.3 启动主界面后先验证哪三个动作
主界面跑起来后,别急着点所有按钮。先做三个验证:第一,取号按钮能不能生成新号并显示在等待列表;第二,叫号按钮能不能把等待列表的第一个号移到当前窗口;第三,完成按钮能不能把当前号标记为已完成并释放窗口。这三个动作对应了INSERT、UPDATE、UPDATE三条 SQL,只要数据库里能看到数据变化,说明主流程通了。
如果取号后列表不刷新,常见原因是JTable的fireTableDataChanged没调用,或者DefaultTableModel没有重新setDataVector。Swing 的表格刷新是新手最容易卡住的地方,记住一句话:改完数据模型必须通知视图,否则界面就是黑匣子,你永远不知道数据到底进没进去。
4. 排号核心逻辑:队列调度、窗口状态与并发处理
4.1 取号逻辑:号票生成与重复号规避
取号看起来简单,但号票格式和每日重置是两个容易忽略的点。常见号票格式是「前缀 + 三位数字」,比如 A001、A002。前缀可能按业务类型分,比如个人业务 A、对公业务 B。生成逻辑一般是这样:
public String generateTicketNo(String prefix) { // 查询当天该前缀的最大号 String sql = "SELECT MAX(ticket_no) FROM ticket WHERE ticket_no LIKE ? AND DATE(create_time) = CURDATE()"; String maxNo = jdbcTemplate.queryForObject(sql, String.class, prefix + "%"); if (maxNo == null) { return prefix + "001"; } int num = Integer.parseInt(maxNo.substring(1)) + 1; return prefix + String.format("%03d", num); }逻辑说明:先按前缀和当天日期查最大号,如果没有就返回 001,否则截取数字部分加一再用%03d补零。参数说明:prefix是业务类型前缀,CURDATE()是 MySQL 函数,保证每天从 001 重新开始。这里有个边界:如果当天取号量超过 999,%03d会变成四位数,号票格式就乱了。课程设计一般不会遇到,但你可以把%03d改成%04d或者加一个判断。
4.2 叫号逻辑:先来先服务与窗口分配
叫号的核心是「先来先服务」,也就是按create_time升序取第一条status=0的记录。SQL 大概长这样:
SELECT * FROM ticket WHERE status = 0 ORDER BY create_time ASC LIMIT 1 FOR UPDATE;FOR UPDATE的作用是锁住这一行,防止两个窗口同时叫到同一个号。执行完这条查询后,紧接着更新status=1和window表的current_ticket,然后提交事务。如果不用FOR UPDATE,在高并发下会出现「一号双叫」,这是排号系统最典型的 bug。课程设计答辩时,如果你能主动说出这个点,基本能加分。
窗口分配的逻辑有两种:一种是固定窗口叫号,操作员点哪个窗口就叫哪个窗口的号;另一种是自动分配,系统找空闲窗口然后叫号。资源包里大概率是第一种,因为实现简单。如果你想改成自动分配,可以在WindowService里加一个方法,查询status=0的窗口列表,取第一个然后调用叫号。
4.3 窗口状态管理:空闲、忙碌与暂停的切换
窗口状态一般用三个值表示:0 空闲、1 忙碌、2 暂停。操作员登录后选择窗口,窗口状态变成忙碌;叫号后当前号显示在窗口上;完成业务后窗口变回空闲;如果操作员要休息,点暂停,状态变成 2,此时不参与叫号分配。状态切换的代码不复杂,但要注意界面上的按钮联动。比如窗口忙碌时,「叫号」按钮可用,「暂停」按钮可用;窗口空闲时,「叫号」按钮应该置灰或者提示先取号。
我见过一个翻车案例:操作员点了暂停,但叫号逻辑没有过滤status=2的窗口,结果暂停的窗口还在叫号。原因就是查询空闲窗口时只查了status=0,但叫号方法直接按窗口号取号,没校验状态。解决方式是在叫号方法入口加一个状态判断,如果不是忙碌状态就抛异常或者弹提示。
4.4 用 Swing 定时器刷新叫号屏
叫号屏需要实时显示当前叫号,但 Swing 不会自动刷新。常见做法是用javax.swing.Timer,每隔一秒查一次数据库当前叫号,然后更新JLabel。代码大概这样:
Timer timer = new Timer(1000, e -> { // 查询当前窗口的当前号 String currentNo = windowService.getCurrentTicket(windowNo); currentNoLabel.setText(currentNo == null ? "暂无叫号" : currentNo); }); timer.start();参数说明:1000是毫秒,也就是一秒刷新一次。如果数据库压力大,可以改成 2000 或者 3000。注意Timer是在 EDT 线程里执行的,查询数据库如果慢会卡界面,所以更稳妥的做法是把查询放到SwingWorker里,查完再回 EDT 更新标签。课程设计级别直接用Timer也能跑,但你要知道这个边界。
5. 避坑与排查:从乱码到连接池的五个血泪经验
5.1 中文乱码:数据库、IDE、界面三处都要查
现象:取号后界面显示「????」或者「锟斤拷」。原因:数据库字符集、连接 URL 字符集、IDE 文件编码三者不一致。解决:先确认数据库和表的字符集是utf8mb4,再确认连接 URL 带了characterEncoding=utf8,最后确认 IDE 文件编码和源码实际编码一致。三处都对了,乱码基本消失。如果还有,检查 Swing 组件的字体是否支持中文,默认字体在某些系统上会显示方块。
5.2 驱动报错:MySQL 8 的时区与 SSL 参数
现象:启动时报The server time zone value '?D1ú±ê×?ê±??' is unrecognized。原因:MySQL 8 驱动要求显式指定时区。解决:在 URL 后面加serverTimezone=Asia/Shanghai。如果还报 SSL 警告,加useSSL=false。这两个参数是 MySQL 8 连接字符串的标配,缺一个都可能启动失败。另外注意驱动类名,MySQL 8 用com.mysql.cj.jdbc.Driver,5.x 用com.mysql.jdbc.Driver,写错了直接ClassNotFoundException。
5.3 叫号重复:没有加锁导致的并发 bug
现象:两个窗口同时点叫号,叫到了同一个号。原因:查询和更新之间没有事务隔离,两个线程都查到了同一条status=0的记录。解决:在查询语句上加FOR UPDATE,并且把查询和更新放在同一个事务里。如果用的是 MyBatis,确保SqlSession没有自动提交。课程设计如果不涉及多窗口并发,这个问题不会暴露,但答辩时老师一问就露馅。
5.4 界面卡死:EDT 线程里做数据库查询
现象:点叫号后界面卡住几秒,按钮点不动。原因:Swing 的事件处理是在 EDT 线程里执行的,如果在按钮回调里直接查数据库,查询期间 EDT 被阻塞,界面就无法响应。解决:把数据库操作放到SwingWorker的doInBackground里,查完在done方法里更新界面。如果不想改架构,至少给按钮加一个「处理中」的提示,让用户知道系统没死。
5.5 数据库连接泄漏:Connection 没关导致连接数耗尽
现象:跑一段时间后报Too many connections。原因:每次操作都new一个 Connection,但用完没close。解决:用 try-with-resources 包住 Connection、Statement、ResultSet,或者写一个DBUtil.close方法在 finally 里调用。更好的做法是引入连接池,比如 Druid 或者 HikariCP,但课程设计用简单封装也能过。我一般会在DBUtil里加一个静态方法,统一关闭资源,避免每个 DAO 都写一遍。
6. 进阶改造:把排号系统从课程设计推到能演示的水平
如果你已经跑通了基础版,想让这套系统在答辩或者演示时更稳,有三个改造方向值得做。第一个是加连接池。把DBUtil里的DriverManager换成 HikariCP,配置maximumPoolSize=10,这样多窗口并发时不会频繁创建连接。代码改动很小,但稳定性提升明显。第二个是加日志。用java.util.logging或者 Log4j 把取号、叫号、完成三个动作记下来,出问题时能回溯。第三个是加一个简单的统计面板,按窗口统计叫号数量,用JFreeChart画个柱状图,演示效果直接拉满。
验证改造是否成功,我一般会做一个小压测:开三个窗口,每个窗口循环叫号 50 次,看数据库里有没有重复号、有没有status卡在 1 的记录、连接数有没有暴涨。如果三个指标都正常,说明改造到位了。还有一个细节:Swing 的JTable在数据量大时会变慢,如果等待列表超过 100 条,建议加分页或者只显示前 20 条。这个点课程设计一般不要求,但你要是想在演示时显得专业,可以顺手加上。
从那以后我每次拿到这类课程设计资源,都会先跑一遍取号、叫号、完成三个动作,再开两个窗口试并发,最后看数据库连接有没有关。这套习惯帮我省了很多答辩现场翻车的机会。希望帮到你。
本文还有配套的精品资源,点击获取