news 2026/10/7 1:32:50

JavaWeb个人网上银行系统:MVC架构、数据库设计与部署避坑全解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
JavaWeb个人网上银行系统:MVC架构、数据库设计与部署避坑全解析

简介:一套基于JavaWeb开发的个人网上银行系统源码包,采用MVC分层架构,完整包含项目源码、数据库SQL脚本、设计报告及系统截图,主要面向计算机科学与技术、软件工程、信息安全、大数据与人工智能等专业的在校学生,适用于毕业设计、课程设计、课程大作业或初期项目演示。资源共368个文件,涵盖52个Java源文件、31个JSP页面、84个JavaScript文件、48个CSS样式文件,以及SQL数据库脚本、Word设计报告、HTML页面和war包等,压缩包64.8MB,结构清晰便于按模块查阅。系统实现了用户登录、存款管理、转账操作、定期存款、活期转定期、日志查看及信息增删改查等核心业务,代码均经过功能验证,稳定可运行。目前已获得147人学习下载,既可直接作为毕设或课程作业的交付成果,也方便在现有架构上二次开发,拓展更多互联网金融应用场景,是学习JavaWeb分层开发与数据库设计的完整参考。

1. 个人网上银行系统这个 javaweb 项目:能跑、能交、能扩展

个人网上银行系统是 JavaWeb 课程设计里被点名频率最高的题目之一,这份资源把它做成了一套完整的 MVC 模式工程:源码、数据库脚本、设计报告、系统截图都在一个压缩包里,解压就能用 IDEA 跑通。对在校生,它覆盖毕业设计、课程设计、课程大作业三类场景;对刚入行的开发者,它也是一份 JSP + Servlet + MySQL 的现成参考实现。

我拆过不少号称"毕设源码"的资源包,很多打开后发现代码和文档对不上,数据库脚本还是老编码,跑起来报错能报一下午。这份相对完整的地方在于:需求分析、数据库设计、功能实现、测试报告能互相印对,代码按报告里的功能点逐一验证过,登录、存取款、转账、定期存款、日志查询都能实际操作,不是那种只能看不能跑的黑匣子。

如果你是计算机、软件工程、信息安全、物联网、大数据相关专业的学生,或者需要一个 JavaWeb 参考项目做二次开发,可以把它当起点。下面从架构、数据库、运行步骤、踩坑记录四个方面拆开讲。

2. MVC 分层与项目结构:先读懂工程再动手

不少同学拿到源码第一反应是解压、导入、点运行,结果一堆报错后整个人懵掉。我建议先花二十分钟把工程骨架摸一遍,后面排错会快很多。这个系统的分层非常标准,理解了它,整个 JavaWeb 课程设计的通用套路也就掌握了。

2.1 MVC 模式在个人银行系统里怎么落地

MVC 是 Model、View、Controller 的缩写,落到这套 JavaWeb 项目里就是:JSP 承担 View,Servlet 承担 Controller,JavaBean、Service、DAO 三层合起来承担 Model。一次登录操作的完整链路是:login.jsp 收集用户名和密码,表单提交到 LoginServlet,Servlet 调用 UserService 的 login 方法做业务判断,UserService 再调 UserDao 去 MySQL 里查询比对,结果一层层返回,最后由 JSP 渲染登录成功页或错误提示。

这样拆的好处有三点。第一,职责清晰,页面里不混业务逻辑,Servlet 里不直接拼 SQL,答辩时按层讲就能讲明白。第二,改动有边界,比如要换数据库,只需要动 DAO 层,页面和 Servlet 都不用改。第三,出了问题知道去哪一层找——页面显示不对看 JSP,流程不对看 Servlet,数据不对看 DAO。

下面是登录模块 Controller 层的典型实现,标注了每行在链路里的作用:

