ARTICLE DETAIL

资讯详情

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

Java Web课设实战:个人信息维护系统从部署到增删改查与排坑指南

Java Web课设实战:个人信息维护系统从部署到增删改查与排坑指南 简介基于Java的个人信息维护系统课程设计项目包专为Java初学者、高校学生及需要完成Web课程设计或毕业设计的开发者打造围绕用户登录、个人信息展示、信息修改、登录历史管理等功能展示了一套完整的Web开发流程。压缩包内含78个文件以Java源码和XML配置文件为主辅以CSS/JS前端页面、2份SQL数据库脚本、Maven工程配置文件及课程设计报告DOCX整体仅1.24MB目录层级清晰便于查阅与复用。代码实现涉及JDBC数据库连接、密码MD5/SHA加密、MVC分层设计、表单提交与数据验证等关键技术并提供了用户信息表和登录日志表的建表语句有助于理解数据库持久化与前后端交互逻辑。登录历史模块进一步展示了登录时间、IP地址的记录及前端表格动态加载方式对学习请求处理和jQuery交互有直接参考价值项目基于Maven与IntelliJ IDEA组织可导入开发环境阅读源码并支持二次扩展。目前已有100人浏览学习适合作为Java Web课程设计、毕业设计或入门实战的参考资源。1. 个人信息维护系统从 zip 压缩包到一个能跑的 Java Web 项目“个人信息维护系统”这个名字配上 .zip 后缀懂行的人一眼就能看出这是大学里最常见的 Java Web 课设档。它本质上是一个跑在浏览器里的个人信息管理页面对一张用户表做增删改查录入姓名、电话、邮箱、地址能编辑、能删掉、能按条件翻出来。别小看这个项目它把 JDBC、Servlet、JSP、MySQL 这些 Java 后端的基本功全串起来了是很多人从语法走进 Java 工程师的第一道门槛。对刚学完 JavaSE 的人来说它是最好的练手对象对准备 java 面试的人来说它就是八股文里的 CRUD 原型对正在选课设题目的学生来说它足够简单也足够完整。我不打算写成一堂教科书就按带人时常用的做法把从解压 zip 到跑通页面的每一步、每个参数、每条血泪经验讲透。2. 技术选型与项目结构Servlet 还是 Spring Boot先算清这四笔账2.1 为什么经典课设多选 ServletJSP学习成本与面试题的双重考量拿到一个名为“个人信息维护系统”的 Java 项目压缩包第一步不是急着解压而是先弄清楚它可能是什么技术栈写的。以我见过的同类项目为例九成以上是 Servlet JSP MySQL少部分是 Spring Boot Thymeleaf也有极少数用 Swing 写桌面版。为什么经典课设都爱用 Servlet JSP 给学生打底因为 Java Web 的整个请求链路——浏览器发 HTTP 请求、Tomcat 把请求交给 Servlet、Servlet 调 Dao 查库、再把结果放进 request、转发给 JSP 渲染页面——每一步都是可看见的。这个链路一旦跑通后面学 Spring MVC、Spring Boot 时你才知道它在帮你省什么。从 java 面试的角度看Servlet 生命周期、请求转发与重定向的区别、Session 与 Cookie 的区别都是 java 开发工程师面试题里的常客。把课设代码吃透再去背 java 基础八股文记忆是挂在具体代码上的而不是悬空的。如果你的目标是做 java 后端工程师这个项目不是拿来交差的是拿来当面试素材的。所以我的建议是如果这个 zip 是给你练手的先别急着嫌弃它老如果是你准备交的课设Servlet JSP 恰恰是最不容易被追问到哑口无言的选择。对比项Servlet JSPSpring Boot学习曲线请求到响应全链路可见框架自动装配屏蔽细节需要理解的概念Tomcat、Servlet、Filter内置 Tomcat、依赖注入、自动配置面试提问率高Servlet 生命周期必问高自动配置原理必问适合规模课设、小型管理页面工程化项目、简历项目这张表不是让你二选一而是提醒你课设阶段把 Servlet 版跑通的价值在于治好了你对黑匣子的恐惧。Spring Boot 的自动配置太方便你加上 SpringBootApplicationTomcat 启动了、数据源注入了看起来一切都好但请求是怎么进 Controller 的、参数是怎么从 HTTP 变成对象的全都隐藏在框架背后。面试官问“DispatcherServlet 怎么工作”时答不上来的人大概率就是没经历过手动配 Servlet 的阶段。还有一点拿到压缩包后先看文件列表能省不少事里面有 pom.xml 就是 Maven 工程有 WebContent 或 src/main/webapp 就是传统 Web 工程有 application.yml 或 application.properties 则可能是 Spring Boot。用压缩软件预览列表比解压整个包再试错要快得多这一步做对了后面导入工程时心里就有底。2.2 项目目录怎么分model、dao、servlet、jsp 各管什么解压之后先确认是不是 Maven 结构再找 web.xml 或 WebServlet 注解判断是不是 Servlet 项目最后找数据库配置文件。一个结构清楚的项目包名通常长这样src/main/java ├── com.example.personinfo │ ├── model # 实体类对应数据库表 │ │ └── User.java │ ├── dao # 数据访问层封装 JDBC │ │ └── UserDao.java │ ├── servlet # 控制器层接收请求、调用业务 │ │ ├── UserListServlet.java │ │ ├── UserAddServlet.java │ │ ├── UserUpdateServlet.java │ │ └── UserDeleteServlet.java │ └── util # 工具类 │ └── DBUtil.java src/main/webapp ├── index.jsp # 入口页面 ├── user_list.jsp ├── user_add.jsp └── user_edit.jsp这段结构说明model 里的 User 实体类字段和数据库表一一对应dao 里只写 SQL 相关的操作servlet 只负责接收参数、转发请求jsp 专心做展示。我刚学这个项目时最常犯的错就是把 SQL 直接糊在 Servlet 里图省事结果后面加一个字段要改三个文件。按这个分层走后期维护才有后悔药吃。面向对象编程在 Web 项目里的落地其实就是“各层各司其职”这六个字而不是把代码写得多么花哨。很多同学会把 Servlet 当成页面来写直接在 doGet 里 out.println( …)。这种写法不是不能跑但页面代码和 Java 代码混在一起后期改样式要重新编译。常见做法是 Servlet 只做转发渲染交给 JSP。如果项目里既有 out.println 又有 JSP也不要慌先把它跑通理解“这条数据是怎么流到页面上的”再慢慢把输出改成 JSP 表达式或 JSTL。找数据库配置时注意文件名可能叫 db.properties、jdbc.properties也可能硬编码在 DBUtil 里。教你一个判断技巧全局搜 3306 这个字符串所有出现的位置都是需要对齐配置的地方不要只改一个文件就以为完事了。2.3 运行环境怎么配JDK、Tomcat、MySQL 的版本相容性先说结论常见做法是 JDK 8 Tomcat 8.5 或 9 MySQL 5.7这套组合在课程设计里兼容性最好。JDK 8 是 java 入门教程和大部分学校环境还在用的版本Tomcat 8.5 支持 Servlet 3.1MySQL 5.7 的 JDBC 驱动 URL 写法是 jdbc:mysql://localhost:3306/personinfo?useSSLfalsecharacterEncodingutf8。如果换到 MySQL 8驱动类要从 com.mysql.jdbc.Driver 换成 com.mysql.cj.jdbc.DriverURL 还要加 serverTimezoneAsia/Shanghai否则启动时直接报时区异常。这个差异是 java 项目里最容易翻车的版本坑。Tomcat 版本和 JDK 的匹配也容易踩Tomcat 8.5 需要 JDK 7 以上Tomcat 9 需要 JDK 8 以上Tomcat 10 开始把包名从 javax.servlet 改成 jakarta.servlet如果你的代码里 import 的是 javax.servlet.*就不要用 Tomcat 10否则编译都过不了。这个匹配关系在部署时最容易被忽略。再补一个环境变量的细节跑项目前先在命令行里敲 java -version 和 javac -version如果 javac 提示找不到命令说明只装了 JRE 或者 JAVA_HOME 没配好这一环属于 java 环境变量配置的经典问题不建议跳过IDEA 里即使选了内置 JDK后续命令行构建还是会踩坑。如果你本机装 MySQL 老出问题另一个可行方案是用 Docker 跑一个 MySQL 5.7docker run --name mysql57 -p 3306:3306 -e MYSQL_ROOT_PASSWORD123456 -d mysql:5.7。这样本机不必安装 MySQL 服务连接串和代码保持一致即可。这个方案在本地验证很方便但交作业或部署到服务器时还是建议落到真实 MySQL 上免得环境差异引入额外问题。3. 从建表到增删改查个人信息维护系统的核心代码与参数说明3.1 数据库设计个人信息表该有哪些字段类型怎么定个人信息维护系统核心就一张表。字段太多显得散太少又撑不起“维护”这个词我一般按下面这样建CREATE DATABASE IF NOT EXISTS personinfo DEFAULT CHARACTER SET utf8mb4; USE personinfo; CREATE TABLE user_info ( id INT AUTO_INCREMENT PRIMARY KEY COMMENT 主键, username VARCHAR(50) NOT NULL UNIQUE COMMENT 用户名, password VARCHAR(100) NOT NULL COMMENT 登录密码, name VARCHAR(50) NOT NULL COMMENT 姓名, gender TINYINT DEFAULT 1 COMMENT 1男 2女, phone VARCHAR(20) DEFAULT NULL COMMENT 手机号, email VARCHAR(100) DEFAULT NULL COMMENT 邮箱, address VARCHAR(200) DEFAULT NULL COMMENT 住址, create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 更新时间 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT个人信息表;参数说明id 用 int 自增主键这个量级没必要用 bigintusername 加唯一约束避免重复账号gender 用 tinyint 而不是 varchar省空间且方便前端做“1 男 2 女”的映射phone 和 email 用 varchar 而不是数字类型手机号可能以 0 开头email 带 符号类型选错是新手常犯的错这里也对应 java 数据类型和 SQL 类型之间的映射习惯。create_time 和 update_time 让数据库自动维护代码里不需要手动设值时间字段由数据库统一生成可以避免多个应用服务器时间不一致带来的数据一致性问题。很多同学在这里犯的另一个错是忘记指定 DEFAULT CHARSET尤其 MySQL 5.7 默认 latin1建完表中文全乱。所以我把建库语句的 charset 也一并写上utf8mb4 兼容 emoji 和生僻字比 utf8 更省心。如果你要做的项目带登录功能可以再拆一张 account 表但课设阶段一张 user_info 表足够第六章我会讲怎么扩展。3.2 连接封装JDBC 工具类的写法与配置参数整个系统最容易被复制粘贴出问题的就是连接数据库这段。我习惯把它单独抽到 util 包里package com.example.personinfo.util; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class DBUtil { private static final String DRIVER com.mysql.jdbc.Driver; private static final String URL jdbc:mysql://localhost:3306/personinfo ?useSSLfalsecharacterEncodingutf8; private static final String USER root; private static final String PASSWORD 123456; static { try { Class.forName(DRIVER); } catch (ClassNotFoundException e) { throw new ExceptionInInitializerError(MySQL 驱动加载失败); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } public static void close(Connection conn, java.sql.Statement stmt, java.sql.ResultSet rs) { if (rs ! null) { try { rs.close(); } catch (SQLException e) {} } if (stmt ! null) { try { stmt.close(); } catch (SQLException e) {} } if (conn ! null) { try { conn.close(); } catch (SQLException e) {} } } }逻辑说明静态代码块里加载驱动保证类第一次被访问时驱动只注册一次getConnection 方法每调一次就开一个物理连接在课设规模下够用但如果是生产环境我一般会换成 Druid 连接池把最大连接数、初始连接数、空闲检测都配好避免频繁开关连接。PASSWORD 直接写在类里只适合本地开发任何要提交的实战项目都应该把配置挪到 jdbc.properties 里并在版本管理里忽略它这是 java 工程师的第一个数据安全习惯。参数说明URL 里的 3306 是 MySQL 默认端口后面跟的 personinfo 是数据库名必须存在characterEncodingutf8 是告诉驱动以 UTF-8 传参这直接关系到第五章要说的中文乱码问题。useSSLfalse 是 MySQL 5.7 时代的通行写法MySQL 8 默认开启 SSL虽然课设里不会真的校验证书但保留 false 能省掉一堆连接日志。如果密码里带特殊字符注意把 URL 里的参数放在库名后面避免把特殊字符拼进 URL。3.3 用户列表与新增Dao 层和 Servlet 层的最小可运行代码Dao 层我习惯先写两个基础方法findAll 用于列表页展示save 用于新增记录package com.example.personinfo.dao; import com.example.personinfo.model.User; import com.example.personinfo.util.DBUtil; import java.sql.*; import java.util.ArrayList; import java.util.List; public class UserDao { public ListUser findAll() { String sql SELECT id, username, name, gender, phone, email, address, create_time FROM user_info ORDER BY id DESC; ListUser list new ArrayList(); try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql); ResultSet rs ps.executeQuery()) { while (rs.next()) { User u new User(); u.setId(rs.getInt(id)); u.setUsername(rs.getString(username)); u.setName(rs.getString(name)); u.setGender(rs.getInt(gender)); u.setPhone(rs.getString(phone)); u.setEmail(rs.getString(email)); u.setAddress(rs.getString(address)); u.setCreateTime(rs.getTimestamp(create_time)); list.add(u); } } catch (SQLException e) { e.printStackTrace(); } return list; } public int save(User user) { String sql INSERT INTO user_info (username, password, name, gender, phone, email, address) VALUES (?, ?, ?, ?, ?, ?, ?); try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, user.getUsername()); ps.setString(2, user.getPassword()); ps.setString(3, user.getName()); ps.setInt(4, user.getGender()); ps.setString(5, user.getPhone()); ps.setString(6, user.getEmail()); ps.setString(7, user.getAddress()); return ps.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); return 0; } } }逻辑说明findAll 里没有写 SELECT *而是把字段名列出来因为表以后会加字段按需取列能让代码意图更清楚密码在列表查询里刻意不查避免日志打印时把密码带出去。save 方法用的是 PreparedStatement 的 ? 占位符而不是拼字符串这是防 SQL 注入的底线做法任何用户输入直接拼进 SQL 的做法都不可取。参数说明ps.setInt(4, user.getGender()) 对应 gender 的 TINYINT 字段JDBC 里 setInt 即可MySQL 会自动做类型转换executeUpdate 返回受影响行数调用方判断返回值大于 0 才算新增成功。Servlet 层再写一个列表接口package com.example.personinfo.servlet; import com.example.personinfo.dao.UserDao; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.List; WebServlet(/user/list) public class UserListServlet extends HttpServlet { private final UserDao userDao new UserDao(); Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding(UTF-8); Listcom.example.personinfo.model.User users userDao.findAll(); req.setAttribute(users, users); req.getRequestDispatcher(/user_list.jsp).forward(req, resp); } }这里的逻辑是Servlet 不直接输出 HTML而是把查出来的 List 放到 request 作用域里forward 给 JSP 渲染。doGet 里先调 setCharacterEncoding 是为了让请求参数按 UTF-8 解码这一步漏了页面新增中文姓名时就会乱码这是第五章要细说的坑。使用 WebServlet 注解代替 web.xml 里配置 是 Servlet 3.0 之后的主流写法代码更集中也方便你一眼看到 URL 路径。3.4 修改与删除事务控制与防 SQL 注入的两个关键点update 和 delete 是两个高频接口代码如下public int update(User user) { String sql UPDATE user_info SET name?, gender?, phone?, email?, address? WHERE id?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, user.getName()); ps.setInt(2, user.getGender()); ps.setString(3, user.getPhone()); ps.setString(4, user.getEmail()); ps.setString(5, user.getAddress()); ps.setInt(6, user.getId()); return ps.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); return 0; } } public int deleteById(int id) { String sql DELETE FROM user_info WHERE id?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setInt(1, id); return ps.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); return 0; } }逻辑说明update 方法里没有把密码放进去因为个人信息维护场景下编辑页通常不要求重输密码密码应该走单独的修改接口避免每次编辑都覆盖一遍密码字段。deleteById 返回受影响行数调用方判断它大于 0 才算删成功。两个方法都用 PreparedStatement 占位符防 SQL 注入的核心就是在任何情况下都不拼接用户输入。你需要知道的一个事务场景是如果以后扩展成“删除用户时同时删他的登录日志、关联地址”那 deleteById 就不能只执行一条 SQL而要在同一个 Connection 上关闭自动提交全部执行成功再 commit失败则 rollback。这正是 java 面试常问的“java 怎么保证数据一致性”的落地答案。课设的单表操作不需要显式事务但代码骨架里要预留这个习惯把 update 和 delete 分别封装成独立方法将来加逻辑时不需要推翻重写。4. 把 zip 跑起来IDEATomcatMySQL 的部署操作与配置核对4.1 导入项目的三件事解压、识别目录、配置 Artifacts拿到 zip 先解压到没有中文和空格的全英文路径这个细节几乎每天都能在答疑里见到路径带中文Tomcat 启动时资源定位失败页面白屏。导入 IDEA 时不要直接 Open 整个文件夹先确认它是 Maven 项目还是普通 Web 项目。看根目录有没有 pom.xml有就 Open 后等 Maven 下载依赖没有就用 File New Project from Existing Sources一路选 src 目录让 IDEA 自动识别源根。配 Artifacts 是老师傅和新手的分水岭。IDEA 里把 Web 项目部署到 Tomcat需要 Project Structure Artifacts Add Web Application: Exploded然后把依赖的库加入 lib。漏掉这一步常见报错是 java.lang.ClassNotFoundException: com.mysql.jdbc.Driver或者找不到 servlet-api 的类。配完在 Output Layout 里核对 WEB-INF/classes 和 WEB-INF/lib 是否都有内容再点运行。如果你是第一次用 IDEA 社区版要注意社区版不直接内置 Tomcat 集成需要手动下载 Tomcat 并配置 Application Server逻辑和旗舰版一样只是入口不同。导入完成后先看一眼项目根目录里有没有 README 或数据库脚本有就照着准备数据库没有也没关系用第三章的 SQL 自己建就行。还有一个常见动作确认 IDEA 的 Project SDK 指向你安装的 JDK 8而不是默认的最新版本因为老项目在 JDK 17 下会有模块访问限制。4.2 数据库初始化在 MySQL 里建库建表的完整命令项目里一般会带一份 personinfo.sql 或 init.sql 脚本没有也没关系用第三章开头那段 SQL 自己建。命令行初始化流程如下mysql -u root -p source /绝对路径/personinfo.sql; show tables;逻辑说明source 后面必须写绝对路径MySQL 客户端不会相对项目目录去解析show tables 看到 user_info 这张表后再进行下一步。注意 Windows 下路径分隔符用 / 或转义的 \不要直接复制文件资源管理器里的反斜杠路径否则会报语法错误。注意如果 source 报错多半是路径里的分隔符用了 Windows 反斜杠换成 / 再试。如果不想进命令行用 Navicat 或 IDEA 自带的 Database 面板直接执行 SQL 脚本也行但一定要先建好库再执行建表语句不然会因为 USE 语句被忽略而把表建到默认库里。建完库之后顺手执行一条 SELECT * FROM user_info; 确认能查出来再回 IDEA 里启动 Tomcat。这一步提前验证了数据库连通性后面再报错你就可以直接排除数据库问题了。4.3 启动前的五项检查避免 Java Web 项目启动失败的常见原因在所有检查之前先建立正确的启动顺序MySQL 服务先起再启动 Tomcat最后浏览器访问。很多人搞反了Tomcat 起来了、MySQL 没起页面一调接口就 500。Tomcat 本身不依赖数据库启动所以它不会立刻报错直到你访问列表页才炸。先确认 MySQL 在跑再检查 Tomcat问题就少一半。第一项DBUtil 里的 URL、用户名、密码是不是和本机 MySQL 一致。URL 里 3306 后面的库名 personinfo 必须存在USER 和 PASSWORD 要严格匹配。第二项MySQL 服务有没有启动Windows 下 services.msc 查看服务状态Linux 下 systemctl status mysqld 或 service mysql status。第三项JDK 版本老项目用 JDK 8 最稳JDK 17 会遇到 java.base 模块访问限制报错信息里出现 IllegalAccessError 时别慌那不是代码错误是编译器版本和运行版本打架。第四项Tomcat 端口默认 8080如果被占用在 Run Configuration 里把 HTTP port 改成 8081同时访问 URL 同步改不必非要杀掉占用进程。第五项Rebuild Project 一次确保 target/classes 里的 class 是最新的IDEA 的增量编译偶尔不彻底改完代码不生效时先执行这个操作。这五项里第一项和第三项是出现频次最高的 java 启动失败原因。DBUtil 里写的是 root/123456你本机密码是 abc123启动时 Tomcat 没问题一访问数据库就抛 Access denied。JDK 版本问题更隐蔽代码编译通过、Tomcat 也能起但访问页面时突然报错这类黑匣子问题最消耗耐心。遇到 Java Web 项目启动失败别急着怀疑代码逻辑先把上面五项按顺序过一遍能排查掉八成问题。5. 避坑排查五个让项目和 Java 启动失败的常见原因这五条问题是我帮别人调课设时遇到频率最高的每一条都按“现象、原因、解决”的顺序写清楚你可以直接对号入座。排查时有个基本顺序先看 Tomcat 日志有没有启动异常再看数据库是否连通最后才怀疑业务代码这个习惯能让你少走很多弯路。5.1 现象ClassNotFoundException: com.mysql.jdbc.DriverTomcat 能启动访问 /user/list 时报 500日志里出现 ClassNotFoundException: com.mysql.jdbc.Driver有些时候编译阶段就直接报错找不到这个类。原因项目里没有把 MySQL 驱动放进运行时。普通 Web 工程里jar 包必须出现在 WEB-INF/libMaven 工程里依赖的作用域不能是 provided。解决普通 Web 工程把 mysql-connector-java 的 jar 拷进 WEB-INF/lib右键 Add as Library然后重新构建 ArtifactsMaven 工程检查 pom.xml把依赖的 scope 去掉或改成 runtime刷新 Maven 让依赖进入运行时。验证方法部署后看日志里不再出现该异常列表页能正常出来。这里提醒一句如果你用的是 MySQL 8驱动类名要从 com.mysql.jdbc.Driver 改成 com.mysql.cj.jdbc.Driver这个细节经常和这个报错一起出现改完驱动还得改 URL 里的时区参数。5.2 现象java.sql.SQLException: Access denied for user rootlocalhost页面能打开但第一次点登录或访问列表就报 Access denied。原因DBUtil 里配置的用户名、密码和本机 MySQL 实际凭据不一致或者你安装 MySQL 时设置的密码和你记忆中的密码不同。解决先核对 DBUtil 的 USER 和 PASSWORD 常量。确认密码没问题后进入 MySQL 执行 ALTER USER rootlocalhost IDENTIFIED BY 你的密码; 然后 FLUSH PRIVILEGES;。注意 MySQL 8 默认的 caching_sha2_password 认证插件老版本驱动不支持时会报 Unable to load authentication plugin解决方式是换成 mysql-connector-java 8.x 的驱动或把用户改成 mysql_native_password 认证方式。这类数据库连接问题本质上是配置不一致不是代码 bug排查时先把所有写着 3306 的文件找出来对齐。5.3 现象新增数据全是问号中文乱码怎么办录入“张伟”列表页显示“????”或“张?”英文正常中文乱码。原因编码链路任何一个环节不是 UTF-8常见有三处数据库表字符集不是 utf8mb4JDBC 连接 URL 没有 characterEncodingutf8JSP 页面没有声明 UTF-8。解决建表用 utf8mb4连接 URL 加 characterEncodingutf8JSP 顶部加一行显式声明% page contentTypetext/html;charsetUTF-8 languagejava %对应的 Servlet 里保证 req.setCharacterEncoding(UTF-8) 在读取任何参数之前调用而且不能只写在某一个 Servlet 里最好用 Filter 统一处理。GET 请求传中文时还要检查 Tomcat 的 server.xml 中 Connector 是否配置了 URIEncodingUTF-8——setCharacterEncoding 只对 POST 请求的 body 生效GET 参数是在 Tomcat 解析 URL 时就完成解码的。这条覆盖了 java 编码问题最经典的组合拳建议一次全改到位再测试。5.4 现象IDEA 部署后访问一直 404IDEA 里运行 Tomcat 显示成功浏览器访问 http://localhost:8080/ 却一直 404。原因Application context 配置和访问路径不一致常见是 IDEA 的 Deployment 里 context 为 /项目名你访问根路径或者 Artifact 没有真正部署上去。解决在 Edit Configurations Deployment 里把 Application context 改成 /然后访问 http://localhost:8080/user/list如果不想改 context就按 /项目名/user/list 访问。同时检查 Artifacts 面板里有没有 Web Application: ExplodedOutput Layout 里是否包含 WEB-INF/classes 和 WEB-INF/lib。部署后还 404再看项目的 web.xml 里 welcome-file 是否配置了 index.jsp如果什么都没配Tomcat 根路径没有默认页面404 是正常的直接访问具体的 Servlet 路径即可。5.5 现象Connection refused数据库连接不上Tomcat 起来了页面也能加载但一访问数据库就抛 java.net.ConnectException: Connection refused: connect。原因MySQL 服务没启动或者连接串里的端口写错或者 MySQL 只监听了 127.0.0.1 而你连接的是其他主机。解决Windows 下 services.msc 启动 MySQL 服务Linux 下 systemctl start mysqld 或 service mysql start。然后执行 netstat -ano | findstr 3306 确认端口在监听。如果本机装了多个 MySQL 实例检查你连的是 3306 还是 3307。开发环境里我一般直接在 JDBC URL 里写 localhost:3306避免用 127.0.0.1 之外的地址。云服务器部署时还要注意防火墙是否放行了 3306以及 MySQL 的 bind-address 是否绑定了内网地址。这个问题排查顺序固定先看服务进程再看端口最后看防火墙。6. 进阶方向从课设到工程化三个值得动手改造的点第一点把 Servlet JSP 改造成 Spring Boot Thymeleaf。改造时不必重写 Dao把 UserDao 原样搬进 Spring Boot用 Repository 注册Service 层加一个 UserService 包住事务Controller 用 Controller 替代 Servlet。这个改造过程能同时理解两套框架的异同而且简历上写“基于 Spring Boot 的个人信息管理系统”比写 Servlet 版更有说服力。代码对比最能体现框架省掉的工作量// Servlet 版手动处理参数、转发、重定向 WebServlet(/user/delete) public class UserDeleteServlet extends HttpServlet { Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { int id Integer.parseInt(req.getParameter(id)); new UserDao().deleteById(id); resp.sendRedirect(req.getContextPath() /user/list); } } // Spring Boot 版参数绑定、视图解析交给框架 GetMapping(/user/delete) public String delete(RequestParam(id) int id) { userService.deleteById(id); return redirect:/user/list; }第二点给列表加模糊搜索和分页。SQL 改成 SELECT * FROM user_info WHERE name LIKE ? ORDER BY id DESC LIMIT ?, ?前端加一个搜索框和分页条。分页参数有个换算容易写错pageSize 取 10pageNo 从 1 开始SQL 里的 offset 是 (pageNo - 1) * pageSize建议单独写一个 PageResult 类把分页逻辑包住别在每个方法里手算。第三点把明文密码换成哈希加盐存储。用 BCryptPasswordEncoder 或 MessageDigest 加盐数据库里只存 hash。这个改动会逼你顺手处理另一个问题修改个人信息时不要把密码一起覆盖密码单独走改密接口并且用事务保护。经过这三处改造之后这个 zip 就不再是课设模板而是一个能讲清楚设计取舍的小作品。我的习惯是每拿到一个课设项目先跑通再重构中间遇到的所有报错都是最好的面试素材。这个方向值不值得做答案是值得——但它值得的不是那个 zip 本身而是你把分层、编码、事务、发布这几件事全部吃透的过程。希望能帮到你。本文还有配套的精品资源点击获取
返回列表