ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

银行账目管理系统JavaWeb毕设:从导库到事务一致性实战指南

银行账目管理系统JavaWeb毕设:从导库到事务一致性实战指南 简介这是一套基于JavaWeb的银行帐目管理系统面向计算机相关专业正在准备毕业设计的学生以及需要JavaWeb项目实战的开发者用以解决银行账户管理中存款、取款、开户、销户、办卡、挂失等典型业务的技术实现问题。资源包共包含3个文件包括项目说明文档txt、完整项目源码zip以及数据库脚本sql整体大小约1.24MB结构紧凑便于部署使用。目前已有2301人学习下载深受同类学习者认可。项目采用B/S架构基于JSP、Servlet、JDBC与MySQL实现系统内置用户和管理员双角色涵盖账户管理、取款机信息管理、用户查询与查询统计等功能模块功能齐全且界面友好。借助该资源可获得可运行的系统源码、数据库脚本和项目说明直接用于毕业设计或进一步学习JavaWeb开发流程亦可作为企业级银行账户系统的设计参考。1. 银行帐目管理系统毕设源码包先跑通再改别一上来就动代码毕设拿到银行帐目管理系统题目的人十个有九个先去找源码包真正打开压缩包那一刻面对src、web、SQL脚本、一堆配置文件最想知道的其实是同一个问题这东西到底能不能跑起来我的经验是这类JavaWeb毕设项目的核心就两条线——账户余额和交易流水的一致性其余全是增删改查。整个复现过程大概半天到一周取决于你踩不踩坑。这篇就按先认清单、再导库、再跑通、再改功能的顺序拆一遍新手能照着走熟手可以直接跳到坑位看边界。2. 系统架构与数据模型先认目录再谈改功能2.1 一个典型JavaWeb毕设包的目录结构打开压缩包先别急着双击解压后先对着目录认一遍。最常见的结构如下表目录/文件作用复现时怎么用sql/ 或 *.sql建库建表脚本和初始数据第一步导入决定后面能不能登录src/Java源码按dao/service/servlet分层改业务逻辑、找事务代码web/JSP页面、WEB-INF/web.xml、libJSP直接决定页面样式README.md环境要求、默认账号、部署步骤先读它能少踩一半坑表里左列加简要说明即可实际文件可能嵌套在src/main/java下但毕设包普遍是扁平src。我一般会先打开SQL脚本看建表语句——它最能暴露技术栈和功能范围。这个动作花不了十分钟却决定了后面导入数据库的方式。常见翻车是源码拿到了却不知道数据库脚本是MySQL 5.7的写法还是MySQL 8的写法等会连不上库才回头改驱动。2.2 技术栈选型JSPServletMySQL为什么是毕设主力这套银行帐目管理系统通常是JSPServletMySQLTomcat的经典组合。它的优点不是性能而是链路短JSP页面提交表单Servlet接收参数Service做业务判断DAO用JDBC读写MySQL。答辩时老师问一次登录请求经过哪些组件照着这条链路答就能讲明白。相比Spring Boot经典JavaWeb不用处理繁杂的依赖注入和自动配置代码平铺直叙适合毕业设计的时间节奏。需要说明的是这份资源解决的是课程范围内能讲清、能演示、能跑通的问题不是生产级高并发系统。如果你的选题是基于Spring Boot的银行账户系统那核心的Service逻辑和数据库设计依然可以复用只是把Servlet换成Controller。先想清楚这一点后面改代码时就不会被技术栈带偏。2.3 核心表设计账户、流水、用户三张表撑起整个系统银行帐目管理的核心数据模型绕不开三张事实表用户表、账户表、交易流水表。用户表管登录身份账户表存余额流水表记录每一笔资金变动。下面是一个符合毕设场景的建表脚本片段-- 用户表登录身份不含资金数据 CREATE TABLE sys_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(32) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL, real_name VARCHAR(32), role VARCHAR(16) DEFAULT USER, -- USER / ADMIN create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); -- 账户表一个用户可开多个账户余额放在这里 CREATE TABLE account ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, account_no VARCHAR(32) NOT NULL UNIQUE, -- 卡号/账号业务唯一键 balance DECIMAL(18,2) DEFAULT 0.00, -- 余额精确计算必须用DECIMAL status TINYINT DEFAULT 1, -- 1正常 0冻结 version INT DEFAULT 0, -- 乐观锁版本号 create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); -- 交易流水表只增不改记录每一笔变动 CREATE TABLE transaction_record ( id BIGINT PRIMARY KEY AUTO_INCREMENT, flow_no VARCHAR(32) NOT NULL UNIQUE, -- 业务流水号防重复入账 account_id INT NOT NULL, type VARCHAR(8) NOT NULL, -- DEPOSIT / WITHDRAW / TRANSFER amount DECIMAL(18,2) NOT NULL, balance_after DECIMAL(18,2), -- 变动后余额便于对账 relate_flow_no VARCHAR(32), -- 转账时关联对方流水 remark VARCHAR(128), create_time DATETIME DEFAULT CURRENT_TIMESTAMP );这个脚本是这类系统最常见的建模方式实际资源里的脚本字段可能略有增减但不影响理解。余额字段必须用DECIMAL(18,2)而不是DOUBLE原因是浮点误差在资金场景会导致对账不平这是毕设答辩最爱问的点。account表里的version字段是为了支持乐观锁——并发扣款时通过version防止超扣ForUpdate悲观锁也行但毕设代码里写乐观锁更容易讲清楚。流水表只插入、不修改、不删除这个设计原则决定了系统的可追溯性后面所有的对账SQL都依赖它。流水表只插入不更新的理由往深一层是对账规则任何一笔余额变动都必须能在流水表里找到来源。如果允许update流水金额审计时就无法确认记录是否被篡改。实际开发里我们还会把balance_after也填上这样查询某时间点的余额快照时不需要临时计算这个字段冗余存储但换来了查询性能和可解释性。毕设答辩时能说出这一层分数会明显不一样。提示导入数据库脚本前先确认脚本里有没有DROP TABLE语句。有些脚本为了方便重复执行会先删表再建这在你自己测试机上没问题但别在有数据的库上直接跑。2.4 初始数据脚本里预置的账号别急着删数据库脚本通常会附带初始化数据一个管理员账号、几个测试用户、若干测试账户。这些数据是复现时的钥匙。导入后先用SELECT确认一下sys_user表里有哪些账号别一上来就改密码否则后面登录都登不上。我习惯在README里查默认账号查不到就搜SQL脚本里的INSERT语句。这不算玄学只是省掉试遍admin/admin123的时间。注意如果脚本里预置的密码是MD5加密后的字符串别试图在数据库里直接改成明文。登录代码大概率是用MD5(password)做比对改成明文会导致永远登录失败。要看清楚登录DAO的比对逻辑再决定怎么改密码。3. 本地搭建与导入数据库从零到能登录的完整操作3.1 环境检查JDK、MySQL、Tomcat版本匹配是第一道坑先打开命令行确认三样东西JDK版本、MySQL版本、IDE里配置的Tomcat。命令如下java -version mysql --versionJDK 1.8配上Tomcat 8或9是这套JavaWeb项目最稳的组合。MySQL 5.7和8.0的差异在JDBC驱动上体现得最明显5.7时代用com.mysql.jdbc.Driver和jdbc:mysql://localhost:3306/bank8.0时代要换成com.mysql.cj.jdbc.DriverURL还要追加serverTimezone参数。这个差异不搞清楚下一步就会在数据库连接上翻车。常见的版本组合我整理成一个对照方便你判断手上的资源和本地环境是否兼容组件推荐版本坑点JDK1.8过高版本可能编译期报错MySQL5.7或8.0驱动和URL写法不同Tomcat8.5或9.08080端口冲突高频数据库驱动mysql-connector-java 5.1.49 / 8.0.x与MySQL版本对应这张表不绝对但按这个组合跑JavaWeb毕设成功率最高。如果本地装的是MySQL 8而脚本是5.7写法优先改驱动而不是换MySQL。检查完版本再打开资源包里的配置文件一般是src/db.properties或jdbc.properties确认数据库名、用户名、密码是否与本地一致。3.2 导入数据库脚本命令行source比图形化粘贴更稳拿到SQL脚本常见的导入方式有两种。命令行方式最稳妥也最容易看到报错信息mysql -u root -p --default-character-setutf8mb4 bank.sql也可以先登进MySQL再执行sourcesource /path/to/bank.sql;第二种方式适合脚本文件路径带空格的情况。导入完成后不要急着走执行一条SHOW TABLES确认关键表都在再SELECT一下sys_user验证初始数据。我见过太多人在这步翻车用Navicat直接把整个脚本粘贴到查询窗口执行遇到脚本里的DELIMITER和注释就报错实际只有一半表建成功登录时提示表不存在。命令行导入虽然看起来没那么图形化但出错时它会把异常原样打出来排查起来快得多。执行完后可以用下面这条SQL快速确认表是否齐全SELECT table_name FROM information_schema.tables WHERE table_schema bank;然后看sys_user表里有没有数据。两步都过了再启动Tomcat能省掉后面一大半报错。3.3 IDEA配置Tomcat并部署Artifacts是把项目送进Tomcat的桥数据库导完接下来是把项目跑起来。IDEA里JavaWeb项目部署流程是固定的File → Project Structure → Artifacts把编译输出和依赖打包成war或war exploded然后Run → Edit Configurations添加Tomcat Server → Local在Deployment页签里把刚才的Artifact挂到Tomcat上。这里要说清楚一个常见误解IDEA跑JavaWeb项目不是运行main方法而是把Web应用部署到Tomcat容器里。很多人点了绿色的三角形却跳到没有配置Artifact的提示就是因为漏了Project Structure这一步。Application context一般设为/bank_web启动后访问http://localhost:8080/bank_web/。如果资源包里自带lib目录记得检查这些jar是否被打进Artifacts否则会出现经典的ClassNotFoundException。数据库连接的配置通常集中在db.properties中确认下面对齐本地环境jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/bank?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalse jdbc.usernameroot jdbc.password你的密码参数说明driver决定用哪个JDBC驱动类MySQL 8必须写com.mysql.cj.jdbc.Driverurl里characterEncodingutf8保证中文正常serverTimezoneAsia/Shanghai解决MySQL 8的时区报错useSSLfalse省去无谓的SSL握手警告。密码这里不要写死成你在SQL脚本里看到的默认值必须改成你自己MySQL的实际密码。改完配置重新启动Tomcat能进入登录页说明环境这关过了。运行后如果访问404先检查Application context路径和实际请求路径是否完全一致Tomcat启动日志里会打印出Context Path对齐它再刷新页面。3.4 启动后的验证顺序登录、查余额、记一笔项目跑起来先别急着看代码按业务顺序验证一遍用初始账号登录、查看账户余额列表、做一笔存款或取款、再查看流水记录。这个顺序能快速判断数据库连接、DAO层、Service层、JSP页面是否全部打通。如果登录能进去但查询余额报错问题多半在SQL语句和表字段名没对上打开控制台看堆栈里的SQL异常基本能定位到具体DAO方法。这一步验证完整后面改功能才有底。4. 核心业务实现转账事务与账户管理的代码边界4.1 DAO层与JDBC连接从db.properties到JdbcUtils这类JavaWeb项目的数据库访问层标配是一个JdbcUtils工具类 多个DAO类。工具类负责加载驱动、建立连接和关闭资源如下所示public class JdbcUtils { private static String url; private static String username; private static String password; static { try { Properties props new Properties(); // 读取类路径下的 db.properties props.load(JdbcUtils.class.getClassLoader() .getResourceAsStream(db.properties)); Class.forName(props.getProperty(jdbc.driver)); url props.getProperty(jdbc.url); username props.getProperty(jdbc.username); password props.getProperty(jdbc.password); } catch (Exception e) { throw new ExceptionInInitializerError(e); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(url, username, password); } public static void close(Connection conn, Statement st, ResultSet rs) { // 逆序关闭rs - st - conn if (rs ! null) { try { rs.close(); } catch (SQLException e) { } } if (st ! null) { try { st.close(); } catch (SQLException e) { } } if (conn ! null) { try { conn.close(); } catch (SQLException e) { } } } }这段代码的逻辑分两部分静态代码块在类加载时执行一次读取db.properties里的连接参数并注册驱动getConnection每次调用新建连接close按ResultSet、Statement、Connection的顺序逆序释放。参数里最容易写错的是jdbc.driverMySQL 8的驱动类名带cjMySQL 5.7不带两种驱动和两种URL不能混用。这个小工具类看起来简单实际上它是整套代码的命脉。常见误用是只关Statement不关Connection或者在一个方法里频繁getConnection却忘记close最终报连接数耗尽。我改这种项目时第一步就是全局搜getConnection和close确认每一条获取连接的路径都有对应的关闭否则改着改着就出现能跑几次然后越来越慢的玄学现象。4.2 转账必须是一个事务扣款、入账、写流水不能分成三步银行帐目系统里最值得看代码的部分是转账。转账业务最少涉及两个账户和两条流水转出方扣款、转入方加款、生成两条交易记录。这三件事必须在一个数据库事务里完成否则任何一步失败都会造成金额不一致。下面是一个典型的Service层实现public void transfer(Long fromAccountId, Long toAccountId, BigDecimal amount, String remark) throws Exception { Connection conn JdbcUtils.getConnection(); AccountDao accountDao new AccountDao(); TransactionDao txDao new TransactionDao(); try { conn.setAutoCommit(false); // 手动控制事务 BigDecimal fromBalance accountDao.getBalance(conn, fromAccountId); if (fromBalance.compareTo(amount) 0) { throw new RuntimeException(余额不足); } accountDao.decrease(conn, fromAccountId, amount); // 扣款 accountDao.increase(conn, toAccountId, amount); // 入账 String outFlowNo TF System.currentTimeMillis() fromAccountId; String inFlowNo TF System.currentTimeMillis() toAccountId; txDao.insert(conn, outFlowNo, fromAccountId, TRANSFER, amount.negate(), fromBalance.subtract(amount)); txDao.insert(conn, inFlowNo, toAccountId, TRANSFER, amount, null); conn.commit(); // 全部成功才提交 } catch (Exception e) { conn.rollback(); // 任何异常回滚全部操作 throw e; } finally { conn.setAutoCommit(true); JdbcUtils.close(conn, null, null); } }这段代码的关键点都在注释里了我再说几个容易被忽略的地方setAutoCommit(false)必须放在所有SQL之前否则JDBC会把每一条update当作独立事务自动提交前面的扣款就无法回滚余额校验要在扣款之前做用compareTo而不是因为这是BigDecimal流水号用时间戳拼接账户ID的方式在毕设里够用但注意两个流水号不能共用同一个时间戳加同一个ID否则主键冲突。参数说明fromBalance和amount都是BigDecimal比较大小用compareToamount.negate()是为了在流水里存负数表示减少方便后面对账时直接SUMbalance_after存变动后余额这样查询账户历史余额时不用反推。扣款和入账两个DAO方法都要接收同一个Connection参数这是保证它们在同一事务里的前提。如果哪个DAO自己内部又去拿了一个新连接事务就断了——这是最容易把转账写成能转账但偶尔对不上账的原因。4.3 与常见误用的差别把事务写在Servlet里为什么能跑但危险有些版本的源码偷懒把业务逻辑直接写在Servlet里doPost里先查余额再update扣款再update入账没用事务。这种代码演示时同样能转账成功因为正常情况下三条SQL顺序执行不会报错。但如果中途某条SQL因锁超时或字段超长失败前面执行成功的扣款就永远留在数据库里了。这就是典型的看起来能跑、细看会炸。正确做法是Servlet只做参数接收和结果转发业务逻辑收拢到Service层事务边界在Service层控制。判断一份源码的Service层设计是否合理最简单的标准是看转账方法有没有独立的Service类、有没有手动事务。如果发现转账逻辑夹在Servlet里也不用慌把扣款、入账、写流水抽成一个Service方法再把事务控制代码按上面示例补上大约半小时就能改完。改完之后你会明显感觉到代码好讲多了答辩老师问这笔转账失败怎么保证数据一致直接指着事务代码回答就可以。账户管理类功能开户、冻结、销户同样要遵循一个原则改变账户状态前在Service层校验账户是否存在、是否已冻结销户前检查余额必须为0否则提示先转账。把这类校验逻辑集中到Service而不是散落在Servlet里答辩时被问到冻结账户还能不能收款就能直接指出校验代码的位置。5. 避坑排查启动失败、数据错乱与中文乱码的五个现场先说个总的原则遇到报错先看完整堆栈不要只看第一行。JavaWeb的问题大半会体现在控制台日志里真正把异常信息读完整定位时间能缩短一半。下面五条是按出现频率排的基本覆盖了八成启动失败现场。5.1 现象一Tomcat启动报ClassNotFoundException现象启动Tomcat时控制台抛出java.lang.ClassNotFoundException后面跟着某个Servlet类或工具类的名字页面访问直接404。原因IDEA的Artifacts没有把依赖jar打进部署目录。JavaWeb项目的依赖jar在运行时必须存在于WEB-INF/lib下IDE编译期能找到类不代表运行时能找到。解决Project Structure → Artifacts → 选中当前Artifact在Available Elements里把lib或External Libraries添加进WEB-INF/lib。如果是war exploded部署确认输出目录里确实有jar文件。改完重新部署一次。这个坑在网上下载的源码里尤其常见因为压缩包里的lib目录往往没被IDEA自动识别。5.2 现象二登录时数据库连接失败或时区报错现象页面能打开一点登录就报Cannot create PoolableConnectionFactory或者The server time zone value йʱ is unrecognized。原因MySQL驱动版本与URL不匹配。MySQL 8要求驱动类名是com.mysql.cj.jdbc.Driver且URL必须带serverTimezoneMySQL 5.7用com.mysql.jdbc.Driver不强制带时区。时区乱码是这个场景最有名的报错大部分人都栽在这里。解决确认你的MySQL是哪个大版本对应修改db.properties。MySQL 8就用jdbc:mysql://localhost:3306/bank?serverTimezoneAsia/ShanghaicharacterEncodingutf8useSSLfalse。改完重启Tomcat再看一次别只改一半。如果还报把url里的serverTimezone换成GMT%2B8试试。5.3 现象三中文乱码页面问号或数据库里是乱码现象页面上显示???或者数据库表里存的中文变成乱码。原因字符集没统一。JavaWeb的字符集链路很长JSP页面编码、Tomcat的URIEncoding、Servlet的request.setCharacterEncoding、MySQL连接参数characterEncoding、数据库建库时的默认字符集任何一层掉链子都会乱码。解决先统一到UTF-8。JSP页面头部写contentTypetext/html; charsetUTF-8Servlet里在读取参数前调request.setCharacterEncoding(UTF-8)db.properties的URL带characterEncodingutf8MySQL建库时用CREATE DATABASE IF NOT EXISTS bank DEFAULT CHARSET utf8mb4。排查时一层一层加改完一层重启一次看效果。5.4 现象四SQL脚本导入报错登录提示表不存在现象用Navicat把SQL脚本粘贴到查询窗口执行报错后部分表没建出来登录时提示Table doesnt exist。原因图形化工具直接粘贴执行时脚本里的DELIMITER、注释、多语句分段处理方式与命令行不同某些语句被截断或跳过。加上脚本可能中途出错后面的建表语句全部没执行。解决改用命令行source方式导入出错会显示具体哪一行修复后重新执行。导入完成后用SHOW TABLES核对表数量拿脚本里的建表语句清单逐步对比。另一个建议导入前先看一眼脚本头部的DROP TABLE避免覆盖已有数据。5.5 现象五IDEA启动Tomcat端口被占用或startup.bat一闪而过现象IDEA启动Tomcat报Port 8080 is already in use或者双击bin/startup.bat窗口一闪就没了访问不到页面。原因端口被其他进程占用命令行启动闪退则通常是JAVA_HOME或CATALINA_HOME环境变量没配好错误信息一闪而过看不见。解决端口冲突时用netstat -ano | findstr 8080查到占用进程或者在IDEA的Server配置里把Tomcat端口改成8081。命令行闪退就先执行catalina.bat run错误会留在控制台里确认JAVA_HOME指向JDK目录而不是JRE目录。这两个解决方式都验证过很多次属于JavaWeb跑不起来的高频原因。6. 进阶验证用对账SQL和事务回滚测试撑起答辩项目跑通之后下一步不是急着加功能而是先证明这套账目系统是对的。账目系统最核心的不可变原则是账户余额必须等于该账户所有成功流水的累加。基于这个原则写一条对账SQLSELECT a.id, a.account_no, a.balance, IFNULL(SUM(t.amount), 0) AS sum_amount FROM account a LEFT JOIN transaction_record t ON t.account_id a.id GROUP BY a.id, a.account_no, a.balance HAVING a.balance ! IFNULL(SUM(t.amount), 0);这条SQL把每个账户的余额和流水求和对比结果集应为空查出数据多半是某笔交易没写流水或事务没包全。我拿到任何账目类毕设源码后都会先执行一遍这条SQL它比人肉翻代码快得多。第二个值得做的验证是事务回滚测试。用余额充足的账户转账在入账前人为抛一个异常刷新两个账户余额确认都没变、流水表里没有半截记录。这一步通过答辩时可以直接演示。时间富余的话给流水表加唯一索引约束flow_no转账接口重复提交时第二次插入报DuplicateKeyException在Service里捕获后返回请勿重复提交这就算给系统补了一层防重。扩展方向上这套资源的Service层和表结构设计可以平移到Spring Boot版本把Servlet换成Controller、JdbcUtils换成MyBatis即可业务边界和数据一致性才是核心功力。最后说个我的习惯从那以后每次拿到毕设源码包我都不急着导入IDEA而是先看SQL脚本、再导库、跑对账SQL、确认事务没问题然后才开始改功能。这套流程帮我绕开了大量看起来能跑、一查数据就是错的坑。希望帮到你。本文还有配套的精品资源点击获取
返回列表