简介:本资源为基于JavaWeb开发的个人网上银行系统完整项目包,采用MVC模式分层设计,面向计算机相关专业的在校学生、教师及企业开发者,可用于毕业设计、课程设计、大作业或初期项目立项演示。包内共368个文件,涵盖52个Java源文件、31个JSP页面、84个JavaScript脚本、48个CSS样式表及83张PNG截图,另附数据库SQL脚本、项目报告文档与系统截图,压缩包约64.8MB,结构清晰便于按模块查阅。系统功能覆盖用户登录、存款存取与增删改查、信息展示、转账、定期存款、活期转定期及日志查看等核心银行业务,代码均经过功能验证,可稳定运行。目前已有147人学习下载,适合作为入门进阶参考或二次开发基础,帮助读者快速理解JavaWeb分层架构与数据库设计思路。
1. 从一份 JavaWeb 个人网上银行系统源码说起:MVC 分层到底解决了什么问题
很多人第一次拿到「基于 JavaWeb 开发的个人网上银行系统源码+数据库+报告+系统截图(MVC 模式)」这类压缩包时,第一反应是解压、找 main 方法、点运行,然后被一堆 Servlet、JSP、DAO 和 web.xml 拦住。它讲的其实是一个典型的 JavaWeb 三层架构落地案例:用 Servlet 或 SpringMVC 做控制层,用 Service 做业务层,用 JDBC/MyBatis 做持久层,前端用 JSP 或 HTML 渲染,数据库存账户、交易流水、转账记录。它解决的核心问题是——把「转账」这种涉及余额校验、事务、并发扣款的逻辑,从一坨 JSP 脚本里拆出来,变成可维护、可测试、可复现的工程结构。
这套东西适合谁?适合正在做课程设计、毕业设计、或者想从「会写 CRUD」进阶到「理解分层与事务」的 JavaWeb 学习者。你不需要是架构师,但至少要能看懂 Servlet 生命周期、JDBC 连接池、以及 MVC 模式里 Model-View-Controller 各自该放什么。热搜里常出现的「javaweb项目完整案例mysql」「javaweb项目完整案例」说的就是这类东西——它不是玩具,但也不是生产级网银,边界要清楚:单机部署、单库、无分布式事务、无真实风控。把它当成一个能跑通、能讲清楚、能改得动的教学级系统,才是正确打开方式。
2. 环境搭建与工程导入:让源码在本地跑起来的最小路径
2.1 JDK、Tomcat、MySQL 的版本对齐
拿到源码后第一件事不是改代码,而是对齐版本。JavaWeb 老项目最常见的翻车点就是 JDK 版本和 Tomcat 版本不匹配。我一般会先看pom.xml或WEB-INF/lib里的 servlet-api 版本,再决定用哪个 Tomcat。
| 组件 | 推荐版本 | 说明 |
|---|---|---|
| JDK | 1.8 或 11 | 老项目优先 1.8,避免模块化报错 |
| Tomcat | 8.5 或 9.0 | Tomcat 10 把 javax 改成 jakarta,老代码直接崩 |
| MySQL | 5.7 或 8.0 | 8.0 要改驱动类和时区参数 |
| IDE | IDEA 或 Eclipse | IDEA 对 JavaWeb 支持更顺 |
| 构建 | Maven 或手动导 jar | 看项目有没有 pom.xml |
如果你用的是 IDEA,导入后要检查Project Structure里的Modules是否把src标记成 Sources Root,Web面版里Web Resource Directory是否指向webapp或WebContent。这一步没做对,JSP 会 404。
2.2 数据库建库与连接配置
数据库脚本一般在sql或db目录下,文件名类似bank.sql。导入命令如下:
# 登录 MySQL 并创建数据库 mysql -u root -p -e "CREATE DATABASE bank_db DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_general_ci;" # 导入表结构和初始数据 mysql -u root -p bank_db < sql/bank.sql # 验证表是否建好 mysql -u root -p -e "USE bank_db; SHOW TABLES;"逻辑说明:先建库再导表,字符集用utf8mb4避免中文乱码。参数上,bank_db是库名,要和配置文件里一致;sql/bank.sql是脚本路径,按实际改。导入后至少应该有user、account、transaction三张核心表。
连接配置通常在db.properties或jdbc.properties:
jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/bank_db?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8 jdbc.username=root jdbc.password=你的密码注意serverTimezone必须写,否则 MySQL 8.0 会报时区错误;useSSL=false是本地开发省事,生产不能这么干。驱动类 MySQL 8.0 用com.mysql.cj.jdbc.Driver,5.7 用com.mysql.jdbc.Driver,写错就是ClassNotFoundException。
2.3 部署到 Tomcat 并验证首页
IDEA 里配置 Tomcat 的步骤:Run → Edit Configurations → + → Tomcat Server → Local → 在 Deployment 里加 Artifact。Application context 建议设成/bank,这样访问路径是http://localhost:8080/bank/。
# 命令行方式:把 war 包丢进 webapps cp target/bank.war $TOMCAT_HOME/webapps/ $TOMCAT_HOME/bin/startup.sh # 看日志确认启动无异常 tail -f $TOMCAT_HOME/logs/catalina.out启动后先访问登录页,用 SQL 里的初始账号(常见是 admin/123456 或自己看user表)登录。如果登录报 500,先看catalina.out的堆栈,八成是数据库连不上或字段名对不上。这一步跑通,后面才有意义。
3. MVC 分层拆解:Controller、Service、DAO 各写什么
3.1 一次转账请求的完整调用链
理解 MVC 最好的方式不是背定义,而是跟一次转账请求走一遍。用户在前端填收款账号和金额,点提交,请求打到TransferServlet(Controller),它做三件事:取参数、调 Service、转发视图。Service 层TransferService.transfer()负责开启事务、查余额、扣款、加款、写流水。DAO 层AccountDao和TransactionDao只负责 SQL 执行。
// Controller 层:只做参数接收和结果转发 @WebServlet("/transfer") public class TransferServlet extends HttpServlet { private TransferService transferService = new TransferServiceImpl(); protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String from = req.getParameter("fromAccount"); String to = req.getParameter("toAccount"); BigDecimal amount = new BigDecimal(req.getParameter("amount")); try { transferService.transfer(from, to, amount); req.setAttribute("msg", "转账成功"); } catch (Exception e) { req.setAttribute("msg", "转账失败:" + e.getMessage()); } req.getRequestDispatcher("/result.jsp").forward(req, resp); } }逻辑说明:Controller 不碰 SQL,不写业务判断,只做参数转换和视图跳转。参数上fromAccount、toAccount、amount要和前端表单 name 一致,金额用BigDecimal而不是double,这是金融类系统的底线。
3.2 Service 层事务:为什么必须手动控制
转账的核心是事务。Service 层要保证「扣款」和「加款」要么都成功,要么都回滚。用 JDBC 的写法:
public void transfer(String from, String to, BigDecimal amount) { Connection conn = null; try { conn = DBUtil.getConnection(); conn.setAutoCommit(false); // 关闭自动提交,开启事务 AccountDao accountDao = new AccountDaoImpl(conn); TransactionDao txDao = new TransactionDaoImpl(conn); BigDecimal fromBalance = accountDao.getBalance(from); if (fromBalance.compareTo(amount) < 0) { throw new RuntimeException("余额不足"); } accountDao.updateBalance(from, fromBalance.subtract(amount)); accountDao.updateBalance(to, accountDao.getBalance(to).add(amount)); txDao.insert(new Transaction(from, to, amount, new Date())); conn.commit(); // 全部成功才提交 } catch (Exception e) { try { if (conn != null) conn.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } throw new RuntimeException(e); } finally { DBUtil.close(conn); } }逻辑说明:setAutoCommit(false)是事务起点,commit()是终点,rollback()是后悔药。关键参数是 DAO 必须共用同一个Connection,否则事务不生效——这是新手最容易踩的坑,每个 DAO 各自getConnection(),事务就散了。
3.3 DAO 层与连接池:别每次请求都新建连接
DAO 只做 SQL,但连接管理有讲究。教学项目常用DBUtil直接DriverManager.getConnection(),每次请求新建连接,并发一高就崩。常见做法是引入 Druid 或 HikariCP:
// Druid 连接池初始化 public class DBUtil { private static DataSource dataSource; static { DruidDataSource ds = new DruidDataSource(); ds.setUrl("jdbc:mysql://localhost:3306/bank_db?useSSL=false&serverTimezone=Asia/Shanghai"); ds.setUsername("root"); ds.setPassword("你的密码"); ds.setInitialSize(5); // 初始连接数 ds.setMaxActive(20); // 最大连接数 ds.setMaxWait(3000); // 获取连接超时毫秒 dataSource = ds; } public static Connection getConnection() throws SQLException { return dataSource.getConnection(); } }逻辑说明:initialSize是启动就建的连接,maxActive是并发上限,maxWait是拿不到连接时的等待时间。参数怎么设?本地开发 5/20 够用,压测时看maxActive是否被打满。热搜里「mysql的数据库连接池」说的就是这个点,别再用裸DriverManager了。
4. 数据库设计与核心表:账户、流水、用户怎么建
4.1 三张核心表的字段与约束
网上银行系统的数据库不复杂,但约束要清楚。核心三张表:
| 表名 | 关键字段 | 约束 |
|---|---|---|
| user | id, username, password, real_name | username 唯一 |
| account | id, user_id, account_no, balance | account_no 唯一,balance 非负 |
| transaction | id, from_account, to_account, amount, create_time | 外键可省,加索引 |
balance字段用DECIMAL(15,2),不要用FLOAT,浮点误差在金额上是灾难。account_no加唯一索引,防止重复开户。transaction表的from_account和to_account加普通索引,查流水快。
CREATE TABLE account ( id BIGINT PRIMARY KEY AUTO_INCREMENT, user_id BIGINT NOT NULL, account_no VARCHAR(32) NOT NULL UNIQUE, balance DECIMAL(15,2) NOT NULL DEFAULT 0.00, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, INDEX idx_user (user_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;逻辑说明:InnoDB支持事务,utf8mb4支持中文和 emoji。DECIMAL(15,2)表示最多 15 位、2 位小数,够个人账户用。INDEX idx_user让按用户查账户变快。
4.2 转账并发下的余额安全
单机教学项目也要考虑并发。两个线程同时给同一账户转账,可能读到相同余额,导致超扣。解决办法有两种:一是SELECT ... FOR UPDATE行锁,二是UPDATE account SET balance = balance - ? WHERE account_no = ? AND balance >= ?乐观扣减。
-- 行锁方式:在事务内锁定该行 SELECT balance FROM account WHERE account_no = ? FOR UPDATE; -- 乐观扣减:一条 SQL 完成校验和扣款 UPDATE account SET balance = balance - ? WHERE account_no = ? AND balance >= ?;逻辑说明:FOR UPDATE必须在事务内才生效,锁到事务提交。乐观扣减看affectedRows,返回 0 说明余额不足或账号不存在。教学项目用哪种都行,但要知道区别:行锁简单但并发低,乐观扣减并发高但要处理失败重试。
5. 避坑与排查:源码跑不起来时先看这几条
5.1 现象:启动报 ClassNotFoundException: com.mysql.jdbc.Driver
原因:MySQL 8.0 驱动类名变了,或者 jar 没放进WEB-INF/lib。解决:改成com.mysql.cj.jdbc.Driver,确认mysql-connector-java的 jar 在lib下,Maven 项目检查pom.xml的 scope 不是provided。
5.2 现象:JSP 页面中文乱码
原因:三处编码没统一——数据库、请求、响应。解决:数据库用utf8mb4,JSP 顶部加<%@ page contentType="text/html;charset=UTF-8" %>,Servlet 里req.setCharacterEncoding("UTF-8")和resp.setContentType("text/html;charset=UTF-8")都要写。
5.3 现象:转账后余额没变,但流水有了
原因:事务没生效,DAO 各自拿了不同 Connection,commit只提交了其中一条。解决:Service 层统一获取 Connection,通过构造函数传给 DAO,保证同一事务内共用一个连接。
5.4 现象:Tomcat 启动正常,访问 404
原因:Application context 配错,或者web.xml里 servlet 映射路径和访问路径不一致。解决:看 IDEA 的 Deployment 里 context 是不是/bank,访问时带上;检查@WebServlet("/transfer")和表单 action 是否匹配。
5.5 现象:登录一直失败,密码明明是对的
原因:数据库存的是明文还是 MD5 没对齐,或者查询条件字段名写错。解决:看user表里 password 字段的实际值,如果是 32 位十六进制就是 MD5,登录时要先 MD5 再比对;检查 SQL 里WHERE username = ?的字段名和表结构一致。
6. 从能跑到能讲:二次开发与答辩加分技巧
把系统跑起来只是及格线,真正拉开差距的是你能不能讲清楚「为什么这么设计」。我一般会做三件事来验证自己是否真懂了。
第一,加一个「交易流水分页查询」。这是最典型的 JavaWeb 综合练习:Controller 接page和pageSize,Service 算offset,DAO 用LIMIT ?, ?。写完你就理解了 MVC 各层边界。
// 分页查询核心:offset = (page - 1) * pageSize int offset = (page - 1) * pageSize; String sql = "SELECT * FROM transaction WHERE from_account = ? ORDER BY create_time DESC LIMIT ?, ?";参数上page从 1 开始,pageSize建议 10 或 20。注意LIMIT的两个参数是offset和size,顺序别反。
第二,把密码从明文改成 MD5 加盐。不是为了安全级别,而是为了在报告里能写「做了密码保护」。用MessageDigest实现,注册时存MD5(password + salt),登录时同样计算比对。
第三,准备一张「分层职责表」用于答辩:
| 层 | 职责 | 不该做的事 |
|---|---|---|
| Controller | 接参数、调 Service、转发 | 写 SQL、写业务判断 |
| Service | 业务逻辑、事务控制 | 直接操作 HttpServletRequest |
| DAO | SQL 执行、结果映射 | 写业务规则 |
这张表能帮你回答「为什么转账逻辑不写在 Servlet 里」这类问题。
最后说个血泪经验:别在答辩前一天才导数据库。我见过太多人代码没问题,卡在 MySQL 时区、驱动版本、端口占用上。提前两天把环境跑通,留出排错时间。这套 JavaWeb 个人网上银行系统源码的价值不在于它多完美,而在于它足够小、足够完整,能让你把 MVC、事务、连接池、分页这些点一个个亲手摸一遍。希望帮到你。
本文还有配套的精品资源,点击获取