ARTICLE DETAIL

资讯详情

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

JSP+Servlet+JDBC实战:数字电视用户管理系统课程设计源码解析

JSP+Servlet+JDBC实战:数字电视用户管理系统课程设计源码解析 简介这是一份基于JSP技术开发的数字电视用户管理系统设计源码面向具备JavaWeb基础、希望学习完整业务实现过程的开发者与高校学生。项目覆盖频道管理、节目管理、用户订购与服务等数字电视运营场景基于DAO、Service与JSP页面的分层结构清楚展示用户管理类项目的典型架构。压缩包共371个文件大小24.29MB主要包括74个Java源文件、61个JSP页面、15个CSS样式表、7个JavaScript脚本、11个XML配置和11个JAR库其中Java源码与class文件处理业务逻辑JSP完成动态界面渲染CSS与JavaScript负责前端交互XML与JAR用于配置和依赖管理。目前已有302人学习下载适合课程设计、毕业设计或项目实战训练通过源码可掌握数字电视用户管理模块的数据表关系、页面与后台联动方式及关键业务流程。目录结构完整导入IDE即可运行参考便于二次开发与扩展。1. 课程设计截止前两周这套 JSP 用户管理系统救了不少人老师丢来一句「做个数字电视用户管理系统」你对着空项目发呆——这是很多 Java Web 初学者都撞过的墙。基于 Java Server Pages 的数字电视用户管理系统设计源码指的正是一套用 JSP Servlet JDBC 搭起来的完整 Web 工程管理员登录、用户档案、套餐订购、缴费记账、机顶盒设备登记业务闭环都在。它解决的是「不靠 Spring Boot 全家桶怎么把管理系统跑起来并讲清楚」的问题。适合正在做课程设计、需要源码参考的在校生以及想用最小依赖交付内部管理系统的开发者。这套技术栈看着老恰好因为老每一次请求、转发、查库都能被看见反而最容易讲明白。2. 业务场景与选型数字电视用户的生命周期为什么适合用 JSP2.1 先盘业务这套系统到底要管哪几件事数字电视用户管理系统直白说就是广电网络运营商或 IPTV 业务方内部用的 CRM。但和一般电商后台比它多了两个特点一是用户和物理设备机顶盒强绑定二是计费周期是按月套餐而非单次下单。这两个特点决定了表结构和页面流转的设计方向。运营视角下的用户生命周期大概是这样的用户带身份证来营业厅开户营业员在系统里建档案顺便选一个基础套餐机顶盒要登记 MAC 地址或 CA 卡号绑定到用户名下之后每个月产生套餐费用用户来缴费或者续订遇到故障要报修报修记录挂在用户档案下长期欠费会进入停机状态复机时需要重新开通。这套流程如果做成一个 JSP 工程映射出来就是六张核心表和七八个主要 JSP 页面。这种管理系统最核心的诉求不是性能而是「操作员看得懂、权限分得清」。「看得懂」是指缴费、退订、停机这些动作页面来回跳转时数据要保持一致「权限分得清」是指普通营业员和系统管理员能做的事要分开。JSP Servlet 的经典 MVC 划分把控制逻辑写在 Servlet 里把展示写在 JSP 里业务边界非常直白带实习生或者做课程设计答辩都容易交代。如果你在网上检索数字电视用户管理系统源码大概率碰到两种实现一种是 Spring Boot MyBatis Vue 前后端分离另一种是 JSP Servlet JDBC。前者更接近工业界现状但我不建议课设阶段直接上手。Spring Boot 项目里后端接口、前端页面、数据库迁移脚本三个部分夹在一起配置一不小心就起不来你根本分不清报错来自哪一层。JSP 工程的结构则是一目了然WebContent 下全是 .jspsrc 下全是 .java。2.2 为什么选 JSP Servlet不是落后是「看得见」我见过不少同学拿到 Spring Boot 项目反而不知道从哪里开始改pom.xml 拉了一堆依赖启动时端口被占、版本冲突、数据库连接串配错光是环境问题就能耗掉一半时间。而 JSP Servlet 的 Java Web 经典结构里依赖就一个 MySQL 驱动 jar配置就一个 web.xml逻辑就三层Servlet 接收请求、DAO 操作数据库、JSP 渲染页面。对「要求讲清楚每一步」的评审场景这是非常大的优点。选它也要接受代价没有依赖注入DAO 层得自己 new没有 ORMResultSet 要手动封装成对象事务要自己管理 Connection。这些「麻烦」恰恰是这套系统最有价值的地方——你在代码里亲手处理了这些问题答辩时老师问起来你能讲出别人 Spring Boot 项目里讲不出的底层细节。很多课程设计案例源码看起来功能多但一问到 JDBC 事务、连接关闭、SQL 注入回答就开始含糊因为框架把这些都藏掉了。另外一个实际考量是部署环境。JSP 老项目的运行组合非常固定JDK 8 Tomcat 8.5 MySQL 5.7三个组件版本对上基本不会出幺蛾子。学校里机房、老师演示机、自己笔记本这套组合几乎通吃。你拿去给老师演示的时候Tomcat 一启动就能跑比在 IDEA 里等 Spring Boot 初始化半天的体验好得多。2.3 功能清单与页面流转先画出一张能用的地图动手写代码之前先把功能边界划清楚。下面这张表是我做这类系统的标准拆分你要做课程设计或者内部小系统可以直接照搬功能模块主要页面关键动作对应数据表登录认证login.jsp、index.jsp登录、退出、会话超时t_admin用户档案user_list.jsp、user_add.jsp、user_edit.jsp开户、编辑资料、停复机t_user套餐管理package_list.jsp、package_edit.jsp上架新套餐、调整价格t_package订购关系order_list.jsp、order_add.jsp订购、退订、续费t_order缴费记账pay_list.jsp、pay_add.jsp缴费、退款、打印凭证t_payment设备管理device_list.jsp、device_bind.jsp机顶盒绑定、换机t_device页面流转上登录成功进 index.jsp左侧是功能菜单右侧用 iframe 加载各个列表页列表页的「操作」列跳转到编辑页表单提交回 ServletServlet 处理完重定向回列表页。这套跳转模式是 JSP 项目的标准做法后面第 4 章的分页代码就基于它。需要注意的一点是这里面的列表页都带分页和关键字搜索因为用户表、缴费表的数据量会随着演示数据累积没有分页的列表页在评委面前翻车率很高。3. 数据表设计六张表把用户生命周期串成闭环3.1 从业务流程反推表结构做这种管理系统最重要的经验是不要一上来就写 CREATE TABLE而是把第 2 章的业务流程在纸上走一遍标出每个步骤要记录和修改哪些数据。按这个思路六个实体就够了。管理员表 t_admin存登录系统的员工信息。字段包括 admin_id、username、passwordMD5 加盐、real_name、role、create_time。role 字段区分普通营业员和超级管理员超级管理员能看缴费汇总报表普通营业员只能操作日常业务。用户表 t_user核心档案表。user_id 自增主键user_no 是业务编号类似广电的 808 开头客户号这个编号在业务上要唯一。user_name 存姓名id_card 存身份证号phone 存手机号address 存安装地址status 表示状态1 正常、0 停机open_time 记录开户时间。套餐表 t_packagepackage_id、package_name、price、duration_months、channel_desc、is_active。duration_months 表示这个套餐是包月还是包年缴费计算周期依赖它。订购表 t_order这是用户和套餐之间的中间表。order_id、user_id、package_id、start_date、end_date、status。一个用户可能同时订两个套餐基础包 影视包一个套餐也会被多个用户订购所以订购关系必须单独成表。缴费表 t_payment每笔缴费一条记录。pay_id、user_id、order_id、amount、pay_time、pay_type现金/转账/预存、operator_id。operator_id 回填操作员是为了出问题能追溯。设备表 t_device绑定机顶盒。device_id、user_id、device_type机顶盒/CA 卡、mac_no、bind_time、status。这里 mac_no 要加唯一索引一个机顶盒不能同时绑两个用户。每个字段的取舍后面都会影响代码。比如 id_card 定成 VARCHAR(18)是因为身份证末位可能带 X用数值类型就存不进去。这种细节在答辩时很容易被提问。3.2 建表 SQL核心表脚本与三处关键设置CREATE TABLE t_user ( user_id INT AUTO_INCREMENT PRIMARY KEY COMMENT 用户ID, user_no VARCHAR(20) NOT NULL UNIQUE COMMENT 业务客户号, user_name VARCHAR(50) NOT NULL COMMENT 姓名, id_card VARCHAR(18) DEFAULT NULL COMMENT 身份证号, phone VARCHAR(11) DEFAULT NULL COMMENT 手机号, address VARCHAR(100) DEFAULT NULL COMMENT 安装地址, status TINYINT DEFAULT 1 COMMENT 1正常 0停机, open_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 开户时间, KEY idx_phone (phone) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT数字电视用户表;CREATE TABLE t_order ( order_id INT AUTO_INCREMENT PRIMARY KEY, user_id INT NOT NULL, package_id INT NOT NULL, start_date DATETIME DEFAULT CURRENT_TIMESTAMP, end_date DATETIME DEFAULT NULL, status TINYINT DEFAULT 1 COMMENT 1生效 0退订, KEY idx_user (user_id), KEY idx_package (package_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT订购关系表;第一段脚本里user_no 设置了 UNIQUE这是业务上的硬约束代码层也要在新增用户时先查重双保险。phone 建普通索引而不是唯一索引因为家庭里多个用户可能共用同一个电话号码。status 用 TINYINT 而不是 INT存储上省空间而且业务状态只有两三种时语义更清楚。CHARSET 必须写 utf8mb4别省那一个字符写成 utf8——MySQL 的 utf8 只支持 3 字节字符集遇到地址栏里的特殊符号会直接报错。第二段脚本中t_order 的两个 KEY 不是外键约束是普通索引。这是刻意的索引能加速按用户查订购记录的 JOIN 查询同时避免物理外键在删除数据时带来的顺序限制。后面 3.3 节专门说这个问题。3.3 三个翻车点外键、金额字段和事务边界先说外键。我在课设评审里见过太多学生被物理外键坑哭想在测试环境下删掉一个用户重新录结果因为 t_order 里有它的订购记录DELETE 直接被外键约束拦死。解决倒是简单但每次操作都被打断很影响演示节奏。我的做法是全部用逻辑外键——表结构里不声明 FOREIGN KEY只在关联字段上建普通索引数据一致性由应用层代码保证。代价是少了数据库层的校验但对于这种管理系统业务层用事务控制好完全够用。再说金额字段。t_payment.amount 必须用 DECIMAL(10,2)绝对不要用 FLOAT 或 DOUBLE。FLOAT 是浮点近似值1.1 2.2 的结果可能是 3.3000000000000003做月度对账时一分钱的差异都说不清。DECIMAL 是精确小数类型底层按字符串存储加减乘除不会产生精度损失。这个坑不做财务功能体会不到等对不上账的时候再回头改表测试数据全得重灌血泪经验。最后说事务边界。用户缴费这个动作涉及两步写操作往 t_payment 插入一条缴费记录同时把 t_user 的 status 从停机恢复成正常。这两个操作必须在一个数据库事务里完成——插了记录但状态没更新用户下次开机还是被拦截状态更新了但缴费记录没插上月底对账少一笔钱。用 JDBC 的 Connection 手动控制事务第 4 章 4.4 节给出完整代码。这里提前标记做表的时候把两张表的写操作规划好。4. 核心链路代码登录拦截、分页查询、缴费事务怎么落4.1 工程目录先看清 WebContent 与 src 的分工拿到源码先别急着跑花十分钟看目录结构。标准的 JSP 工程分两大块src 下放 Java 代码WebContent 下放页面和静态资源。下面这个结构是我整理这类项目最常见的分包方式src/ cn/example/ controller/ # Servlet接收请求、调业务、转发 dao/ # JDBC 数据访问一个表对应一个 DAO entity/ # JavaBean对应数据库表字段 filter/ # 过滤器登录拦截、编码处理 util/ # DBUtil 连接工具、MD5 工具类 WebContent/ login.jsp index.jsp # 主框架页左侧菜单 右侧 iframe user/ # 用户模块页面 order/ # 订购模块页面 css/ js/ # 静态资源 WEB-INF/ web.xml lib/ # mysql-connector-java-5.1.49.jar分包原则是「一个表一个实体类、一个 DAO、对应的 JSP 页面放同名目录」。你改用户模块只需要动 entity/User.java、dao/UserDAO.java、controller/UserServlet.java 和 user/ 目录下的页面其他模块不受影响。controller 里的 Servlet 统一叫 XxxServlet不要搞成模糊的 DoServlet否则后期自己都找不到。4.2 登录过滤器三行代码守住整个系统登录校验这种「每个请求都要做」的逻辑放在 Servlet 里逐份复制是灾难。用 Filter 拦截/*一次写完全站生效。package cn.example.filter; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.IOException; public class LoginFilter implements Filter { Override public void init(FilterConfig filterConfig) { } Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request (HttpServletRequest) req; HttpServletResponse response (HttpServletResponse) res; // 登录页和登录接口直接放行避免死循环 String uri request.getRequestURI(); if (uri.endsWith(/login.jsp) || uri.endsWith(/LoginServlet) || uri.contains(/css/) || uri.contains(/js/)) { chain.doFilter(req, res); return; } HttpSession session request.getSession(false); if (session null || session.getAttribute(admin) null) { response.sendRedirect(request.getContextPath() /login.jsp?timeout1); return; } chain.doFilter(req, res); } Override public void destroy() { } }在 WEB-INF/web.xml 里注册filter filter-nameloginFilter/filter-name filter-classcn.example.filter.LoginFilter/filter-class /filter filter-mapping filter-nameloginFilter/filter-name url-pattern/*/url-pattern /filter-mapping逻辑说明uri 判断放在最前面把登录页、登录接口、CSS 和 JS 放行避免未登录用户访问登录页时被重定向到登录页造成循环跳转。request.getSession(false)传 false 很关键——它不会主动创建新的 Session只有已登录用户才能拿到有效 Session未登录用户直接走重定向分支。参数说明request.getContextPath()是当前 Web 应用根路径重定向时必须拼上它否则在部署到非 ROOT 路径时登录跳转会 404。url-pattern 写/*而不是/因为/*匹配所有路径/只匹配默认 servlet。这两个字符的区别值得你亲手试一次就记住。4.3 分页查询PreparedStatement 是这条链路的底线用户列表页必须分页。核心思路是 Servlet 接收 pageNum 和 keyword调 DAO 查当前页数据和总条数算总页数再把数据放进 request转发给 JSP 渲染。// controller/UserServlet.java —— 分页查询入口 int pageSize 10; int pageNum 1; String p request.getParameter(pageNum); if (p ! null !p.trim().isEmpty()) { try { pageNum Integer.parseInt(p); if (pageNum 1) pageNum 1; } catch (NumberFormatException e) { pageNum 1; // 参数非法时兜底回第一页 } } String keyword request.getParameter(keyword); if (keyword null) keyword ; UserDAO dao new UserDAO(); ListUser userList dao.findByPage(pageNum, pageSize, keyword); int total dao.count(keyword); int totalPages (int) Math.ceil(total * 1.0 / pageSize); request.setAttribute(userList, userList); request.setAttribute(totalPages, totalPages); request.setAttribute(pageNum, pageNum); request.setAttribute(keyword, keyword); request.getRequestDispatcher(/user/user_list.jsp).forward(request, response);// dao/UserDAO.java —— 分页 SQL 与参数绑定 public ListUser findByPage(int pageNum, int pageSize, String keyword) throws SQLException { String sql SELECT * FROM t_user WHERE user_name LIKE ? OR phone LIKE ? ORDER BY user_id DESC LIMIT ?, ?; ListUser users new ArrayList(); try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, % keyword %); ps.setString(2, % keyword %); ps.setInt(3, (pageNum - 1) * pageSize); ps.setInt(4, pageSize); try (ResultSet rs ps.executeQuery()) { while (rs.next()) { User u new User(); u.setUserId(rs.getInt(user_id)); u.setUserName(rs.getString(user_name)); u.setPhone(rs.getString(phone)); u.setStatus(rs.getInt(status)); users.add(u); } } } return users; }逻辑说明SQL 里的 LIKE 通配符%是拼在参数上的不是拼在 SQL 字符串里的。PreparedStatement 的好处是参数和 SQL 模板分离即使 keyword 里带了单引号或者%字符也只会被当作普通文本处理不会改变 SQL 结构。这是防 SQL 注入最基础也最有效的手段。参数说明LIMIT 第一个参数是偏移量(pageNum - 1) * pageSize第二页就是从第 10 条开始取。如果 pageNum 被传入 0 或负数偏移量变成负数MySQL 直接报错所以 Servlet 层做了两次兜底——解析失败回第 1 页、小于 1 强制回第 1 页。搜索场景下 keyword 必须原样回传到 JSP 的分页链接里否则翻页后搜索条件丢失点第 2 页就变成全量查询。这个细节在演示时非常容易被评委点出来。4.4 缴费事务手动 commit 与 rollback不能省用户来缴费系统要做两件事在 t_payment 插入缴费流水把 t_user 的 status 更新为正常。这两条 SQL 任一条失败另一条也必须回滚否则账实不符。JDBC 的默认行为是每条 SQL 自动提交所以必须显式关掉自动提交用手动事务包住两条更新。// dao/PaymentDAO.java —— 缴费 恢复用户状态 public void pay(Payment payment) throws SQLException { Connection conn null; PreparedStatement psInsert null; PreparedStatement psUpdate null; try { conn DBUtil.getConnection(); conn.setAutoCommit(false); // 关掉自动提交开启事务 String sqlInsert INSERT INTO t_payment (user_id, order_id, amount, pay_time, pay_type, operator_id) VALUES (?, ?, ?, NOW(), ?, ?); psInsert conn.prepareStatement(sqlInsert); psInsert.setInt(1, payment.getUserId()); psInsert.setInt(2, payment.getOrderId()); psInsert.setBigDecimal(3, payment.getAmount()); psInsert.setString(4, payment.getPayType()); psInsert.setInt(5, payment.getOperatorId()); psInsert.executeUpdate(); String sqlUpdate UPDATE t_user SET status 1 WHERE user_id ?; psUpdate conn.prepareStatement(sqlUpdate); psUpdate.setInt(1, payment.getUserId()); psUpdate.executeUpdate(); conn.commit(); // 两条都成功才提交 } catch (SQLException e) { if (conn ! null) { conn.rollback(); // 任一条失败整体回滚 } throw e; } finally { if (psInsert ! null) psInsert.close(); if (psUpdate ! null) psUpdate.close(); if (conn ! null) { conn.setAutoCommit(true); // 还给连接池前恢复默认状态 conn.close(); } } }逻辑说明事务的前提是两条 SQL 必须使用同一个 Connection。如果你让 PaymentDAO 和 UserDAO 各开一个连接各自的 update 自己提交那就根本谈不上事务。常见的做法是在 Util 包维护一个 DBUtil用 ThreadLocal 存放当前线程的 Connection业务层保证一次请求内拿到的是同一个连接。你也可以简化处理把这个事务方法放在一个 Service 类里Controller 只调 Service 的一个方法DAO 之间不要互相调用。参数说明setBigDecimal 对应数据库里的 DECIMAL 类型避免金额在 JDBC 转换过程中丢精度。pay_time 用 MySQL 的 NOW() 在 SQL 里生成不依赖 Java 侧的时间格式避免服务器时区差异。finally 里把 autoCommit 恢复成 true 是为了连接被回收重用时不会带着事务残留状态影响下一次操作。这套写法看着啰嗦但它把资源清理、异常回滚、状态恢复三个风险点都管住了答辩问到「失败怎么办」你不会没话说。5. 部署避坑JDK 8 Tomcat 8.5 环境下绕不开的 5 个问题这一章是从真实部署里捡出来的教训。JSP 老项目的坑不在业务代码全在环境和兼容性。你按下面顺序排查能省下大半天折腾时间。5.1 Tomcat 10 启动直接 500从 javax 到 jakarta 的迁移断层现象把源码打进 Tomcat 10启动后所有页面 500控制台报ClassNotFoundException: javax.servlet.Filter。原因我对接过的版本里Tomcat 10 把 Java EE 的javax.servlet迁移到了jakarta.servlet老项目的 import、web.xml 配置全部失效。这不是你代码写错是容器版本不兼容。解决装 Tomcat 8.5.100 或者 9.0.x配合 JDK 8 使用这是老 JSP 工程兼容性最稳的组合。如果你坚持用 Tomcat 10需要全局替换代码里所有javax.*为jakarta.*工作量不大但对课设没必要还会引入新风险。5.2 数据库连接失败驱动类名、jar 路径和连接串三处对齐现象启动后访问列表页报ClassNotFoundException: com.mysql.jdbc.Driver或者直接报Public Key Retrieval is not allowed。原因驱动 jar 没放在 WEB-INF/lib 下Tomcat 运行时加载不到或者 MySQL 版本和驱动版本不匹配。MySQL 8.x 的驱动类名是com.mysql.cj.jdbc.Driver老代码里写的是com.mysql.jdbc.Driver一对不上就抛类找不到。解决统一用 MySQL 5.7 mysql-connector-java-5.1.49.jar连接串按下面这个标准三段式写String url jdbc:mysql://localhost:3306/dtv_db ?characterEncodingUTF-8 useSSLfalse serverTimezoneAsia/Shanghai;逻辑说明characterEncoding 保证数据库读写都用 UTF-8useSSLfalse 关掉本地开发用不上的 SSL 握手serverTimezone 显式指定时区避免 MySQL 8 连接时报时区错误。三个参数少一个都是一个坑。jar 包务必拷到 WEB-INF/lib 下不是 build path 里能看到就算数运行时 Tomcat 只认这个目录。5.3 中文乱码四个地方编码不统一系统就全是问号现象登录页正常但新增用户后中文姓名在列表页显示成???或者在编辑页显示成乱码。原因JSP 页面编译编码、Servlet 读取请求参数的编码、Tomcat 默认 URI 编码、数据库表的字符集这四个地方只要有一个是默认的 ISO-8859-1中文就会在某一环丢字。解决四处统一成 UTF-8。JSP 页面顶部写% page contentTypetext/html;charsetUTF-8 languagejava %Servlet 接收 POST 请求时先调request.setCharacterEncoding(UTF-8)web.xml 里注册一个编码过滤器强制所有请求统一数据库建表时用第 3 章说过的DEFAULT CHARSETutf8mb4。新项目一开始就全用 UTF-8别给各种编辑器默认编码留发挥空间。5.4 分页翻到第 2 页报错NumberFormatException 与搜索条件丢失现象列表页第一页正常点击「下一页」直接 500或者搜索后再翻页结果疑似是全量数据的分页。原因分页链接里只带了pageNum没带keyword搜索后翻页又变成全表查询或者 URL 里的 pageNum 参数被拼成了非法字符串。我用过的老旧示例代码里有人直接用 JSP 表达式拼参数遇到字符时参数被截断。解决分页链接里同时回传keyword用 JSTL 的fn:escapeXml对参数做转义。Servlet 解析 pageNum 必须 try-catch解析失败默认回第 1 页。分页组件的代码里还要对totalPages做边界控制当前页等于最后一页时禁用「下一页」按钮从根源上避免越界请求。5.5 静态资源样式 404过滤器把 CSS 和 JS 也拦了现象页面功能正常但排版是纯 HTML 裸样式控制台一堆 CSS 404。原因登录过滤器拦截/*CSS 请求进来时 Session 里没有用户信息直接被重定向到 login.jsp。浏览器拿到的是 HTML 而不是 CSS 文件于是渲染乱套。解决回到 4.2 节的过滤器代码放行条件里加上uri.contains(/css/)和uri.contains(/js/)。更稳的做法是放行固定前缀uri.startsWith(request.getContextPath() /css)这类判断。这个问题太典型了几乎每个我上手过的 JSP 项目第一次启动都会碰到建议部署后先按 F12 看 Console 和 Network 面板静态资源 404 一眼就能发现。6. 让源码变成你自己的作品三个保底技巧6.1 用 Git 留痕给每版改动留一颗后悔药拿到源码第一件事在项目根目录执行git init并做首次提交。之后的每次改动哪怕只是改了一个字段名都按逻辑分批 commit。这个习惯有两个实际好处改坏了可以随时回退到上一个可用版本答辩时老师问「你做了哪些工作」git log列出来的提交记录就是最扎实的证据。比空口说「我改了很多」有说服力得多。6.2 写一份能跑的 README部署文档比代码更贵我习惯把源码和笔记放在一起README 里固定写四块环境版本清单JDK 8、Tomcat 8.5、MySQL 5.7数据库初始化步骤先执行建表 SQL再执行测试数据 SQL启动步骤导入 IDEA → 配置 Tomcat → 改数据库连接串 → 启动测试账号管理员用户名和密码。这份文档既是给接手的人看的也是给两周后的自己看的——那时你大概率记不清当初怎么把环境跑起来的。6.3 画一张用户状态流转图让评审半分钟看懂业务在答辩 PPT 里放一张图开户 → 正常 → 欠费 → 停机 → 复机标清楚每个状态转换由哪个页面、哪个操作触发。这张图比十页文字描述更高效因为它直接回答了「系统干什么」这个核心问题。状态字段在第 3 章就是 t_user.status代码里所有对 status 的判断都围着这张图转业务边界清楚了代码自然有底气。这套 JSP 项目做完你得到的不只是一份能交差的数字电视用户管理系统课程设计案例源码而是一次把 Servlet 生命周期、JDBC 事务、过滤器链、PreparedStatement 全部亲手过一遍的机会。我当年做完类似项目后再看 Spring Boot 里的Transactional和拦截器脑子里立刻能对应到 JDBC 里那些手写代码这种底层映射很多年后都在帮我。希望这里的每一段代码、每一个坑位都能让你少走点弯路也希望帮到你。本文还有配套的精品资源点击获取
返回列表