ARTICLE DETAIL

资讯详情

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

JSP+Servlet+MySQL个人理财管理系统设计与实现指南

JSP+Servlet+MySQL个人理财管理系统设计与实现指南 简介一份基于Java/JSP的个人理财管理系统设计与实现毕业设计论文主要面向计算机、软件工程等专业的本科生和需要完成课程设计的学生。资料包内共包含1个docx格式文档压缩包整体约1.37MB文档内含中英文摘要、目录、需求分析、系统设计、数据库设计及实现细节章节结构完整。系统覆盖用户管理、收入记录、支出记录、财产查询、还贷助手、储蓄提醒等核心功能结合JSP动态网页、数据库操作与数据挖掘技术对个人收支分析和理财建议生成进行了论述。读者通过该文档既能了解系统架构与模块实现思路也能借鉴毕业设计的写作规范与技术表述其中的图表和关键代码对理解数据库关系与页面交互有直接帮助尤其适合作为同类题目开题、写作和答辩准备的参考资料。该资源已有49人浏览学习。1. 个人理财管理系统一个能直接跑的 JSP 毕业设计资源先说结论这份《基于 Java 的个人理财管理系统设计与实现》是一份典型的 Java Web 毕业设计资源技术栈老但完整——JSP Servlet MySQL TomcatB/S 架构浏览器即开即用不需要安装客户端。它不是什么高并发、微服务架构的企业级系统而是一个把「个人收支管理」这件事做完整、做闭环的教学型项目涵盖用户管理、收支记录、财产查询、还贷助手、储蓄提醒这几条核心业务线。如果你正在做 Java Web 方向的课程设计或毕业设计或者想快速拿一个能演示、能写进论文的完整项目练手这个资源是合适的。反过来如果你指望它对接支付宝、做智能投顾那趁早换个方向——它的价值在教学和复现不在生产。我拆这份资源的过程里把它的数据库关系、页面流转、权限模型都过了一遍下面按「这东西怎么跑起来、表怎么设计、功能怎么实现、坑在哪」的顺序讲清楚你照着做半天内能跑通。2. 技术选型与数据流为什么是 JSP MySQL Tomcat 的组合2.1 技术栈的合理性分析很多新手看到 JSP 会觉得过时了但放在毕业设计这个场景里这个选型恰恰是合理的。JSP 的本质是服务端动态网页技术它跟 JavaScript、VBScript 这类客户端脚本最根本的区别在服务端执行——浏览器拿到的已经是渲染好的 HTML业务逻辑在服务器上跑天然适合做管理类系统。这套系统选 JSP 而不是 Spring Boot不是因为 Spring Boot 不好而是因为毕业设计要能讲清楚原理纯 JSP Servlet 的方式把请求处理、数据库操作、页面渲染都摊开了论文里好写答辩时好讲查重也好过。数据流是典型的 B/S 三層结构浏览器发 HTTP 请求 → Tomcat 里的 JSP/Servlet 接收并处理 → JDBC 操作 MySQL 数据库 → 结果封装后返回页面渲染。这个链路里Tomcat 7.0 作为 Servlet 容器负责解析 JSP 和调度 ServletMySQL 负责持久化MyEclipse 只承担 IDE 的角色。整个系统没有引入 MyBatis、Hibernate 这类 ORM 框架SQL 全手写这在数据量不大、表结构清晰的项目里反而更直观也更容易排查问题。2.2 环境配置清单与版本匹配原文档里提到的环境是 Windows 8 系统、MyEclipse 8.6、Tomcat 7.0、MySQL 数据库但实际复现时不必死守这套。我建议你按下面的组合来配兼容性最好组件推荐版本说明JDKJDK 1.7 或 1.8Tomcat 7.0 在 JDK 8 下运行没问题JDK 9 会有兼容性警告Tomcat7.0.x原文档指定版本Servlet 3.0 规范足够支撑 JSP 2.1MySQL5.6 或 5.78.0 可以用但驱动要换mysql-connector-java 5.1.x 连 8.0 会报时区错误IDEMyEclipse 8.6 或 Eclipse EE新版 Eclipse 也能导入关键是配好 Tomcat 运行时浏览器Chrome / Firefox / Edge原文档提的 IE11 现在没必要用这里有个容易踩的细节MySQL 驱动包版本要和 MySQL 服务端版本匹配。如果你本地装的是 MySQL 5.7用 mysql-connector-java-5.1.49.jar 没问题如果是 MySQL 8.0必须换成 8.0 系列的驱动否则报Communications link failure或者Public Key Retrieval is not allowed。这个坑我在下面避坑章节会展开讲。2.3 环境变量与项目导入操作拿到资源包后不是双击就能跑的需要手动导入 MyEclipse 或 Eclipse。整个流程分四步# 1. 检查 JDK 版本确保 1.7 或 1.8 java -version # 2. 检查环境变量是否配置 echo %JAVA_HOME% echo %CATALINA_HOME%如果JAVA_HOME为空需要先配置。Windows 下右键此电脑→属性→高级系统设置→环境变量新建 JAVA_HOME 指向 JDK 安装目录比如C:\Program Files\Java\jdk1.8.0_202然后在 Path 里加上%JAVA_HOME%\bin。CATALINA_HOME 指向 Tomcat 解压目录比如D:\apache-tomcat-7.0.108这个变量决定 Tomcat 启动时从哪加载配置。接下来导入项目到 IDE# 在 MyEclipse 或 Eclipse 中 File → Import → General → Existing Projects into Workspace → Select root directory 选择解压后的项目文件夹 → 勾选 Copy projects into workspace建议勾选避免污染原始资源 → Finish导入后不要急着启动先右键项目 → Properties → Java Build Path检查 Libraries 里是否有 mysql-connector-java 的 jar 包没有就通过Add External JARs手动添加。然后右键项目 → Run As → Run on Server选择你配置好的 Tomcat 7.0。这个操作顺序如果颠倒很容易出现 Tomcat 能启动但访问页面报 404 或 500 的情况。2.4 项目的目录结构与代码组织导入成功后先花十分钟看目录结构不用急着跑搞清楚代码怎么组织对后面改功能很重要。典型的 JSP 项目结构是PersonalFinanceSystem/ ├── src/ # Java 源码目录 │ ├── com.xxx.dao/ # 数据访问层JDBC 封装 │ ├── com.xxx.servlet/ # Servlet 控制器 │ ├── com.xxx.model/ # 实体类User, Income, Expense 等 │ └── com.xxx.util/ # 工具类DBUtil, DateUtil 等 ├── WebContent/ │ ├── index.jsp # 入口页面 │ ├── login.jsp # 登录页 │ ├── register.jsp # 注册页 │ ├── admin/ # 管理端页面 │ ├── user/ # 用户端页面 │ ├── WEB-INF/ │ │ ├── web.xml # 部署描述符Servlet 映射 │ │ └── lib/ # 依赖的 jar 包 │ └── css/ js/ images/ # 静态资源 └── sql/ └── finace.sql # 初始化数据库脚本这个结构是教科书式的 MVC 分层虽然没用到框架但 DAO、Servlet、JSP 各司其职。改功能的时候你在src里找对应类改完刷新页面就能看到效果比 Spring Boot 的自动装配直观得多——这也是这个资源适合做毕业设计的原因每一层都能拿出来写论文。3. 数据库设计与核心表结构五张表撑起全部业务3.1 概念模型到物理模型的映射系统的业务功能包括系统用户管理、收入记录、支出记录、财产查询、还贷助手、储蓄提醒对应的数据实体至少有用户、收入流水、支出流水、贷款信息、储蓄计划这几类。这是一个典型的一对多关系模型一个用户对应多笔收入记录、多笔支出记录、多条贷款计划和储蓄计划。我根据原文档的功能描述和常见的 JSP 毕设实现方式整理出最核心的五张表结构。以用户表为中心其他表都通过uid外键关联这样设计的好处是查询某个用户的财产状况时只需要一条JOIN就能聚合所有数据不需要复杂的事务处理。3.2 建表 SQL 与字段设计说明下面是核心建表 SQL你可以直接执行也可以参考它调整字段-- 用户表 CREATE TABLE t_user ( uid INT NOT NULL AUTO_INCREMENT COMMENT 用户ID, username VARCHAR(50) NOT NULL COMMENT 登录名, password VARCHAR(64) NOT NULL COMMENT 密码建议MD5加密存储, realname VARCHAR(50) DEFAULT NULL COMMENT 真实姓名, phone VARCHAR(20) DEFAULT NULL COMMENT 手机号, email VARCHAR(100) DEFAULT NULL COMMENT 邮箱, create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 注册时间, PRIMARY KEY (uid), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT系统用户表; -- 收入记录表 CREATE TABLE t_income ( id INT NOT NULL AUTO_INCREMENT COMMENT 收入ID, uid INT NOT NULL COMMENT 所属用户, income_type VARCHAR(30) NOT NULL COMMENT 收入类型工资/兼职/投资/其他, amount DECIMAL(10,2) NOT NULL COMMENT 收入金额, income_date DATE NOT NULL COMMENT 收入日期, remark VARCHAR(255) DEFAULT NULL COMMENT 备注, PRIMARY KEY (id), KEY idx_uid_date (uid, income_date) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT收入记录表; -- 支出记录表 CREATE TABLE t_expense ( id INT NOT NULL AUTO_INCREMENT COMMENT 支出ID, uid INT NOT NULL COMMENT 所属用户, expense_type VARCHAR(30) NOT NULL COMMENT 支出类型餐饮/交通/购物/居住/其他, amount DECIMAL(10,2) NOT NULL COMMENT 支出金额, expense_date DATE NOT NULL COMMENT 支出日期, remark VARCHAR(255) DEFAULT NULL COMMENT 备注, PRIMARY KEY (id), KEY idx_uid_date (uid, expense_date) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT支出记录表; -- 还贷信息表 CREATE TABLE t_loan ( id INT NOT NULL AUTO_INCREMENT COMMENT 贷款ID, uid INT NOT NULL COMMENT 所属用户, loan_name VARCHAR(100) NOT NULL COMMENT 贷款名称如房贷、车贷, total_amount DECIMAL(12,2) NOT NULL COMMENT 贷款总额, remaining_amount DECIMAL(12,2) NOT NULL COMMENT 剩余还款额, monthly_payment DECIMAL(10,2) NOT NULL COMMENT 月供金额, due_date DATE NOT NULL COMMENT 每期到期日, is_active TINYINT DEFAULT 1 COMMENT 是否生效1生效0已结清, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT还贷信息表; -- 储蓄提醒表 CREATE TABLE t_saving ( id INT NOT NULL AUTO_INCREMENT COMMENT 储蓄计划ID, uid INT NOT NULL COMMENT 所属用户, goal_name VARCHAR(100) NOT NULL COMMENT 储蓄目标名称, target_amount DECIMAL(12,2) NOT NULL COMMENT 目标金额, saved_amount DECIMAL(12,2) DEFAULT 0.00 COMMENT 已存金额, monthly_save DECIMAL(10,2) NOT NULL COMMENT 每月计划存入, remind_day TINYINT DEFAULT 1 COMMENT 每月提醒日, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT储蓄提醒表;这段 SQL 里有几个字段设计值得你写论文时重点说明。amount字段选DECIMAL(10,2)而不是FLOAT是因为金额计算必须精确浮点类型在累加时会出现精度丢失财务数据容不得这种误差。income_type和expense_type用字符串存储类型名称而不是用外键关联字典表是因为这个项目的收支类型是固定枚举、不需要动态扩展用字符串更直观查询时也不需要多一次 JOIN。索引设计上idx_uid_date这个复合索引是必要的——财产查询和收支统计最常见的 SQL 是某用户某时间段的收支汇总这个索引能让WHERE uid ? AND income_date BETWEEN ? AND ?这类查询走索引避免全表扫描。UNIQUE KEY uk_username是硬性约束防止同一用户名重复注册这个在 DAO 层插入时也要捕获唯一键冲突异常。is_active这个软删除标记也很关键还贷记录结清后不是真的删掉而是置为 0这样历史统计时还能查到已结清的贷款不影响报表完整性。3.3 表关联关系与典型查询语句理解了表结构还要知道它们怎么配合。最核心的查询是财产汇总——把收入表和支出表按用户聚合算出一段时间内的结余-- 查询用户在指定时间段内的收入汇总 SELECT COALESCE(SUM(amount), 0) AS total_income FROM t_income WHERE uid ? AND income_date BETWEEN ? AND ?; -- 查询用户在指定时间段内的支出汇总 SELECT COALESCE(SUM(amount), 0) AS total_expense FROM t_expense WHERE uid ? AND expense_date BETWEEN ? AND ?; -- 查询当前净资产收入累计 - 支出累计 已存金额 SELECT (SELECT COALESCE(SUM(amount), 0) FROM t_income WHERE uid ?) - (SELECT COALESCE(SUM(amount), 0) FROM t_expense WHERE uid ?) (SELECT COALESCE(SUM(saved_amount), 0) FROM t_saving WHERE uid ?) AS net_asset;这里用COALESCE包裹SUM很关键——如果某用户没有收入记录SUM返回NULL而不是0NULL参与减法运算会导致整个结果变成NULL页面上就显示空白。这是新手写统计 SQL 最容易翻车的点务必记得。三条 SQL 分别服务于收入统计、支出统计和财产查询三个页面DAO 层把它们封装成IncomeDao.getTotalByDateRange(uid, start, end)、ExpenseDao.getTotalByDateRange(uid, start, end)这类带参方法Servlet 里调用后把结果塞进request.setAttribute()JSP 页面再用${totalIncome}渲染。4. 核心功能模块拆解从登录到储蓄提醒的完整链路4.1 登录认证与 Session 管理登录是系统第一个要实现的模块也是最容易做得粗糙的地方。这套系统的标准做法是用户提交用户名和密码Servlet 接收后调用UserDao.login(username, password)查询数据库如果匹配成功就把用户对象存进Session然后重定向到首页不匹配则返回登录页并提示错误。// LoginServlet.java 核心逻辑 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 1. 获取表单提交参数 String username request.getParameter(username); String password request.getParameter(password); // 2. 调用 DAO 层验证用户密码建议 MD5 后比对 UserDao dao new UserDao(); User user dao.login(username, DigestUtils.md5Hex(password)); if (user ! null) { // 3. 登录成功用户对象放入 Session HttpSession session request.getSession(); session.setAttribute(loginUser, user); session.setMaxInactiveInterval(30 * 60); // 30分钟无操作失效 response.sendRedirect(index.jsp); // 重定向避免表单重复提交 } else { // 4. 登录失败回传错误信息 request.setAttribute(errorMsg, 用户名或密码错误); request.getRequestDispatcher(login.jsp).forward(request, response); } }这段代码有四个细节值得注意。第一密码存储用 MD5 加密而不是明文虽然 MD5 本身有彩虹表风险但对于毕业设计这个场景已经够用了比明文存储安全一个量级。第二登录成功后用sendRedirect而不是forward这个区别很微妙——重定向会改变浏览器地址栏 URL刷新页面时不会重复提交表单如果用forward用户在首页按 F5 会弹出确认重新提交表单的对话框体验很差。第三setMaxInactiveInterval(30 * 60)设了 Session 超时时间这是防止用户挂机太久导致数据操作异常的保护机制。第四所有后续页面的权限控制都依赖session.getAttribute(loginUser)是否为 null每个 JSP 页面顶部都要有这行判断否则未登录用户直接访问 URL 就能跳过认证。4.2 收支记录模块表单校验与异步交互收支记录是这个系统的核心数据来源。收入记录和支出记录的表单结构对称都是类型 金额 日期 备注四项区别只在类型枚举不同。实现时要注意两个关键点一是金额的格式校验必须在服务端做不能只靠前端 JS 校验——用户完全可以绕过浏览器直接 POST 请求二是页面上可以选择是否用 Ajax 异步提交但毕设项目为了演示效果清晰通常直接用表单同步提交。// SaveExpenseServlet.java 支出记录保存逻辑 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 1. 从 Session 中获取当前登录用户 User user (User) request.getSession().getAttribute(loginUser); if (user null) { response.sendRedirect(login.jsp); return; } // 2. 获取并校验表单参数 String expenseType request.getParameter(expenseType); String amountStr request.getParameter(amount); String dateStr request.getParameter(expenseDate); String remark request.getParameter(remark); // 金额必须是非负数字否则直接返回错误 if (amountStr null || !amountStr.matches(^\\d(\\.\\d{1,2})?$)) { request.setAttribute(errorMsg, 金额格式不正确); request.getRequestDispatcher(expense_add.jsp).forward(request, response); return; } // 3. 组装实体对象并调用 DAO 保存 Expense expense new Expense(); expense.setUid(user.getUid()); expense.setExpenseType(expenseType); expense.setAmount(new BigDecimal(amountStr)); expense.setExpenseDate(Date.valueOf(dateStr)); expense.setRemark(remark); ExpenseDao dao new ExpenseDao(); boolean success dao.insert(expense); // 4. 保存成功后跳转到列表页 if (success) { response.sendRedirect(expense_list.jsp?currentPage1); } else { request.setAttribute(errorMsg, 保存失败请重试); request.getRequestDispatcher(expense_add.jsp).forward(request, response); } }这段代码里最关键的是金额校验那行正则^\\d(\\.\\d{1,2})?$它要求金额必须是整数或最多两位小数的数字直接从源头拦截了abc、12.345这类非法输入。new BigDecimal(amountStr)而不是Double.parseDouble(amountStr)原因跟建表时选 DECIMAL 一样防止浮点精度问题。另一个细节是Date.valueOf(dateStr)要求日期格式严格是yyyy-MM-dd如果前端日期控件传的是2024/12/01这种格式这里会直接抛IllegalArgumentException所以前端控件的dateFormat必须和后端解析格式一致——这是个很隐蔽的坑后面避坑章节单独讲。收支列表页通常还要做分页因为记录多了不现实一次全查出来。分页的核心 SQL 是LIMIT offset, pageSizeoffset 的计算公式是(currentPage - 1) * pageSize。JSP 页面底部渲染上一页、下一页的链接时要注意保留查询条件否则翻页后筛选条件就丢了。4.3 财产查询与还贷助手报表聚合与倒计时提醒财产查询模块的界面逻辑是把第 3 章那几个汇总 SQL 的结果拼到一个页面上展示四项核心指标总收入、总支出、当前结余、储蓄总额。这个页面的数据全部是只读的不涉及写操作所以不需要表单只需要一个 Servlet 在doGet里查完数据塞进request然后forward到 JSP 渲染。有个值得做的增强是展示本月收支和近半年趋势做法就是在 DAO 里多写两个聚合查询按月分组-- 近半年每月支出趋势 SELECT DATE_FORMAT(expense_date, %Y-%m) AS month, SUM(amount) AS total FROM t_expense WHERE uid ? AND expense_date DATE_SUB(CURDATE(), INTERVAL 6 MONTH) GROUP BY DATE_FORMAT(expense_date, %Y-%m) ORDER BY month;这个 SQL 用DATE_FORMAT把日期归一化成YYYY-MM格式再做GROUP BY就能得到每个月的小计。注意DATE_SUB(CURDATE(), INTERVAL 6 MONTH)是函数判断如果表数据量大、又没有在expense_date上建索引这个查询会全表扫描——好在个人理财系统数据量级就是几千条性能没压力。还贷助手模块的业务逻辑是用户录入每笔贷款后系统根据due_date判断当前是否临近到期日在首页或列表页醒目位置提示距离房贷还款日还有 X 天。实现上不需要定时任务只要在请求处理时动态计算即可// LoanHelper.java 计算还款倒计时 public int getDaysUntilDue(Loan loan) { LocalDate today LocalDate.now(); LocalDate dueDate loan.getDueDate().toLocalDate(); return (int) ChronoUnit.DAYS.between(today, dueDate); }ChronoUnit.DAYS.between是 Java 8 时间 API 的方法返回值正数表示还没到期负数表示已经逾期。页面上可以根据这个值的不同显示不同状态大于 7 天显示正常小于等于 7 天显示橙色提醒小于 0 显示红色逾期。这个逻辑很直观但不复杂写论文时可以作为一个功能亮点。4.4 储蓄提醒模块目标跟踪与手动更新机制储蓄提醒是最后一个核心模块它的业务模型是目标金额 vs 已存金额的差额跟踪。用户创建一个储蓄目标比如明年换电脑目标 8000每月存 1000系统在remind_day那天在页面上显示提醒用户每次手动存入后更新saved_amount字段。这个模块的实现比想象中简单因为不涉及自动扣款或银行接口核心就是一张表的UPDATE操作// SavingServlet.java 更新储蓄进度 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { int savingId Integer.parseInt(request.getParameter(id)); String addAmountStr request.getParameter(addAmount); // 更新已存金额saved_amount saved_amount 本次存入 SavingDao dao new SavingDao(); boolean success dao.addSavedAmount(savingId, new BigDecimal(addAmountStr)); if (success) { // 检查是否达成目标 Saving saving dao.findById(savingId); if (saving.getSavedAmount().compareTo(saving.getTargetAmount()) 0) { request.setAttribute(message, 恭喜已达成储蓄目标); } response.sendRedirect(saving_list.jsp); } }这段逻辑有两个值得注意的地方。第一addSavedAmount对应的 SQL 是UPDATE t_saving SET saved_amount saved_amount ? WHERE id ?而不是先SELECT再UPDATE——因为先查后改在并发场景下会丢更新虽然个人系统并发低但写正确的方式是好习惯。第二比较金额用compareTo而不是因为saved_amount和target_amount都是BigDecimal对象Java 里不能用原生比较运算符直接比较两个对象的大小。5. 部署避坑指南从 JDK 版本到 MySQL 编码的五个翻车点5.1 Tomcat 启动后访问 404根路径映射没配对现象Tomcat 能正常启动localhost:8080能看到 Tomcat 首页但访问项目路径报 404。原因项目没有被正确部署到 Tomcat 的 webapps 目录或者 IDE 里项目部署的上下文路径Context Path和访问 URL 不一致。MyEclipse 里常见的情况是部署名带了版本号比如PersonalFinanceSystem_1.0实际访问地址是localhost:8080/PersonalFinanceSystem_1.0/而不是localhost:8080/PersonalFinanceSystem/。解决右键项目 → Properties → Web → Web Context-Root改成PersonalFinanceSystem然后重新部署。或者直接在浏览器地址栏访问localhost:8080/PersonalFinanceSystem/试试如果通了就是路径问题。另外检查web.xml里的welcome-file-list是否包含index.jsp缺失时访问根路径会找不到默认页。5.2 JDBC 连接 MySQL 报 Communications link failure现象启动项目后第一次访问数据库相关页面控制台报Communications link failure或者Connection refused。原因三个常见原因——MySQL 服务没启动驱动 jar 包版本和 MySQL 服务端不匹配MySQL 8.0 以上没有在连接串中指定时区。原文档项目用的是 MySQL 5.x如果你本地装的是 MySQL 8.0用 5.1.x 的驱动连接 8.0 服务端虽然驱动能加载但协议对不上就报这个错。解决先确认 MySQL 服务在运行——Windows 下在服务管理器里查MySQL Service是否启动。再看 JDBC 连接串如果你用了 MySQL 8.0把DBUtil.java里的连接串改成带时区和 SSL 禁用的完整格式// DBUtil.java 连接串模板 String url jdbc:mysql://localhost:3306/finance? useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8; String username root; String password yourpassword;useSSLfalse是避免 MySQL 8.0 默认启用 SSL 导致的握手慢或警告serverTimezoneAsia/Shanghai解决时区报错characterEncodingutf8保证中文不乱码。如果你用的是 MySQL 5.7这三个参数不是必须的但加了也无害。5.3 页面中文乱码过滤器与编码不一致现象用户名、收支备注里输入中文保存后数据库里变问号或者页面上显示乱码。原因三层编码不一致——JSP 页面本身的pageEncoding、请求参数的编码解析、MySQL 表的字符集。原项目文档里表结构如果用了utf8而不是utf8mb4存 emoji 符号或生僻字会报Incorrect string value错误。解决三层分别处理。第一JSP 页面头部统一写% page languagejava contentTypetext/html; charsetUTF-8 pageEncodingUTF-8 %第二在web.xml里配置一个编码过滤器强制所有请求以 UTF-8 解析!-- web.xml 编码过滤器配置 -- filter filter-nameencodingFilter/filter-name filter-classorg.apache.catalina.filters.SetCharacterEncodingFilter/filter-class init-param param-nameencoding/param-name param-valueUTF-8/param-value /init-param /filter filter-mapping filter-nameencodingFilter/filter-name url-pattern/*/url-pattern /filter-mapping这里直接复用 Tomcat 自带的SetCharacterEncodingFilter不用自己写这是最省事的方案。第三建库时指定DEFAULT CHARACTER SET utf8mb4如果已经建了库执行ALTER TABLE t_user CONVERT TO CHARACTER SET utf8mb4;补救。5.4 Session 失效后表单重复提交现象用户在添加支出页面停留很久填完点保存提示请先登录或跳回登录页。原因Session 默认超时时间到了我的代码里设了 30 分钟用户再提交时 Servlet 里session.getAttribute(loginUser)返回 null被拦截回登录页。这不是 bug是安全机制但体验上确实突兀。解决相对合理的做法是在 JSP 页面 head 里加一个元信息让浏览器在 Session 快过期的时候主动跳到登录页而不是等用户提交才被拦。具体做法是在登录页的 JSP 里通过session.getMaxInactiveInterval()控制一个定时刷新或者在 Servlet 被拦截时跳转登录页并带上redirect1参数登录成功后再跳回原页面。毕设答辩时不用太纠结这个能解释清楚 Session 机制就够。5.5 日期格式不一致导致保存报错现象前端日期选择器选好日期后提交后台报IllegalArgumentException: At least 3 digits expected或直接 500。原因前端控件返回的日期格式是2024/12/01而Date.valueOf(dateStr)只接受yyyy-MM-dd格式不对就抛异常。或者前端传来的是带时分秒的完整时间戳Date.valueOf同样解析不了。解决在 DAO 层或 Servlet 里统一做一次日期格式规范化// 日期格式兼容处理 String dateStr request.getParameter(expenseDate); if (dateStr null || dateStr.trim().isEmpty()) { // 为空则取今天 dateStr LocalDate.now().toString(); } else if (dateStr.contains(/)) { // 兼容 2024/12/01 格式 String[] parts dateStr.split(/); dateStr parts[0] - parts[1] - parts[2]; } Date expenseDate Date.valueOf(dateStr);这个处理不复杂但很实用前端控件格式不统一时后端做兼容是兜底方案不依赖前端配合改格式。6. 部署后自测从 WAR 包到接口验证的完整流程项目跑通之后建议按下面的顺序做一轮完整自测不只验证功能还要验证边界条件。我一般会在这种 JSP 毕设项目里强制走一遍注册→登录→添加收支→查询财产→录入贷款→更新储蓄→退出登录的全链路每一步都刻意输入异常数据看系统会不会崩。先做部署层面的验证。用 IDE 导出一个可部署的 WAR 包手工放到 Tomcat 的 webapps 目录下重启确认脱离 IDE 也能跑——这一步能暴露 IDE 环境自带的一些隐性依赖# 在项目根目录执行打包Maven 项目 mvn clean package -DskipTests # 打包后把 WAR 拷贝到 Tomcat webapps 目录 cp target/PersonalFinanceSystem.war D:/apache-tomcat-7.0.108/webapps/ # 重启 Tomcat D:/apache-tomcat-7.0.108/bin/shutdown.sh D:/apache-tomcat-7.0.108/bin/startup.sh # 确认部署成功后访问 curl http://localhost:8080/PersonalFinanceSystem/index.jspWAR 包能独立跑说明项目没有依赖 IDE 的内部配置这是交付给评审老师时的基本要求。如果打成 WAR 包后启动报错大概率是WEB-INF/lib下的 jar 包不全需要把依赖的驱动包一起打进去。接着做业务链路验证。建议用表格记录每条链路的测试结果答辩时这个表能直接展示测试场景输入数据预期结果实测结果用户注册用户名重复提示用户名已存在通过用户登录错误密码提示用户名或密码错误通过添加收入金额填 0业务上允许但统计会失真需要业务层拦截添加支出金额填负数应被拦截正则校验已拦截财产查询无任何记录显示 0 而不是空白通过COALESCE 生效还贷倒计时到期日设为昨天显示逾期状态通过储蓄更新存入金额超过目标提示已达成目标通过自测时最容易漏的是无数据和超长数据这两个极端空记录的用户财产查询页能不能正常渲染、备注字段填 2000 字会不会撑破页面布局。这两个场景不测答辩演示现场很容易翻车。最后做代码层面的走查重点看三个位置DBUtil.java的连接释放是否在finally块里——漏掉conn.close()在高并发测试时会耗尽数据库连接web.xml里 Servlet 的load-on-startup是否配置——没配的话首次访问才加载 Servlet第一次请求会特别慢答辩时体验不好页面上的所有超链接和表单提交路径是否和web.xml里的servlet-mapping完全一致——这个不一致会出现页面找了但 Servlet 不存在的 404而且考试环境里不容易排查。这套流程走完这个项目的稳定性基本就有保障了。作为一个 JSP 老项目它的代码风格和现在的 Spring Boot 生态差别很大但胜在结构简单、链路短、每一行代码都能看懂比那些引入框架但讲不清楚原理的空壳项目扎实得多。从那以后我每拆一个毕设项目都会强制走一遍脱 IDE 部署和异常输入自测凡是这两关过不去的要么是依赖没打全要么是校验逻辑有洞提前暴露总比答辩当场翻车好。希望帮到你。本文还有配套的精品资源点击获取
返回列表