// LoginServlet.java —— Controller 层,负责接收请求、调 Service、决定跳转 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 不设编码的话,表单里的中文大概率乱码,这句话必须在读取参数之前执行 request.setCharacterEncoding("UTF-8"); String username = request.getParameter("username"); String password = request.getParameter("password"); UserService userService = new UserService(); User user = userService.login(username, password); if (user != null) { // 登录成功:把用户对象放进 Session,后面页面判断登录态都靠它 HttpSession session = request.getSession(); session.setAttribute("currentUser", user); response.sendRedirect(request.getContextPath() + "/index.jsp"); } else { // 登录失败:塞一个错误信息再转发回登录页,注意是 forward 不是 redirect request.setAttribute("errorMsg", "用户名或密码错误"); request.getRequestDispatcher("/login.jsp").forward(request, response); } }

逻辑说明:这里有两个容易被问倒的细节。第一,request.getParameter 必须在 setCharacterEncoding 之后调用,否则编码设置对已经解析过的参数不生效,中文取出来就是乱码。第二,成功时用 sendRedirect 重定向,地址栏会变成 index.jsp,刷新页面不会重复提交表单;失败时用 forward 转发,request 域里的 errorMsg 才能被 login.jsp 读取到,如果用 redirect,request 域是空的,错误信息就丢了。这两个知识点在课程设计答辩里几乎是必问题。

参数说明:一般这种课程设计项目的账号密码校验放在 Service 层完成,常见做法是 DAO 层按用户名查出用户,Service 层比对密码,返回 null 表示失败。注意这套代码为了演示简洁用的明文比对,真实系统没人敢这么干,后面扩展章节我会讲怎么改成加盐哈希。

2.2 工程目录结构与核心文件职责

导入 IDEA 之前,先看一遍目录能少走很多弯路。这个项目不是 Maven 工程,是传统的 JavaWeb 动态 Web 项目,依赖 jar 放在 WEB-INF/lib 下。源码包按层级分,打开 src 目录后一般能看到这几个包:entity(实体类)、dao(数据访问)、service(业务逻辑)、servlet 或 controller(控制器)、util(工具类)。Web 根目录下是 JSP 页面和静态资源。

静态资源里这套素材比较全:bootstrap.min.css 是整套页面样式的地基,fontawesome-webfont 那几个字体文件负责页面上的小图标,datepicker3.css 是日期选择器的样式,定期存款选日期时会用到,summernote 是个富文本编辑器插件,属于引入的现成组件,不影响核心业务,animate.css 是入场动画类库。认识它们的作用之后,改页面样式就知道往哪个文件里加代码,不用满目录乱翻。

层级代表文件职责改动频率
ControllerLoginServlet.java、DepositServlet.java、TransferServlet.java接收请求、调 Service、跳转页面低
ServiceUserService.java、AccountService.java业务规则、事务边界中
DAOUserDao.java、AccountDao.java、LogDao.java拼 SQL、执行查询中
ModelUser.java、Account.java、Transaction.java与表字段一一对应的实体低
Viewlogin.jsp、index.jsp、deposit.jsp、transfer.jsp页面展示、表单提交高
工具类DBUtil.java获取连接、关闭资源低
配置文件WEB-INF/web.xmlServlet 映射、欢迎页、过滤器注册中

这里必须提一下 WEB-INF/web.xml,它是所有 Servlet 的登记表。课程设计项目里 Servlet 的映射基本都是在这里声明的,格式大概是这样:

<servlet> <servlet-name>LoginServlet</servlet-name> <servlet-class>com.bank.servlet.LoginServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>LoginServlet</servlet-name> <url-pattern>/login</url-pattern> </servlet-mapping>

参数说明:你访问的 URL 是 /bank/login.jsp 还是 /bank/login,取决于这里 url-pattern 的配置。改访问路径、加过滤器、配欢迎页都在 web.xml 里做,排查 404 时要先来这里对一遍 url-pattern,这是很多人忽略的第一现场。

2.3 技术栈选型:为什么这个组合最稳

