简介:这是一套面向Java初学者与课程设计学习者的银行管理系统项目源码,基于IntelliJ IDEA开发,采用Java Swing构建图形界面,以MySQL作为后台数据存储,实现管理员与顾客两类角色的完整业务闭环。管理员端支持登录验证、添加与删除顾客、统计存储金额;顾客端涵盖登录、存款、取款、转账、修改密码、查看交易记录及安全退出等功能,适合作为Java面向对象编程、数据库应用与Swing界面开发的综合练习素材。资源包为rar压缩格式,共28个文件,约40KB,其中包含14个class编译文件、6个xml与2个yml配置、2个html页面、1个java源码及说明文档,覆盖项目结构、依赖配置与构建产物,便于直接导入IDEA运行调试。目前已有424人学习浏览,可帮助读者快速理解银行系统的模块划分、数据表设计与前后端交互逻辑,积累金融类软件开发的实战经验。
1. 从一张课程设计截图说起:Java 银行管理系统到底在练什么
很多人第一次见到「Java 银行管理系统(IDEA项目/JavaSwing界面/MySQL数据库)」这个标题,是在课程设计选题表或者简历项目栏里。它看起来像个玩具,但真正动手做过的人知道,这套东西把 Java 桌面开发最核心的三条链路一次性串了起来:Swing 负责把界面画出来,JDBC 负责把数据写进 MySQL,IDEA 负责把这两者组织成一个能跑、能调试、能打包的工程。它解决的不是「银行怎么运转」,而是「一个带登录、开户、存取款、转账、流水查询的桌面程序,怎么从零搭到能演示」。
适合谁?一是刚学完 Java 基础、面向对象编程,想找一个能写进简历的完整案例的在校生;二是想从控制台程序过渡到带界面、带数据库的 JavaWeb 项目完整案例的初学者;三是需要快速交付一个课程设计或实训作业的开发者。它不涉及高并发、分布式,但把「界面事件 → 业务逻辑 → 数据库持久化」这条主线走通了,后面换 Spring Boot + MyBatis 只是换框架,思路是一样的。
2. 环境与工程骨架:IDEA、MySQL、Swing 三件套怎么配才不翻车
2.1 为什么选 Swing 而不是 JavaFX,选 MySQL 而不是 SQLite
先说选型。Swing 是 JDK 自带的,不需要额外引入依赖,IDEA 社区版新建项目就能直接写,这对课程设计场景是决定性的优势——你不需要折腾模块化配置,也不需要处理 JavaFX 在 JDK 11 之后被剥离的问题。JavaFX 界面确实更现代,但一旦遇到「IDEA 运行 JavaWeb 项目配置」这类环境问题,排查成本会成倍上升。Swing 的组件模型虽然老,但JFrame、JPanel、JTable、JButton这套东西足够撑起银行管理系统的所有界面。
数据库选 MySQL 而不是 SQLite,理由更实际:MySQL 安装配置教程满地都是,mysql installer for windows一路下一步就能装好,而且银行管理系统天然需要多表关联——用户表、账户表、交易流水表,MySQL 的外键、事务、存储过程都能用上。SQLite 虽然零配置,但面试官看到你项目里写的是 MySQL,默认你对 JDBC、连接池、SQL 语句是有概念的。
提示:MySQL 5.7 和 8.0 在 JDBC 驱动类名和连接 URL 上有差异,5.7 用
com.mysql.jdbc.Driver,8.0 用com.mysql.cj.jdbc.Driver,URL 还要加时区参数。选一个版本从头到尾用,别中途换。
2.2 用 IDEA 建工程:目录结构与依赖引入
打开 IDEA,新建一个 Java 项目,不要选 Maven 或 Gradle,课程设计阶段用普通 Java 项目最省事。建好之后,在项目根目录下新建lib文件夹,把 MySQL 的 JDBC 驱动 jar 包(mysql-connector-java-8.0.xx.jar)复制进去,然后右键 jar 包选择Add as Library。这一步就是「idea怎么导入jar包」的标准操作,很多人卡在这里是因为只复制了文件没做Add as Library,结果编译时报ClassNotFoundException。
目录结构建议这样组织:
src/ com/bank/ entity/ // 实体类:User、Account、Transaction dao/ // 数据访问层:UserDao、AccountDao service/ // 业务逻辑层:BankService ui/ // 界面层:LoginFrame、MainFrame util/ // 工具类:DBUtil test/ // 测试入口:Main分层不是为了好看,是为了让你在调试时能快速定位问题。界面报错找ui,数据不对找dao,业务规则错了找service。如果所有代码都堆在一个类里,改一个存取款的逻辑要翻三百行,血泪经验。
2.3 数据库建表:三张表撑起整个系统
在 MySQL 里建一个库bank_db,字符集用utf8mb4,然后执行下面的建表语句。这三张表是整个系统的地基,字段设计直接决定了后面业务逻辑好不好写。
CREATE DATABASE bank_db DEFAULT CHARACTER SET utf8mb4; USE bank_db; -- 用户表:登录用 CREATE TABLE t_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL, real_name VARCHAR(50), create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); -- 账户表:一个用户可以有多个账户 CREATE TABLE t_account ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, card_no VARCHAR(20) NOT NULL UNIQUE, balance DECIMAL(15,2) DEFAULT 0.00, status TINYINT DEFAULT 1, -- 1正常 0冻结 FOREIGN KEY (user_id) REFERENCES t_user(id) ); -- 交易流水表:记录每一笔操作 CREATE TABLE t_transaction ( id INT PRIMARY KEY AUTO_INCREMENT, card_no VARCHAR(20) NOT NULL, type VARCHAR(20) NOT NULL, -- 存款/取款/转账 amount DECIMAL(15,2) NOT NULL, balance_after DECIMAL(15,2), target_card VARCHAR(20), trans_time DATETIME DEFAULT CURRENT_TIMESTAMP );balance用DECIMAL(15,2)而不是FLOAT或DOUBLE,因为金额计算不能有浮点误差,这是金融类项目的铁律。t_transaction表里存了balance_after,这样查流水时不用再关联账户表算余额,直接读就行,查询效率高很多。
3. 从登录到转账:Swing 界面与 JDBC 的完整对接
3.1 登录界面:事件监听与数据库校验
登录界面是整个系统的入口,用JFrame加几个JLabel、JTextField、JPasswordField和JButton就能搭出来。关键不在界面长什么样,而在按钮的ActionListener里怎么把用户输入和数据库里的记录做比对。
// LoginFrame.java 核心逻辑 loginBtn.addActionListener(e -> { String username = usernameField.getText().trim(); String password = new String(passwordField.getPassword()).trim(); if (username.isEmpty() || password.isEmpty()) { JOptionPane.showMessageDialog(this, "用户名和密码不能为空"); return; } UserDao userDao = new UserDao(); User user = userDao.login(username, password); if (user != null) { this.dispose(); // 关闭登录窗口 new MainFrame(user).setVisible(true); // 打开主界面 } else { JOptionPane.showMessageDialog(this, "用户名或密码错误"); } });UserDao.login方法里用PreparedStatement而不是Statement,这是防 SQL 注入的基本功。参数用?占位,然后ps.setString(1, username)依次赋值。很多人写课程设计时图省事用字符串拼接,结果输入' or '1'='1就能直接登录,这种代码放到简历里是减分项。
public User login(String username, String password) { String sql = "SELECT * FROM t_user WHERE username = ? AND password = ?"; try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { ps.setString(1, username); ps.setString(2, password); ResultSet rs = ps.executeQuery(); if (rs.next()) { User user = new User(); user.setId(rs.getInt("id")); user.setUsername(rs.getString("username")); user.setRealName(rs.getString("real_name")); return user; } } catch (SQLException ex) { ex.printStackTrace(); } return null; }DBUtil.getConnection()里把 URL、用户名、密码写死或者读配置文件都行,但要注意每次用完连接必须关闭。上面用了 try-with-resources,Connection和PreparedStatement会自动关闭,不用手动写finally块。这是 Java 7 之后的标准写法,比老教程里的finally关闭优雅得多。
3.2 主界面与账户操作:JTable 展示数据,事务保证一致性
主界面用JTabbedPane分几个标签页:账户信息、存款、取款、转账、交易流水。账户信息页用JTable展示当前用户的所有账户,数据从t_account表查出来塞进DefaultTableModel。
// 加载账户列表到表格 String[] columns = {"卡号", "余额", "状态"}; DefaultTableModel model = new DefaultTableModel(columns, 0); List<Account> accounts = accountDao.findByUserId(user.getId()); for (Account acc : accounts) { model.addRow(new Object[]{ acc.getCardNo(), acc.getBalance(), acc.getStatus() == 1 ? "正常" : "冻结" }); } accountTable.setModel(model);存款和取款逻辑相对简单,就是更新t_account的balance字段,同时往t_transaction插一条记录。但转账不一样,它涉及两个账户的余额变动,必须放在一个数据库事务里,要么都成功,要么都回滚。
public boolean transfer(String fromCard, String toCard, BigDecimal amount) { Connection conn = null; try { conn = DBUtil.getConnection(); conn.setAutoCommit(false); // 开启事务 // 扣款 String sql1 = "UPDATE t_account SET balance = balance - ? WHERE card_no = ? AND balance >= ?"; PreparedStatement ps1 = conn.prepareStatement(sql1); ps1.setBigDecimal(1, amount); ps1.setString(2, fromCard); ps1.setBigDecimal(3, amount); int rows1 = ps1.executeUpdate(); // 入账 String sql2 = "UPDATE t_account SET balance = balance + ? WHERE card_no = ?"; PreparedStatement ps2 = conn.prepareStatement(sql2); ps2.setBigDecimal(1, amount); ps2.setString(2, toCard); int rows2 = ps2.executeUpdate(); if (rows1 == 1 && rows2 == 1) { conn.commit(); return true; } else { conn.rollback(); return false; } } catch (SQLException e) { try { if (conn != null) conn.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } return false; } finally { try { if (conn != null) { conn.setAutoCommit(true); conn.close(); } } catch (SQLException e) { e.printStackTrace(); } } }扣款 SQL 里带了AND balance >= ?,这是防止余额不足时扣成负数。如果rows1返回 0,说明余额不够或者卡号不存在,直接回滚。conn.setAutoCommit(false)是事务的起点,commit()和rollback()是终点。注意在finally里要把autoCommit恢复成true再关闭连接,否则连接池复用时会出问题。
3.3 交易流水查询:条件筛选与分页思路
流水查询界面给用户一个卡号输入框和一个日期范围选择,点查询后从t_transaction表里捞数据。SQL 用WHERE card_no = ? AND trans_time BETWEEN ? AND ?,参数用PreparedStatement设置。
String sql = "SELECT * FROM t_transaction WHERE card_no = ? " + "AND trans_time BETWEEN ? AND ? ORDER BY trans_time DESC";如果数据量大,可以加分页,用LIMIT ? OFFSET ?。课程设计阶段数据量小,不分页也能跑,但简历上写了分页会显得更完整。ORDER BY trans_time DESC让最新的交易排在最前面,符合用户习惯。
4. 避坑与排查:那些让课程设计卡三天的典型问题
4.1 连接 MySQL 报Public Key Retrieval is not allowed
现象:IDEA 里运行程序,控制台抛出javax.net.ssl.SSLException: java.security.cert.CertificateException或者Public Key Retrieval is not allowed。
原因:MySQL 8.0 默认使用caching_sha2_password认证插件,JDBC 驱动在连接时需要获取公钥,但默认不允许。
解决:在 JDBC URL 后面加上allowPublicKeyRetrieval=true&useSSL=false。完整 URL 类似jdbc:mysql://localhost:3306/bank_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=false。serverTimezone不加也可能报时区错误,一并加上。
4.2 中文乱码:数据库、连接、界面三处都要统一
现象:界面上输入中文姓名,存进数据库变成???,或者查出来显示乱码。
原因:字符集不统一。数据库建库时没指定utf8mb4,JDBC URL 没加characterEncoding=utf8,Swing 组件没设置字体。
解决:建库语句写DEFAULT CHARACTER SET utf8mb4;JDBC URL 加useUnicode=true&characterEncoding=utf8;Swing 的JLabel、JButton如果显示中文方块,在main方法开头设置UIManager.setLookAndFeel为系统外观,或者手动给组件设setFont(new Font("微软雅黑", Font.PLAIN, 14))。
4.3ClassNotFoundException: com.mysql.cj.jdbc.Driver
现象:编译通过,运行时报找不到驱动类。
原因:jar 包没有真正加入 classpath。在 IDEA 里只把 jar 复制到lib目录不够,必须右键Add as Library,或者在Project Structure的Modules里把 jar 加到Dependencies。
解决:打开File -> Project Structure -> Modules -> Dependencies,点+号选JARs or directories,把lib下的驱动 jar 加进去,Scope 选Compile。加完后重新运行。
4.4 转账后余额不对:事务没生效或自动提交没关
现象:转账时扣了款但对方没收到,或者两边都加了钱。
原因:conn.setAutoCommit(false)没写,或者写了但commit()之前抛异常没回滚。
解决:确认转账方法里第一行就是conn.setAutoCommit(false),并且commit()和rollback()都在try-catch里成对出现。调试时可以在commit()前打断点,看两个executeUpdate的返回值是不是都是 1。
4.5 IDEA 运行报cannot start internal http server
现象:IDEA 启动项目时弹窗报cannot start internal http server,项目跑不起来。
原因:通常是 IDEA 内置服务器端口被占用,或者防火墙拦截。
解决:重启 IDEA;如果不行,在Settings -> Build, Execution, Deployment -> Debugger里把Built-in server port改成一个不常用的端口,比如 63342 改成 63343。这个报错和银行管理系统本身无关,是 IDEA 环境问题,别去改代码。
5. 进阶技巧:把课程设计改成能写进简历的项目
5.1 用 DAO 工厂 + 配置文件解耦
现在的DBUtil里数据库账号密码是写死的,换个环境就要改代码。进阶做法是建一个db.properties放在src目录下:
jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/bank_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=false jdbc.username=root jdbc.password=123456然后DBUtil用ClassLoader.getSystemClassLoader().getResourceAsStream("db.properties")加载。这样部署到别的机器上只需要改配置文件,不用重新编译。这个改动很小,但面试时提到「配置外置」是个加分点。
5.2 给关键操作加日志,排查问题不用猜
Swing 程序出问题时,控制台往往只有一行NullPointerException,根本不知道哪一步空了。在service层的关键方法入口加一行System.out.println或者用java.util.logging.Logger记录参数:
private static final Logger logger = Logger.getLogger(BankService.class.getName()); public boolean transfer(String fromCard, String toCard, BigDecimal amount) { logger.info("转账开始: from=" + fromCard + ", to=" + toCard + ", amount=" + amount); // ... 业务逻辑 logger.info("转账结束: result=" + result); return result; }日志不用多,转账、存款、取款、登录这四个操作各加两条就够。出问题时看控制台输出,能快速定位是参数传错了还是 SQL 没执行。
5.3 用 JUnit 做 DAO 层单元测试
界面测试靠手点,但 DAO 层可以用 JUnit 自动测。在 IDEA 里新建一个test目录,标记为Test Sources Root,然后写:
@Test public void testTransfer() { BankService service = new BankService(); boolean result = service.transfer("6222020200112233", "6222020200112244", new BigDecimal("100.00")); assertTrue(result); // 验证余额 AccountDao dao = new AccountDao(); Account from = dao.findByCardNo("6222020200112233"); assertTrue(from.getBalance().compareTo(new BigDecimal("900.00")) == 0); }跑一遍测试,比手动点十遍界面靠谱。而且简历上写「使用 JUnit 对 DAO 层进行单元测试」,比只写「实现了银行管理系统」有说服力得多。
5.4 打包成可执行 JAR,让演示不依赖 IDEA
课程设计答辩时,总不能现场打开 IDEA 跑。用File -> Project Structure -> Artifacts新建一个JAR -> From modules with dependencies,Main Class 选com.bank.test.Main,然后Build -> Build Artifacts生成 jar。生成的 jar 在out/artifacts目录下,双击就能运行(前提是目标机器装了 JDK)。如果想让没装 JDK 的机器也能跑,可以用jpackage打成 exe,但那是另一个话题了。
我自己的习惯是:每完成一个模块就打包一次,确保 jar 能跑起来。别等到答辩前一天才发现打包报错,那时候改配置的时间都不够。这个银行管理系统看着简单,但把环境、分层、事务、打包这几件事都走一遍,对 Java 桌面开发的理解会比看十篇教程都扎实。希望帮到你。
本文还有配套的精品资源,点击获取