简介一份基于Java Swing与MySQL的ATM取款机模拟系统完整源码包面向Java初学者、毕业设计及课程实训场景。资源涵盖用户注册/登录、密码修改、存款、取款、转账、交易记录查询等核心银行功能演示了Swing图形界面搭建、JDBC数据库交互、事务处理、密码加密存储及异常处理等关键知识点。包内含14个Java源码文件、1个SQL数据库脚本、4个运行所需JAR依赖库含C3P0连接池、MySQL驱动、DbUtils工具类及Eclipse工程配置文件总计22个文件压缩包大小1.98MB解压后可直接导入IDE运行。已有1354人学习使用适合想通过完整项目快速掌握Java GUI编程与MySQL整合开发的学习者。通过运行本系统读者可直观理解从界面设计到数据持久化的全流程实现思路便于二次开发与课设参考。1. 一份能跑起来的 Swing ATM 源码别急着双击先看清它给你准备了什么专业课结课的前一晚很多同学都经历过这样的场景从网上下载一个 Java 课程设计源码包解压、导入、改数据库然后祈祷能跑起来。这份 Swing 实现的 ATM 取款机系统带 sql 文件就是典型的 Java 桌面课程设计资源——界面层用 Swing数据层接 MySQL工程里还带了 C3P0 连接池和 DbUtils 工具库压缩包里的 atm.sql 负责把数据库表和测试数据一次性建好。它适合两类人一类是要交 Java 课程设计、需要快速看懂完整系统怎么串起来的学生另一类是刚学完 JDBC想知道真实项目里数据库访问层怎么写的人。如果你是冲着「下载就能跑」来的建议先花十分钟看完这篇因为直接导入双击运行大概率会在数据库连接这一步翻车。2. 工程拆解与导入四个 jar、一个 atm.sql、一份 c3p0 配置各自的活拿到压缩包第一件事不是解压导入而是先弄清楚里面每一块是干什么的。这个包的结构很清晰.project、.classpath、.settings 是 Eclipse 的工程标记文件src/cn 下面是 Java 源码lib 目录里躺着四个 jar根目录的 atm.sql 是数据库初始化脚本。把职责分清之后后面排错才知道该去哪个文件里找问题。2.1 压缩包里的东西拆开看从 Eclipse 工程标记到 lib 四件套先看 lib 目录这四个 jar 决定了这个项目的数据访问方式缺一个都会在启动时报错。我把它们列成一张表你导入前先核对一遍。jar 文件版本职责c3p00.9.5.5数据库连接池负责管理 JDBC 连接mchange-commons-java0.2.19c3p0 的运行依赖少了它 c3p0 起不来mysql-connector-java5.1.48MySQL 的 JDBC 驱动Java 程序靠它和 MySQL 通信commons-dbutils1.7封装 JDBC 的 DAO 工具库简化查询和更新操作这套组合是前些年 Java 桌面教学项目里非常常见的标配。裸写 JDBC 的话每一次查询都要自己管 Connection、PreparedStatement、ResultSet 的开关代码里全是重复样板用 c3p0 做连接池加上 DbUtils 的 QueryRunner数据访问层能缩到几行。你在这个项目里看到的 DAO 代码大概率就是 QueryRunner 配合各种 ResultSetHandler 的写法。源码在 src/cn 下面典型的分包方式是 dao、service、ui、util 四层。ui 放 Swing 界面类dao 放数据库操作service 放业务逻辑util 放连接池工具类。不用急着把所有类都看完先找到带 main 方法的入口类常见命名是 MainFrame 或 LoginFrame双击 Run As Java Application 就能看到登录窗口。2.2 导入前的三个前置检查JDK、MySQL 服务与 Build Path我一般会劝人先做三个检查再导入省得报错的时候分不清是环境问题还是代码问题。第一条是 JDK 版本这个项目用 Java 8 语法写的装了 8 或更高版本就能跑命令行验证一下java -version mysql --versionjava -version 输出里有 1.8 或更高版本号就说明 JDK 没问题mysql --version 能输出版本号说明 MySQL 客户端工具可用。第二条是确认本机 MySQL 服务真的在运行Windows 上可以在服务管理器里看Linux/macOS 上执行systemctl status mysql或service mysql status。第三条是导入后检查 Build Path 里有没有把 lib 下四个 jar 全部加进去Eclipse 导入后右键项目 → Build Path → Configure Build Path在 Libraries 标签下核对。导入步骤是 Eclipse 里 File → Import → Existing Projects into Workspace选压缩包解压后的目录Eclipse 会自动识别 .project 文件。如果导入后代码报红大多数情况是 Build Path 里没加 jar或者 JDK 版本不匹配先解决这两类问题再往下走。工程能编译通过后下一步不是急着看界面而是先把数据库立起来——连接配置写错了界面跑起来也白搭。3. 数据库层先立稳执行 atm.sql、配好 C3P0、再用 DbUtils 写 DAO这个项目的核心不在 Swing 界面在于它把「界面操作 → DAO 层 → MySQL」这条链路完整串起来了。要让它跑通数据库层必须先立稳。顺序错了会踩很多冤枉坑有人先改代码后执行 SQL启动时报表不存在有人直接双击 atm.sql 用记事本打开看到一堆乱码以为脚本坏了。按下面三步走十分钟内数据库层就能稳定工作。3.1 先手动执行一遍 atm.sql建库、建表与测试数据一次到位atm.sql 是数据库初始化脚本里面按顺序包含了建库语句、建表语句和 INSERT 测试数据。拿到脚本先不要急着整段执行用记事本或 IDE 打开往下翻找到 CREATE DATABASE、USE、CREATE TABLE 这几组关键词确认库名、字符集和你本机的 MySQL 版本兼容。常见的情况是脚本用 utf8mb4 建库如果你的 MySQL 是 5.5 或更老版本utf8mb4 支持不完整需要手动把字符集改成 utf8。确认没问题后命令行执行mysql -u root -p atm.sql输入 root 密码后脚本就会跑完。如果你用的是 Navicat、DataGrip 这类图形工具直接打开 atm.sql 文件选择目标连接后执行整个脚本效果一样。执行完进 MySQL 看一眼mysql -u root -p SHOW DATABASES;看到新增的数据库后再用USE 库名; SHOW TABLES;看表结构。这类 ATM 系统最常出现的三张表是用户表、账户表和交易流水表常见字段设计如下。表常见字段说明userid, username, password登录凭证accountcard_no, balance, user_id卡号、余额与用户关联trade_logid, type, amount, create_time, account_no交易流水执行完脚本后我习惯先SELECT * FROM user LIMIT 5;看看测试数据长什么样重点看密码是明文还是密文。这直接决定了后面登录逻辑里密码比对是怎么写的也关系到你后面改密码功能时的判断。3.2 C3P0 连接池配置四个参数决定了能不能连上这个项目用 c3p0 管理数据库连接配置文件是 c3p0-config.xml放在 classpath 根目录下。如果压缩包里没带这个文件就需要自己补一份这是 c3p0 的约定俗成类路径下找不到它连接池会直接报错。标准配置长这样c3p0-config default-config property namejdbcUrljdbc:mysql://localhost:3306/atm_db?useSSLfalseamp;characterEncodingutf8/property property nameuserroot/property property namepassword123456/property property namedriverClasscom.mysql.jdbc.Driver/property property nameinitialPoolSize3/property property namemaxPoolSize10/property /default-config /c3p0-configjdbcUrl 里的 localhost:3306 是 MySQL 地址和端口3306 是 MySQL 默认端口基本不用改atm_db 要替换成 atm.sql 里实际创建的库名。useSSLfalse 表示不使用 SSL 连接本地开发环境没必要开characterEncodingutf8 解决中文乱码。user 和 password 改成你本机的 MySQL 账号密码。driverClass 是 com.mysql.jdbc.Driver对应 mysql-connector-java 5.1.48 这个 jar如果你后面把驱动换成了 8.x这里要改成 com.mysql.cj.jdbc.Driver。配置写好后一般会在 util 包里写一个工具类来获取数据源常见写法是import com.mchange.v2.c3p0.ComboPooledDataSource; import javax.sql.DataSource; public class C3p0Util { private static ComboPooledDataSource ds new ComboPooledDataSource(); public static DataSource getDataSource() { return ds; } }new ComboPooledDataSource() 不带参数时c3p0 会自动去 classpath 根目录找 c3p0-config.xml读取 default-config 里的配置。这里最容易出的问题是配置文件没放到 src 根目录导致运行时抛 No such config 异常这一点后面避坑章节会详细说。工具类写好后整个项目的数据源就只有这一处DAO 层所有类都从这里拿连接。3.3 DAO 层用 DbUtils把 SQL 从界面代码里捞出来有了连接池DAO 层写起来就很清爽。commons-dbutils 的 QueryRunner 帮我们省掉了手动开关 Connection、PreparedStatement、ResultSet 的样板代码而且它支持把 ? 占位符参数传给 SQL避免字符串拼接导致 SQL 注入。查询余额的方法可以写成import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.handlers.ScalarHandler; import java.math.BigDecimal; import java.sql.SQLException; public class AccountDao { private QueryRunner runner new QueryRunner(C3p0Util.getDataSource()); public BigDecimal queryBalance(String cardNo) throws SQLException { String sql SELECT balance FROM account WHERE card_no ?; return runner.query(sql, new ScalarHandlerBigDecimal(), cardNo); } }QueryRunner 构造时传入数据源query 方法内部自动从连接池拿连接、执行查询、关闭连接。ScalarHandler 表示只取结果集第一行第一列的值这里就是 balance 字段传参 cardNo 会替换 SQL 里的 ? 占位符而不是拼进 SQL 字符串这是防止 SQL 注入的标准姿势。BigDecimal 是金额字段的正确类型float/double 做加减会有精度问题这点在很多课程设计里是被忽略的。这一层搞定后界面上的登录按钮、取款按钮才有可靠的数据支撑。数据库层的正确顺序是先执行 atm.sql 建库再配 c3p0-config.xml最后写 DAO。反过来做每一步都会产生让你误以为是代码问题的报错。4. 界面与业务闭环Swing 面板切换、取款校验与转账事务数据库层稳了接下来看界面怎么和业务串起来。Swing 项目最怕的不是界面复杂而是界面代码和业务逻辑揉在一起——按钮事件里直接写 SQL、查询数据库的耗时操作把界面卡死、面板切换时引用空对象。这一章把常见的设计套路和一个最核心的事务写法讲清楚。4.1 Swing 界面怎么串一个主窗口配 CardLayout 切换面板看这类项目的 ui 包典型的结构是一个主 JFrame内部放一个 JPanel 作为容器容器使用 CardLayout 布局登录界面、主菜单、存款面板、取款面板、转账面板、流水查询面板都是独立的 JPanel通过 CardLayout 切换。这样做的好处是切换界面时不需要反复创建和销毁窗口数据在面板间传递也方便。切换面板的核心代码只有两行CardLayout cardLayout (CardLayout) mainPanel.getLayout(); cardLayout.show(mainPanel, depositPanel);show 方法第一个参数是容器第二个参数是目标面板的约束名。这个约束名是在添加面板时通过 add 方法设定的比如mainPanel.add(depositPanel, depositPanel)名字必须完全一致否则运行时抛 IllegalArgumentException。我见过不少新手在这个名字上栽跟头——add 时写的和 show 时写的差一个字母报错信息又不直观排查半天。登录流程一般是用户输入卡号和密码点登录按钮按钮事件里调用 UserDao 的查询方法核对密码匹配成功后跳转到主菜单面板匹配失败弹 JOptionPane 提示。这里要注意一件事登录校验的耗时操作如果直接写在按钮事件里界面会短暂卡住本地数据库感觉不明显但换成远程数据库就能察觉。正确做法是放到 SwingWorker 里异步执行后面避坑章节会展开。4.2 取款先验余额转账必须一个事务核心业务逻辑这样写取款逻辑比很多人想象中简单关键不是「先查余额再扣款」这种两步走而是用一条条件更新的 SQL 把校验和扣款合并成一步public int decreaseBalance(Connection conn, String cardNo, BigDecimal amount) throws SQLException { String sql UPDATE account SET balance balance - ? WHERE card_no ? AND balance ?; QueryRunner runner new QueryRunner(); return runner.update(conn, sql, amount, cardNo, amount); }这条 SQL 的意思是只有余额不小于取款金额时才执行扣款。UPDATE 返回受影响行数返回 0 就说明余额不足或账号不存在。这种做法是原子的不需要先 SELECT 再 UPDATE避免了两个操作之间数据被其他线程修改的并发问题。QueryRunner 构造时不传数据源而是把 Connection 作为第一个参数传进去目的是让这个方法参与外部事务——连接由调用方传入并统一提交或回滚。转账是 ATM 系统里最有教学价值的功能因为它天然要求事务。转出扣款和转入加款是两个独立的 UPDATE任何一个失败都不能让另一个生效否则就会出现钱扣了对方没收到或者对方收到了但转出方没扣钱。标准写法是public void transfer(String fromCard, String toCard, BigDecimal amount) throws Exception { Connection conn null; try { conn C3p0Util.getDataSource().getConnection(); conn.setAutoCommit(false); AccountDao dao new AccountDao(); dao.decreaseBalance(conn, fromCard, amount); dao.increaseBalance(conn, toCard, amount); dao.insertTradeLog(conn, TRANSFER, amount, fromCard); conn.commit(); } catch (Exception e) { if (conn ! null) { conn.rollback(); } throw e; } finally { if (conn ! null) { conn.close(); } } }setAutoCommit(false) 是关键它让连接进入手动提交模式之后的每个 SQL 都先缓存在事务里直到调用 commit 才真正生效。中间任何一步抛出异常catch 块里调用 rollback之前的扣款和加款全部撤销。finally 里关闭连接把连接还给连接池而不是真正断开——这是连接池和裸 JDBC 在资源管理上的本质区别。这条转账方法就是整个项目里最值得反复读的代码读懂它就理解了事务的四个特性为什么在真实系统里这么重要。5. 常见问题避坑五条让你半夜改代码的翻车现场以下五条一半是我自己跑类似项目时踩过的一半是帮别人排错时见到的。按出现频率排序每一条都是「现象 → 原因 → 解决」的结构你可以直接对照自己的报错信息查。5.1 连不上 MySQL驱动类、SSL 与时区三连现象一运行时报ClassNotFoundException: com.mysql.jdbc.Driver程序在加载 JDBC 驱动这一步直接退出。大多数情况下是 lib 下的 mysql-connector-java-5.1.48.jar 没有被加入 Build PathEclipse 里光把 jar 放在 lib 目录还不够必须右键项目 → Build Path → Add JARs 手动添加。这也是新手最容易忽略的一步。现象二驱动类能加载但连数据库时报Communications link failure或 SSL 握手失败。经常是本机 MySQL 是 8.x 版本而驱动还是 5.1.48 导致的。MySQL 8 默认的认证插件是 caching_sha2_password5.1 系列驱动兼容性不好。解决方法是登录 MySQL 把用户认证方式改回 mysql_native_passwordALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 你的密码; FLUSH PRIVILEGES;或者在 jdbcUrl 里加上useSSLfalseserverTimezoneAsia/Shanghai把加密和时区问题一起解决。我一般两个都做省得改完一个又冒出来另一个。5.2 数据库连上了但界面一堆问号字符集问题现象登录成功后界面上的按钮、提示文字正常但查询出来的用户姓名、交易备注全是问号。原因多半是数据库表字符集不是 utf8mb4或者连接的 characterEncoding 参数没设置。atm.sql 建表时如果指定了 latin1插入的中文在查询时就会乱。解决检查 MySQL 表字符集执行SHOW TABLE STATUS LIKE user;看 Collation 列如果是 latin1 开头就把表转成 utf8mb4ALTER TABLE user CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;然后确认 c3p0-config.xml 的 jdbcUrl 里有characterEncodingutf8。这两个地方都改对中文才不会再翻车。5.3 按钮点下去界面卡死数据库操作写在 EDT 线程里现象点击查询余额按钮后整个窗口变成「未响应」状态几秒后才恢复数据库数据量大了甚至永久卡死。原因是按钮事件里直接调用了 DAO 的数据库查询方法而 Swing 的界面事件回调运行在 EDTEvent Dispatch Thread线程上这个线程被数据库 IO 阻塞了界面就无法重绘和响应鼠标操作。解决把耗时操作从事件线程挪走。标准做法是 SwingWorkernew SwingWorkerBigDecimal, Void() { Override protected BigDecimal doInBackground() { return accountDao.queryBalance(cardNo); } Override protected void done() { try { BigDecimal balance get(); balanceLabel.setText(余额 balance); } catch (Exception e) { JOptionPane.showMessageDialog(frame, 查询失败); } } }.execute();doInBackground 里做数据库查询done 里更新界面Swing 保证 done 在 EDT 上执行不会触发线程安全问题。这是 Swing 项目里一个很重要的习惯课设里不写没人说你错但真实应用里这是必须的。5.4 c3p0 报 No such config配置文件没进 classpath现象程序启动时抛No such config: 默认配置或者提示找不到 default-config。原因是 c3p0-config.xml 文件没有放在 classpath 根目录。Eclipse 里把文件放进 src 根目录后构建时才会复制到 classes 输出目录如果直接放在项目根目录或者 lib 里classpath 里找不到它。解决把 c3p0-config.xml 放在 src 根目录然后 Project → Clean 重新构建再启动。顺带检查一下文件的编码如果之前用记事本改过且带 BOM 头c3p0 解析 XML 有时会出怪问题用 IDE 重新保存为 UTF-8 无 BOM 格式更保险。5.5 找不到主类或入口类main 方法不在预期位置现象导入工程后想运行但 Run As 里没有 Java Application 选项或者直接提示找不到主类。原因是启动类不在 src/cn 下的某个包里可能在 src 根目录也可能被放在了别的模块里还有一种情况是导入时 Eclipse 没有把 src 识别为源码目录。解决右键 src 目录 → Build Path → Use as Source Folder确认源码目录被正确标记。然后在 src/cn 下翻找带public static void main的类看到类声明旁边有绿色运行图标就说明主类可运行。如果实在找不到在项目里全局搜索public static void main五分钟内能定位。6. 验证与扩展三个信号确认跑通再补一道密码加密系统改到能启动、能登录、能存取款很多人就认为大功告成了。但我习惯再做三个验证动作确认它不是「表面跑通」关键业务真的正确。每完成一个验证就相当于给系统的正确性上了一道保险。第一个验证看连接池日志。启动程序时注意控制台输出c3p0 初始化成功后会打印连接池初始化的日志或者至少不会报任何配置错误。连接池都起不来后面所有数据库操作都是空谈。第二个验证做一次真实取款。登录后取 100 元然后用 MySQL 命令行查询SELECT * FROM trade_log ORDER BY create_time DESC LIMIT 5; SELECT balance FROM account WHERE card_no 你的卡号;流水表多了一条 type 为取款的记录余额比原来少了 100两个数据能对上说明业务闭环是通的。第三个验证做一次「故意失败」的转账转给一个不存在的卡号。这一步最能说明事务是否真的生效转账失败后查流水表不应该出现半条记录查两个账户余额都不应该发生变化。这三个信号全绿才敢说这个项目真的跑通了。之后我会建议顺手做一个扩展给密码加盐哈希。如果 atm.sql 里的密码是明文说明登录比对也是明文这在课程设计里能交差但作为练手有必要升级。用 MessageDigest 做 SHA-256 加盐MessageDigest md MessageDigest.getInstance(SHA-256); String salt UUID.randomUUID().toString().substring(0, 8); byte[] hash md.digest((salt password).getBytes(StandardCharsets.UTF_8)); String stored salt : HexFormat.of().formatHex(hash);验证时把盐拆出来重新哈希再比对数据库里即使备份泄露拿到的也不是原始密码。这是摘要里提到安全性时最值得落实的一处——桌面程序不涉及 HTTPS但密码加密存储是实打实该补的短板。如果这份带 SQL 的源码包就在你手上按上面的顺序走一遍从执行 atm.sql 到转账回滚测试二十分钟内能全部跑完。从那以后我每次拿到一个带 SQL 的 Java 桌面项目都强制走一遍「先执行 SQL → 再改连接配置 → 再跑登录 → 最后测转账回滚」的顺序四步全绿我才敢说这个项目能交付。希望你这次跑得比我第一次顺利。本文还有配套的精品资源点击获取