JSP + Servlet + MySQL 在很多团队眼里已经"过时",但对课程设计和毕设来说,它恰恰是最不容易翻车的一套组合。原因是老师对这套技术栈的评判标准非常成熟,答辩问题基本固定;同时它不依赖 Spring 全家桶,不用背一堆注解和自动装配规则,代码每一行都是自己写的,被追问细节时能答得上来。

运行环境建议锁定这个组合:JDK 1.8、Tomcat 8.5、MySQL 5.7、mysql-connector-java 5.1.x。这套搭配在课程设计资源里最常见,兼容性最好。MySQL 8.0 引入了新的驱动类名和时区要求,如果不改配置直接跑旧项目,大概率会在连接数据库那一步报错,这一点第五章避坑部分会详细展开。

提示:IDEA 请使用 Ultimate 版本,社区版 Community 不带 Tomcat 集成,配置 Web 项目会很痛苦。这是很多人下了源码却跑不起来的第一个隐性原因。

3. 数据库设计与核心功能:从建表到转账一条线

数据库是这类系统的灵魂。报告里的功能点——登录、存款、信息增删改查、转账、定期存款、日志——最后几乎都落在 SQL 上。把表设计看懂了,功能实现基本能猜出七八成。

3.1 表结构:五张表怎么支撑全部业务

这个系统的核心表一般是五张:用户表、账户表、交易流水表、定期存款表、操作日志表。用户表存登录信息和实名信息;账户表存每个账户的余额;交易流水表记录每一笔收支,只允许插入不允许修改;定期存款表记录活期转定期的本金、利率、存期和状态;日志表记录谁在什么时间做了什么操作。

表名用途关键字段
t_user用户登录与身份信息username、password、real_name、id_card
t_account账户与余额account_id、user_id、balance、account_type
t_transaction交易流水account_id、amount、type、create_time、remark
t_fixed_deposit定期存款account_id、amount、rate、term、end_date、status
t_log操作日志user_id、action、detail、create_time

设计上有三个点必须注意。第一,余额字段用 DECIMAL(12,2) 而不是 DOUBLE,float/double 在二进制下无法精确表示小数,算利息和余额会越积越错,这是金融类系统的基本原则,答辩时主动讲出来是加分项。第二,交易流水只增不改,转账做错了只能再做一笔反向流水冲正,不能 UPDATE 改掉原记录。第三,用户和账户分离,一个用户可以开多个账户,为扩展账户类型留余地。

建表 SQL 长这样,主键、外键、索引一次性建好,避免后面手动补:

-- 用户表 CREATE TABLE t_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(32) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL, real_name VARCHAR(32) NOT NULL, id_card VARCHAR(18), phone VARCHAR(15), create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- 账户表 CREATE TABLE t_account ( account_id VARCHAR(20) PRIMARY KEY, user_id INT NOT NULL, account_type CHAR(1) DEFAULT '1', balance DECIMAL(12,2) NOT NULL DEFAULT 0.00, open_time DATETIME DEFAULT CURRENT_TIMESTAMP, KEY idx_user (user_id), CONSTRAINT fk_user FOREIGN KEY (user_id) REFERENCES t_user(id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

参数说明:account_id 用 VARCHAR 是因为银行账号通常带前导零或长度规则,用自增 INT 会在展示时丢失格式。DECIMAL(12,2) 表示最大 10 位整数加 2 位小数,对个人网银足够。外键约束在这里是合理的,课程设计数据量小,不需要考虑删除性能。

3.2 登录与存款:一条请求如何落到数据库

登录链路在第二章已经讲过,这里重点说存取款。存款(存钱)这个操作表面上是"余额加一个数",但正确实现必须包含两步:更新账户余额,同时插入一条交易流水。两步必须在同一个事务里完成,否则就会出现余额变了但没有流水记录,或者有流水但余额没变的账实不符。

常见的一个错误是:先 SELECT 出余额,在 Java 里加好金额,再 UPDATE 回去。这种"读改写"模式在并发下会丢更新,两个人的操作同时发生时,后写的人会把先写的人的结果覆盖掉。正确做法是把加法直接写进 SQL,让数据库自己完成原子操作:

// 存款核心代码:余额更新 + 流水插入,同事务 public boolean deposit(String accountId, double amount) throws Exception { Connection conn = DBUtil.getConnection(); try { conn.setAutoCommit(false); // 关闭自动提交,事务开始 String sql1 = "UPDATE t_account SET balance = balance + ? WHERE account_id = ?"; PreparedStatement ps1 = conn.prepareStatement(sql1); ps1.setDouble(1, amount); ps1.setString(2, accountId); ps1.executeUpdate(); String sql2 = "INSERT INTO t_transaction(account_id, amount, type, remark) " + "VALUES(?, ?, 'DEPOSIT', '存款')"; PreparedStatement ps2 = conn.prepareStatement(sql2); ps2.setString(1, accountId); ps2.setDouble(2, amount); ps2.executeUpdate(); conn.commit(); // 两步都成功才提交 return true; } catch (Exception e) { conn.rollback(); // 任何一步失败,全部回滚 throw e; } finally { DBUtil.close(conn); } }

逻辑说明:balance = balance + ? 这种写法把"读余额、算新值、写回去"压缩成数据库内部的一次原子操作,并发情况下由数据库行级锁保证正确性。PreparedStatement 的作用是预编译加参数绑定,既防 SQL 注入,又避免反复拼字符串产生语法错误。commit 和 rollback 的分支是这段代码的保命线,课程设计里很多"账对不上"的 bug 就是少了 rollback。

报告里提到的"存款(存钱)、删除信息、修改信息、查询信息",拆开看就是针对账户或流水列表的增删改查。删除信息不是把交易记录物理删掉,常见做法是软删除,给记录加状态字段置为无效;修改信息一般指改账户绑定的手机号、地址这类非核心字段,不动余额。

3.3 转账与活期转定期:事务和状态机的关键点

转账是所有银行系统里最容易出问题的地方。一笔转账涉及三个操作:扣转出方余额、加转入方余额、记录流水。任何一个失败都不能留下半截数据,所以整个流程必须包在事务里。更进一步,扣款前要锁定转出账户的行,防止转账过程中余额被其他操作改掉:

// 转账核心:先锁行,再校验余额,最后扣款 + 入账 String lockSql = "SELECT balance FROM t_account WHERE account_id = ? FOR UPDATE"; String deductSql = "UPDATE t_account SET balance = balance - ? WHERE account_id = ? AND balance >= ?"; String creditSql = "UPDATE t_account SET balance = balance + ? WHERE account_id = ?"; Connection conn = DBUtil.getConnection(); conn.setAutoCommit(false); PreparedStatement psLock = conn.prepareStatement(lockSql); psLock.setString(1, fromAccountId); ResultSet rs = psLock.executeQuery(); if (rs.next() && rs.getDouble("balance") < amount) { conn.rollback(); throw new RuntimeException("余额不足"); } // 扣款、入账、写流水,全部执行成功后 commit

逻辑说明:SELECT ... FOR UPDATE 是数据库层的行锁,锁住转出账户直到事务结束,别的并发请求在这个事务提交前改不了这一行。扣款 SQL 里故意多写了 AND balance >= ? 条件,这是第二道保险——即使前面的校验逻辑有漏洞,扣款也会因为余额不足而更新 0 行,通过 executeUpdate 的返回值就能判断扣款是否真正成功。转账完成后往 t_transaction 插入两条记录,一条转出、一条转入,备注里填对端账号,这样查账能看到完整链路。

活期转定期可以理解成一个状态机:活期账户余额减少,定期存款表新增一条记录,状态为"存期中";到期后执行"到期转活期",定期记录状态变成"已结清",本金加利息回到活期账户。定期表里的 rate、term 字段决定利息计算,这部分逻辑不复杂但很考察细心,答辩时可以多讲两步状态流转,属于容易拿分的点。

4. IDEA 运行 javaweb 项目全流程:导入、配置、部署、验证

前面讲了架构和数据库,这一章直接落地。按下面的顺序操作,正常情况下半小时内能跑起来。顺序不要乱,环境没配好之前去改代码,只会越改越乱。

4.1 环境版本:别用最新,用最稳

课程设计项目对运行环境敏感,版本选错会连数据库这一步都过不去。我一般会锁定这套组合:JDK 1.8、Tomcat 8.5 或 9.0、MySQL 5.7、mysql-connector-java 5.1.4x。确定本机版本最快的方法是命令行跑 java -version 和 mysql --version,Tomcat 版本看 IDEA 配置页或安装目录。如果本机已经装了 MySQL 8.0,也不是不能跑,但要改驱动类名和连接串,具体差异见下表:

组件推荐版本说明与注意点
JDK1.8高版本 JDK 编译旧代码可能报非法字符或反射错误
Tomcat8.5.xx稳定,Servlet 3.1 规范
MySQL5.7与项目脚本兼容性最好
驱动 jarmysql-connector-java-5.1.49与 MySQL 5.7 配套,驱动类名 com.mysql.jdbc.Driver
MySQL 8.0 替换方案mysql-connector-java-8.0.x驱动类名 com.mysql.cj.jdbc.Driver,连接串需加 serverTimezone

4.2 导入项目与数据库初始化

第一步,解压资源包,用 IDEA 直接 Open 项目目录。这个项目不是 Maven 工程,不要用 New 的方式创建,直接 Open 后 IDEA 会自动识别出 Web 项目结构。如果源码里有 pom.xml,说明是 Maven 版本,导入后等依赖下载完即可;如果没有,检查 WEB-INF/lib 下的 jar 是否完整,IDEA 通常会把这个目录识别成依赖库。IDEA 弹"Add as Maven Project"之类的提示时,确认一下再点,别把非 Maven 工程误转成 Maven,否则后续跑起来依赖会乱。

第二步,初始化数据库。用 Navicat 或命令行新建一个 bank 数据库,字符集选 utf8mb4,然后执行资源包里的 .sql 脚本。命令行导入的命令是:

mysql -uroot -p --default-character-set=utf8 -e "CREATE DATABASE bank DEFAULT CHARSET utf8mb4;" mysql -uroot -p --default-character-set=utf8 bank < bank.sql

参数说明:--default-character-set=utf8 必须加,否则脚本里的中文注释和初始数据可能变成乱码。bank.sql 是资源包里数据库脚本的通用命名,实际解压后可能叫 bank.sql、bank_data.sql 之类,以你解压出来的实际文件名为准,执行的时候注意当前目录要对。

第三步,改数据库连接配置。连接信息一般在 src 根目录的 jdbc.properties 或者 DBUtil.java 里,把用户名密码改成自己本机的:

jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/bank?useUnicode=true&characterEncoding=utf8 jdbc.username=root jdbc.password=你的数据库密码

这段配置里只需要改第三行和第四行,username 和 password 换成你本机 MySQL 的登录信息。如果改了密码还报错,先用 Navicat 手动连一次 MySQL 排除服务问题,再去查连接串里的数据库名是否和第一步建的一致。

4.3 配置 Tomcat 并启动验证

IDEA 里点 Run → Edit Configurations,新建一个 Tomcat Server → Local。在 Deployment 页签里点加号,选 Artifact,把这个项目的 war exploded 添加进去,Application context 一栏填 /bank。Deployment 里优先选 war exploded 而不是 war,前者是解压目录,改完 JSP 页面不用重新打包,热部署更快。这一步是新手最容易漏的:只配置了 Tomcat 没部署 Artifact,启动后会看到 Tomcat 默认页但访问不了项目,卡在这个环节的人最多。

启动之后浏览器访问 http://localhost:8080/bank/login.jsp,如果能打开登录页,说明 Web 层已经通了。接下来登录测试,初始账号通常在报告里写了,常见做法是 admin / 123456。如果报告没写,或者密码不对,直接查数据库把测试账号的密码重置成已知值:

-- 课程设计项目多数字段是明文或 MD5,测试时可以把密码直接改成明文 123456 UPDATE t_user SET password = '123456' WHERE username = 'admin';

登录成功后,按顺序做三个验证:先做一笔小额存款,回账户首页看余额是否增加,再执行 SELECT * FROM t_transaction WHERE type='DEPOSIT'; 确认流水也写进去了;然后测试转账,看转出余额扣减、转入余额增加、流水出现两条记录;最后打开日志页面看操作记录。这三步走完,代码层面就确认可用了。

5. 避坑指南:跑这个系统最常翻车的 5 个坑

环境问题占了这类课程设计项目报错的一大半。下面五条是我跑这类 javaweb 项目时真实遇到过的,按现象、原因、解决的顺序写,覆盖从导入到运行的完整链路。遇到问题先对照这一章,比自己瞎猜快得多。

5.1 数据库脚本导入报错,中文全变问号

现象:用 Navicat 导入 .sql 时报 Unknown character set 或乱码报错,表能建出来但数据全是"????"。

原因:脚本文件本身的编码是 GBK,而客户端连接用的是 utf8,两边不一致;也可能是脚本里建表语句的 CHARSET 和文件实际编码对不上。这种情况很隐蔽,光改连接串不转文件编码是治不好的。

解决:先用编辑器把 .sql 文件转成 UTF-8 无 BOM 格式,再重新导入;命令行导入时加 --default-character-set=utf8。如果手头没有编辑器,也可以在 MySQL 里先执行 SET NAMES gbk; 再 source 脚本,但终归不如直接把文件编码统一成 utf8 干净。

5.2 Tomcat 能启动,访问项目路径却 404

现象:Tomcat 正常启动,访问 http://localhost:8080/ 能出 Tomcat 默认页,但访问 /bank/login.jsp 就是 404,控制台也不报错。

原因:IDEA 的 Run Configuration 里只添加了 Tomcat,没有在 Deployment 页签部署项目的 Artifact;或者 Application context 填了别的名字,实际访问路径对不上。九成新手翻车都在这个环节。

解决:Run → Edit Configurations → Deployment → 点加号添加 Artifact,Application context 统一填 /bank,重启后访问 http://localhost:8080/bank/login.jsp。改完如果还 404,就去看 WEB-INF/web.xml 里的 welcome-file 和 url-pattern,确认启动页路径没写错。

5.3 找不到 JDBC 驱动类:NoClassDefFoundError

现象:启动控制台报 java.lang.ClassNotFoundException: com.mysql.jdbc.Driver,或者页面 500 提示找不到驱动类。

原因:mysql-connector-java 的 jar 虽然放在某个目录里,但没被编译进 WEB-INF/lib,Tomcat 运行时根本看不到它;如果项目是 Maven 管理的,则是 pom.xml 里没加依赖或依赖没下载完。这个报错在 IDEA 里经常被误报成"编译问题",其实运行时依赖和编译依赖是两回事。

解决:非 Maven 项目在 IDEA 里右键 jar 文件选择 Add as Library,或者直接把 jar 复制到 WEB-INF/lib 目录下,这类动态 Web 项目的 jar 实际就该放这里。Maven 项目检查 pom.xml 里 mysql 依赖的坐标和版本,把依赖下载完成后重新导入。

5.4 数据库连接报 Communications link failure

现象:启动后访问页面,报 Cannot create PoolableConnectionFactory 或 Communications link failure,翻译过来是"连不上数据库"。

原因:一半是 MySQL 8.0 的问题——驱动类名从 com.mysql.jdbc.Driver 改成了 com.mysql.cj.jdbc.Driver,连接串上还要求带 serverTimezone;另一半是数据库名或密码写错,或者 MySQL 服务根本没启动。这个报错洋气,但原因往往很朴素。

解决:如果本机是 MySQL 8.0,把 jdbc.properties 改成 driverClassName=com.mysql.cj.jdbc.Driver,url 末尾加上 &serverTimezone=Asia/Shanghai;再排查密码和数据库名,用 Navicat 先手动连一次 MySQL,确认服务在线且能正常登录。

5.5 登录或存款后中文乱码

现象:页面打开正常,但登录跳转后用户名、备注、错误提示全是问号,或者数据库里存进去的中文读出来是问号。

原因:JSP 页面编码、Servlet 接收参数编码、数据库连接 URL 编码、表字符集四处只要有一处不是 utf8,就会出现乱码。乱码是最考耐心的 bug,因为四层错哪一层的表现都一样。

解决:四层统一。JSP 顶部写 <%@ page contentType="text/html;charset=UTF-8" language="java" %>;Servlet 的 doGet/doPost 开头都加 request.setCharacterEncoding("UTF-8");连接串 URL 带 useUnicode=true&characterEncoding=utf8;建表用 DEFAULT CHARSET=utf8mb4。如果项目里有一个编码过滤器,直接在里面统一设置,比每个 Servlet 里写一遍省事:

// EncodingFilter.java —— 过滤器里统一设置请求和响应编码 public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { req.setCharacterEncoding("UTF-8"); resp.setCharacterEncoding("UTF-8"); chain.doFilter(req, resp); }

这个过滤器要在 web.xml 里注册,并配到所有路径上。注册之后,只要记住一条规则:页面、连接、表全部 utf8,乱码问题基本绝迹。

6. 二次开发:把课程设计升级成能拿得出手的演示项目

项目跑通只是第一步。想在答辩里出彩,至少要补两个短板:密码存储和流水分页。密码加盐是安全上最容易被老师一句话问倒的点。原来的实现如果是明文或裸 MD5,直接改成加盐哈希,代码量很小但讲出来完全不同:

// 常见做法:MD5 加盐后入库,避免数据库泄漏时密码直接暴露 MessageDigest md = MessageDigest.getInstance("MD5"); byte[] bytes = md.digest((salt + password).getBytes("UTF-8")); StringBuilder sb = new StringBuilder(); for (byte b : bytes) { sb.append(String.format("%02x", b)); } String hashed = sb.toString(); // 把 hashed 存库,salt 按用户单独生成

逻辑说明:salt 每个用户随机生成,存到用户表单独一列。校验时取出该用户的 salt 重新计算再比对。MD5 本身并不安全,真实项目要用 bcrypt 这类慢哈希,但课程设计做到"加盐"这一步已经能展示安全意识,比明文强一个量级。

分页也很简单,给查询日志的 DAO 加 LIMIT offset, size 两个参数,配合 JSP 页面的上一页下一页按钮,改动二十行以内,但演示效果完全不同——数据量一大,没分页的页面能卡两三秒,有分页的毫秒级响应,这是答辩时肉眼可见的差异。

最后一个建议是学会验证事务。答辩前用两个浏览器开两个窗口,对同一个账户同时发起两笔转账,操作完成后查 t_transaction 和 balance,能对上说明事务和行锁生效了;再故意输入一个不存在的转入账号,确认转出方余额没变。这套验证做完,老师基本不会再追问系统健壮性,因为方案和证据都摆在那了。

从那以后我每次拿到一个 javaweb 项目,都会强制先走一遍这套流程:看 DBUtil、看 web.xml、看五张表的关系,然后跑通转账再谈其他。这个习惯帮我筛掉过不少看着完整实际跑不起来的资源,也让我接到任何旧项目时心里都有底。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/7 1:32:47

C++飞机大战源码解析:从版本演进学EasyX游戏开发

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/7 1:32:38

NFC标签双端App唤起与未安装兜底:Android/iOS全链路配置

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/7 1:31:32

南大计院夏令营机试与笔试题型破解:刷题路线与算法模板全攻略

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/7 1:30:58

ESP32隐藏射频通路:寄存器级IQ采样与嵌入式SDR实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/7 1:30:21

RC电路微分与积分全解析:从原理到工程实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/7 1:30:17

TL431不是稳压芯片,而是精密并联基准源

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华