ARTICLE DETAIL

资讯详情

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

JSP+Servlet+MySQL实验设备管理系统部署实战与避坑指南

JSP+Servlet+MySQL实验设备管理系统部署实战与避坑指南 简介这套基于JSPServletMySQL的实验室设备管理系统面向Java Web初学者与课程设计人群完整演示了传统Servlet技术栈下的MVC分层实现JSP负责视图展示Servlet处理请求与控制逻辑MySQL存储设备、申请、用户等数据。压缩包共182个文件、约6.21MB涵盖34个JSP页面、32个Java源文件及对应class文件同时包含数据库SQL脚本、前端CSS/JS与图片素材、项目配置文件等基本开箱可导入Eclipse配合Tomcat运行。系统覆盖设备管理、申请审批、课程与用户管理、分类维护等模块并预置了针对增删改查、权限控制及SQL注入防护的设计思路适合作为毕业设计、课设模板或练手项目参考。目前已有454人学习下载对想快速理解ServletJSP协作机制、补齐项目完整度的开发者有直接的借鉴价值。1. 这个打包好的JavaWeb项目值不值得你花一个周末跑通拿到一个名为基于jspservletmysql的实验室设备管理系统.zip的项目包时最常见的开局是解压、启动、浏览器打开、页面404然后你怀疑人生。这套技术栈是JavaWeb课程设计和毕设里最稳的组合——JSP负责把数据渲染成页面Servlet接收请求并控制跳转MySQL把设备台账、借用记录、维修状态全部落库。它解决的问题很具体实验室设备多、借还乱、故障无记录这套系统用最朴素的方式把这些流程搬上线。适合刚学完JavaWeb想找个完整案例练手的人也适合拿现成代码当毕设底子的人。但前提是你得先把环境对齐JDK、Tomcat、MySQL的版本有一处不匹配后面全是在跟报错搏斗。2. 部署前的环境底子JDK、Tomcat、MySQL 怎么配才不打架2.1 版本匹配是第一道门槛这类基于jspservletmysql的老项目对运行环境没那么宽容。最常见的组合是JDK 8配上Tomcat 8.5或9.0MySQL用5.7或8.0。先说JDK如果你机器上装的是JDK 17甚至更高直接跑老代码大概率会在编译阶段翻车因为Tomcat 10把javax.servlet包改成了jakarta.servlet很多老项目里的import javax.servlet.*根本过不了编译。同理Tomcat 10配上老war包运行时会直接给你报ClassNotFoundException: javax.servlet.http.HttpServlet。这不是项目坏了是环境没对上。所以拿到zip包之后第一步不是打开代码而是先看web.xml里的web-app版本、看源码里import的是javax.servlet还是jakarta.servlet再决定装哪个JDK和Tomcat。我一般建议老项目一律JDK 8 Tomcat 9这是兼容面最广的一组。如果你机器上已经装了新版JDK优先去配置环境变量而不是卸载重装IDEA里可以单独给项目指定JDK版本。MySQL这边相对宽松5.7和8.0对这张项目的大多数SQL都能兼容但连接驱动要选对。MySQL 5.7配mysql-connector-java 5.1.x没问题MySQL 8.0则要用mysql-connector-java 8.0.x否则启动时大概率遇到Communications link failure。判断手里驱动版本的方法很简单看lib下的jar包文件名8.x开头的就是新驱动。2.2 MySQL安装与初始化Windows、Linux 两条路Windows下按mysql安装教程走就行下载MSI安装包选Developer Default就可以。有一点容易忽略安装时选认证方式MySQL 8.0默认用caching_sha2_password这对老项目不友好很多老版本连接池不认这个插件建议选Use Legacy Authentication或者安装完了再用ALTER USER改回mysql_native_password。设置root密码时别绕行后面所有配置文件都要写它。Linux下通常是apt或yum安装# Ubuntu/Debian sudo apt update sudo apt install mysql-server sudo systemctl start mysql sudo systemctl status mysql # CentOS/RHEL 7 sudo yum install -y mysql-community-server sudo systemctl start mysqld sudo systemctl status mysqld说明systemctl start mysqld启动的是MySQL服务端进程单独跑一个JavaWeb项目时MySQL必须以服务形式常驻不能只在终端里敲一个mysql命令就以为数据库可用了——那个命令只是客户端进去还得有服务在背后监听3306端口。装完先确认端口起来netstat -tlnp | grep 3306。如果看到报错ERROR 2002 (HY000): Cant connect to local MySQL server through socket /tmp/mysql.sock十有八九是mysqld根本没拉起来先去查/var/log/mysql/error.log和mysqld.service的运行状态而不是去检查代码。初始化表结构和数据推荐用MySQL Workbench它的使用教程在刚开始看有点繁琐但比命令行直观很多。connector JDBC的URL里主机就填localhost:3306数据库名按项目里的jdbc.properties来填。注意Workbench自带的默认端口就是3306如果你手改过/etc/mysql/mysql.conf.d/mysqld.cnf这里也要同步改。2.3 Tomcat 启动startup.bat 不弹窗才是真问题Tomcat解压后放在无中文、无空格的路径下比如D:\apache-tomcat-9.0.xx。Windows下直接双击bin/startup.bat的一般都没好下场——窗口一闪而过你根本不知道后台发生了什么。正确做法是先配置好JAVA_HOME环境变量再打开命令行手动执行# Windows 命令行 set JAVA_HOMEC:\Program Files\Java\jdk1.8.0_202 cd D:\apache-tomcat-9.0.xx\bin startup.bat# Linux export JAVA_HOME/usr/lib/jvm/java-1.8.0 $CATALINA_HOME/bin/startup.sh # 看实时日志 tail -f $CATALINA_HOME/logs/catalina.out说明Tomcat本身是一个Servlet容器不解析JSP以外的脚本。很多人问nginx支持jsp吗答案是否定的——nginx只管静态资源转发动态请求必须转发给Tomcat。这套实验室设备管理系统在单机部署时根本不需要nginx直接访问http://localhost:8080/项目名/即可。启动后验证两件事一是后台没打印Exception二是浏览器能打开Tomcat默认首页。如果8080被占用去conf/server.xml里把Connector端口改成8081同时改掉Server标签的8005端口避免冲突。Tomcat启动失败时第一件事是看logs/catalina.out最末端几百行里一定写着真实原因。JAVA_HOME配错、项目里缺类、端口被占全在这里现形。别去改代码先把日志读明白。3. 数据库先行建库建表与初始化数据的完整过程3.1 实验室设备管理系统的六张表各自管什么拿到项目后第一步别急着启动先看SQL文件。这类系统的表结构大同小异核心是围绕设备展开的。常见做法是建这几张表sys_user存登录用户和管理员category存设备分类lab_info存实验室信息equipment存设备台账borrow_record存借用记录repair_record存维修记录。设备状态是这套系统的灵魂字段一般用TINYINT表示0在用、1已借出、2维修中、3报废后面所有列表和统计都是基于这个值。表与表之间的关系也简单equipment外键关联category和lab_infoborrow_record外键关联equipment和sys_user。外键在毕设答辩时是加分项但在实际导入时也是麻烦来源——有外键的表必须按依赖顺序创建否则直接报错。3.2 建库建表SQL默认值、字符集、引擎的细节下面这套SQL是我调整过的通用版本兼容MySQL 5.7和8.0默认值都用得很保守避免导入时报错-- 建库utf8mb4 必须指定否则中文全乱码 CREATE DATABASE IF NOT EXISTS lab_dev DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE lab_dev; -- 用户表 CREATE TABLE sys_user ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT 主键, username VARCHAR(50) NOT NULL UNIQUE COMMENT 登录名, password VARCHAR(64) NOT NULL COMMENT 密码建议存MD5, real_name VARCHAR(50) DEFAULT COMMENT 真实姓名, role TINYINT NOT NULL DEFAULT 1 COMMENT 角色:1管理员 2教师 3学生, create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT系统用户表; -- 设备分类表 CREATE TABLE category ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT设备分类表; -- 实验室表 CREATE TABLE lab_info ( id INT PRIMARY KEY AUTO_INCREMENT, lab_name VARCHAR(100) NOT NULL COMMENT 实验室名称, location VARCHAR(200) DEFAULT COMMENT 位置, manager_id INT DEFAULT NULL, FOREIGN KEY (manager_id) REFERENCES sys_user(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT实验室表; -- 设备台账表 CREATE TABLE equipment ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT 设备编号, equip_name VARCHAR(100) NOT NULL COMMENT 设备名称, model VARCHAR(50) DEFAULT COMMENT 型号, category_id INT DEFAULT NULL, lab_id INT DEFAULT NULL, status TINYINT NOT NULL DEFAULT 0 COMMENT 0在用 1已借出 2维修中 3报废, buy_date DATE DEFAULT NULL COMMENT 购置日期, price DECIMAL(10,2) DEFAULT 0.00 COMMENT 单价, FOREIGN KEY (category_id) REFERENCES category(id), FOREIGN KEY (lab_id) REFERENCES lab_info(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT设备台账表; -- 借用记录表 CREATE TABLE borrow_record ( id INT PRIMARY KEY AUTO_INCREMENT, equip_id INT NOT NULL, user_id INT NOT NULL, borrow_time DATETIME DEFAULT CURRENT_TIMESTAMP, expect_return_time DATETIME DEFAULT NULL, actual_return_time DATETIME DEFAULT NULL, status TINYINT DEFAULT 0 COMMENT 0借用中 1已归还, remark VARCHAR(255) DEFAULT , FOREIGN KEY (equip_id) REFERENCES equipment(id), FOREIGN KEY (user_id) REFERENCES sys_user(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT借用记录表; -- 维修记录表 CREATE TABLE repair_record ( id INT PRIMARY KEY AUTO_INCREMENT, equip_id INT NOT NULL, report_user_id INT DEFAULT NULL, repair_result VARCHAR(255) DEFAULT , status TINYINT DEFAULT 0 COMMENT 0待维修 1维修中 2已完成, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, finish_time DATETIME DEFAULT NULL, FOREIGN KEY (equip_id) REFERENCES equipment(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT维修记录表;逻辑说明DEFAULT 0是刻意写的很多老代码里status字段靠默认值兜底你不给默认值Java代码里一旦漏传这个字段MySQL直接报错。DEFAULT CHARSETutf8mb4也是血的教训老版本很多用utf8但utf8在MySQL里并不是真正的全量Unicode插入生僻字或Emoji时会变成问号。CURRENT_TIMESTAMP是MySQL 5.6以后通用的默认值写法不用考虑写NOW()。参数说明AUTO_INCREMENT自增主键每张表都要有这是Java代码里rs.getInt(id)能工作的前提DECIMAL(10,2)价格字段用定点数而不是FLOAT浮点算钱会出精度问题设备管理系统虽然不涉及交易但答辩时能说清楚这一条是加分项。初始化数据时顺手插入管理员账号密码建议用MD5加密后的值。前端拿用户输入传进来前先MD5一下再比对这是这套系统里最容易被老师追问的点。3.3 导入SQL的三种方式以及最常踩的坑方式一命令行source导入mysql -u root -p lab_dev lab_dev.sql这种方式要求SQL文件里已经写了CREATE DATABASE不然得先手动建库。导入时看到ERROR 1064通常是SQL文件编码不对用记事本另存为UTF-8再试。方式二Navicat、MySQL Workbench 导入Navicat连接mysql后右键数据库运行SQL文件。这类GUI工具的坑在于如果你的SQL文件里有USE lab_dev;但当前选中了别的库执行结果会跑到另一个库去。我建议每次导入前都先双击选中目标库再执行整个文件人肉确认左上角库名。方式三IDEA Database插件直接跑适合在开发环境里调试选中lab_dev后执行整个脚本错误信息直接在控制台打印排查效率最高。导入成功与否的验证不是看日志显示成功而是执行几条查询USE lab_dev; SHOW TABLES; SELECT * FROM sys_user; SELECT COUNT(*) FROM equipment;sys_user里能看到刚插入的管理员说明表结构没问题equipment是空表不奇怪你可以手动插一条假设备用来测试列表页正常系统启动后应该在页面上有新增设备的入口。3.4 导入SQL时的三个边界坑第一个坑是外键顺序。如果你的SQL文件里先建equipment再建category而equipment外键指向category导入时直接报Cannot add foreign key constraint。解决办法是调整建表顺序先建被依赖的表。这也是为什么我上面的SQL按用户、分类、实验室、设备、借用、维修的顺序排列。第二个坑是重复导入。同一份SQL跑两遍第二遍报Table sys_user already exists。有些人的处理方式是先DROP TABLE IF EXISTS再CREATE TABLE这会导致数据被清空。正确姿势是有意识地管理SQL文件把它当成初始版本后续的增量改动写进单独的update_xxx.sql不要回头改初版。第三个坑是历史遗留数据。从zip里打开的是别人导出来的SQL里面可能带着他们的测试数据。你本地装的是MySQL 8.0数据里如果有utf8mb4_0900_ai_ci排序规则而你的SQL文件用的utf8mb4_general_ci不会报错但编码方式不一致会让后期排查乱码时多绕一圈。统一把CHARSETutf8mb4 COLLATEutf8mb4_general_ci写清楚省得后面玄学问题频出。4. Servlet生命周期与登录链路代码在跑什么4.1 项目目录结构与三层职责这类zip项目解压后典型结构是标准的IDEA/Eclipse Web工程src/ com.lab.entity/ 实体类对应每张表 com.lab.dao/ 数据访问层JDBC操作 com.lab.servlet/ 控制层接收HTTP请求 com.lab.util/ 工具类DBUtil、MD5等 web/ WEB-INF/web.xml Servlet映射配置 login.jsp 登录页 index.jsp 主框架页 equipment/list.jsp 设备列表页 ... lib/ 依赖jar包含mysql驱动这套结构其实就是经典的ServletJSP三层DAO管数据库Servlet管控制流转JSP管展示。没有Spring没有MyBatis所有对象都手动new。在一个毕设项目里这是优点而不是缺点——代码逻辑全在明面上每个new都能看到老师问起来你也讲得清。vscode编写servlet代码需要怎么配置环境这个问题本质上是给vscode装Java插件、Tomcat插件再把lib下的jar包加进classpath但说实话做这种项目用IDEA Community版或Eclipse更顺没必要在vscode上折磨自己。4.2 Servlet生命周期与请求流转Servlet生命周期是JavaWeb的必考题也是这个系统能跑起来的地基。一个Servlet从加载到销毁经历init() - service() - destroy()三个阶段。实例化后init()只执行一次适合做初始化配置每次请求都经过service()由它根据HTTP方法分发到doGet()或doPost()应用卸载时执行destroy()释放资源。这套系统里最常见的流转是登录login.jsp表单提交到LoginServletdoPost()里取参数、调DAO查库、结果写session、forward或redirect。要说明的是req.getRequestDispatcher(/xxx).forward()和resp.sendRedirect()的区别——forward是服务器内部跳转浏览器地址栏不变request里的属性还在redirect是浏览器重新发一次请求地址栏变化request里的东西全丢。登录成功后存了session的用户信息所以两种跳转都不影响后续取用户但登录失败时用forward把错误提示带回页面否则用户什么都看不到只能干瞪眼。4.3 登录功能的JSP Servlet DAO 三段式实现按Model2思想实现用户注册和登录功能核心原则是JSP里尽量不写Java逻辑只做展示和参数收集。登录页的JSP关键片段% page contentTypetext/html;charsetUTF-8 languagejava % html head title实验室设备管理系统 - 登录/title /head body form action${pageContext.request.contextPath}/login methodpost input typetext nameusername placeholder账号/ input typepassword namepassword placeholder密码/ button typesubmit登录/button /form span stylecolor:red${msg}/span /body /html说明${pageContext.request.contextPath}是EL表达式动态获取项目根路径避免上下文路径写死。表单没写method会默认GET登录信息出现在URL上所以必须显式methodpost。${msg}是Servlet里setAttribute进去的错误提示直接用EL取出页面里干干净净没有一行Java代码这就是Model2中view该有的样子。对应的LoginServletpackage com.lab.servlet; import com.lab.dao.UserDao; import com.lab.entity.User; import javax.servlet.ServletException; import javax.servlet.http.*; import java.io.IOException; public class LoginServlet extends HttpServlet { private UserDao userDao new UserDao(); Override public void init() throws ServletException { // 生命周期第一步实例化后只执行一次适合加载配置 super.init(); System.out.println(LoginServlet init ...); } Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 1. 取参数先做校验 String username req.getParameter(username); String password req.getParameter(password); if (username null || username.trim().isEmpty() || password null || password.isEmpty()) { req.setAttribute(msg, 账号和密码不能为空); req.getRequestDispatcher(/login.jsp).forward(req, resp); return; } // 2. 查库比对密码应该MD5后再拼入 User user userDao.findByUsernameAndPwd(username, password); // 3. 根据查询结果决定去向 if (user ! null) { HttpSession session req.getSession(); session.setAttribute(loginUser, user); resp.sendRedirect(req.getContextPath() /equipment/list); } else { req.setAttribute(msg, 用户名或密码错误); req.getRequestDispatcher(/login.jsp).forward(req, resp); } } Override public void destroy() { super.destroy(); System.out.println(LoginServlet destroy ...); } }逻辑说明doPost里先取参数做非空校验再查库最后按结果分流。这里有个细节值得注意密码比对是在SQL里做还是查出来在Java里做。上面的写法是findByUsernameAndPwd把密码也拼进SQL这种方式简单但理论上不够好——SQL日志里会打印完整SQL密码就暴露了。更稳的做法是先按用户名查出User对象再在Java里比对MD5值但这个度你自己权衡毕设追求的是讲清原理两种都能自圆其说。UserDao的实现package com.lab.dao; import com.lab.entity.User; import com.lab.util.DBUtil; import java.sql.*; public class UserDao { public User findByUsernameAndPwd(String username, String password) { String sql SELECT * FROM sys_user WHERE username ? AND password ?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, username); ps.setString(2, password); try (ResultSet rs ps.executeQuery()) { if (rs.next()) { User u new User(); u.setId(rs.getInt(id)); u.setUsername(rs.getString(username)); u.setRealName(rs.getString(real_name)); u.setRole(rs.getInt(role)); return u; } } } catch (SQLException e) { e.printStackTrace(); } return null; } }逻辑说明用PreparedStatement而不是Statement这是这条代码里最值得讲的一点。前者预编译占位符?由参数传入天然防SQL注入后者拼字符串用户名里输入 or 11就能绕过登录。你要是敢在DAO层写字符串拼接的SQL答辩时基本属于送人头。JDK 7之后Connection、PreparedStatement、ResultSet都实现了AutoCloseabletry-with-resources写法能把三个资源一次性关掉别再用老代码里那种finally里套finally的写法。Servlet的映射配在web.xml里servlet servlet-nameloginServlet/servlet-name servlet-classcom.lab.servlet.LoginServlet/servlet-class /servlet servlet-mapping servlet-nameloginServlet/servlet-name url-pattern/login/url-pattern /servlet-mapping参数说明url-pattern填/login那么表单的action路径结尾就得是/login两者必须完全一致。表单上我写了${pageContext.request.contextPath}/login这是带上下文路径的写法因为部署后的地址是http://localhost:8080/lab_dev/loginlab_dev就是上下文路径。如果web.xml里的url-pattern是/servlet/login那页面里的路径也要对应改成/servlet/login。这类404里十个有八个是这里对不上。5. 部署与避坑检修清单启动失败、乱码、连接超时的5个现场5.1 部署到Tomcat的两种方式第一种是war包发布。IDEA里Build Artifacts生成war包扔到Tomcat的webapps目录下启动Tomcat自动解压。这种方式适合部署到服务器目录结构干净但开发时每改一行代码都要重新打war包效率极低。第二种是IDEA里直接配置Tomcat运行。Run/Debug Configurations里选Tomcat ServerDeployment页签里把web目录和exploded war关联上开Update resources热更新。改JSP不用重启就生效改Java代码要用Debug模式配合JRebel或IDEA自带的HotSwap普通改动重启一次也就三五秒。生产服务器上没有IDEAwar包才是正路。但对你现在这个阶段先把第二种跑通所有排错都发生在IDEA的Run窗口里日志可读性比catalina.out好不少。5.2 MySQL 8连接串useSSL、serverTimezone、allowPublicKeyRetrievalDBUtil里的JDBC连接串是这套系统连接MySQL的核心配置也是报错重灾区public class DBUtil { private static String url jdbc:mysql://localhost:3306/lab_dev?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue; private static String user root; private static String password 123456; static { try { Class.forName(com.mysql.cj.jdbc.Driver); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(url, user, password); } }参数说明useUnicodetruecharacterEncodingutf8保证中文字节流正确解码这条丢了就是页面问号useSSLfalse是MySQL 8的硬要求新版驱动默认要加密连接本地开发根本用不上SSL不关就会报Communications link failure或者让你配证书serverTimezoneAsia/Shanghai解决时区差8小时的问题不设的话日期字段查出来比实际早8小时allowPublicKeyRetrievaltrue专门解决MySQL 8的caching_sha2_password认证时公钥获取问题不设会报Public Key Retrieval is not allowed。这四个参数在MySQL 8.0mysql-connector-java 8.x的组合下缺一不可缺一个就是一套报错。驱动类名也注意老驱动是com.mysql.jdbc.Driver8.x换成了com.mysql.cj.jdbc.Driver。如果你用的jar是8.x却写老类名Class.forName会直接抛ClassNotFoundException。5.3 五个现场现象、原因、解决现场一浏览器访问项目路径直接404现象Tomcat首页能开输入http://localhost:8080/项目名/却404。原因war包没解压成功或者IDEA里部署的Application context和实际访问路径不一致。解决先看webapps目录下有没有对应的文件夹再开logs/catalina.out看解压日志绝大多数情况是war包没生成完整。开发环境检查IDEA配置里的Deployment - Application context它决定URL里的项目名段改完重启Tomcat。现场二控制台报ClassNotFoundException: com.mysql.jdbc.Driver现象Tomcat能启动登录页能打开一点登录就500控制台找不到驱动类。原因mysql驱动jar包没放在正确位置。解决检查WEB-INF/lib下有没有mysql-connector-java-8.0.x.jar。IDEA里还要确认File - Project Structure - Artifacts里lib有没有被打进artifact。这事很玄学IDEA里明明引了jar打包时就是不带手动在WEB-INF/lib里放一份再重新构建最稳妥。现场三Access denied for user rootlocalhost现象应用启动时数据库连接就失败日志末尾是这句话。原因DBUtil里的密码写错或者MySQL 8的root用户认证插件和驱动不兼容。解决先命令行验证密码是否对mysql -u root -p能进去说明密码没错那就是认证插件的问题。MySQL 8执行ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 你的密码; FLUSH PRIVILEGES;把认证方式改回mysql_native_password最省事。要是这条命令提示Unknown plugin或者不想动root那就新建一个专用账号CREATE USER lab% IDENTIFIED BY lab123456; GRANT ALL PRIVILEGES ON lab_dev.* TO lab%; FLUSH PRIVILEGES;然后把DBUtil里的用户名密码换成lab / lab123456这比动root的认证方式更安全也更像真实项目里的做法。现场四页面数据全是问号或乱码现象登录成功进去设备名称、实验室名称显示???或者æµè¯。原因三层编码不一致。数据库是utf8mb4页面是GBK连接串没指定字符集三个环节只要有一个不对就乱。解决从上到下统一。数据库表已经建好的情况下先ALTER TABLE equipment CONVERT TO CHARACTER SET utf8mb4;再把连接串里的characterEncodingutf8加上最后确认每个JSP头部都有pageEncodingUTF-8。Tomcat 8以上GET请求默认UTF-8但POST还是要靠统一编码的Filter兜底或者在每个doPost开头写req.setCharacterEncoding(UTF-8)。哪个环节漏了乱码就在哪个环节出现。现场五启动Tomcat报端口被占用现象日志里Port 8080 required by Tomcat v9.0 Server at localhost is already in use。原因上次Tomcat没关干净或者有其他程序占了8080。解决Linux执行netstat -tlnp | grep 8080Windows执行netstat -ano | findstr 8080找到PID后杀掉进程。顺手改端口的话编辑conf/server.xml里Connector port8080为8081同时避坑如果你开了多个Tomcat实例server.xml里的Server port8005也会冲突两个都要改。这条属于典型的参数没对上不是代码问题。6. 验证路径与优化方向从能跑到像一个真正的系统部署成功不代表系统能用你要按业务流程走一遍完整链路才算真正跑通。我的验证顺序是用初始化SQL里的管理员账号登录添加一个设备分类再添加一台设备把它的状态改一次创建一条借用记录再归还最后在维修模块发起一条报修。每一步都去MySQL里查一下对应表确认页面显示和数据落库一致。这套流程走完说明JSP页面、Servlet控制、JDBC访问三层是通的。这里的检查要点是设备状态流转设备从在用变成已借出是由借用记录的插入和状态更新两个SQL共同完成的。源码里如果只插记录不更新equipment.status那么借还链路就是半残的。我看代码时会特意搜UPDATE equipment SET status确认它在借出和归还两个动作里都被调用如果没有那这就是你系统里需要自己补的逻辑。进阶方向上先把数据库连接从DriverManager.getConnection换成Druid连接池这是成本最低、收益最明显的优化。老代码每次请求都新建物理连接数据库压力一大就卡死换成连接池后连接复用性能会有质变。再往后是加一个统一的编码Filter把每个Servlet里的setCharacterEncoding删掉集中管理。Servlet配置也可以用注解WebServlet替代web.xml少写一堆XML。等你哪天觉得JSP里的EL表达式不够用了或想换前后端分离再去了解springboot2集成jsp的目录约束——SpringBoot官方不建议用jsp真到了那一步大概率是你转向新架构的时候。允许我以一个踩过的坑收尾有一次部署到现场服务器页面一直报500查了一天发现是服务器MySQL的lower_case_table_names参数和本地不一致表名大小写敏感度不同sys_user在本地能查到在线上直接Table doesnt exist。后来我把所有表名和SQL里的表名统一成小写这个坑再没出现过。环境差异是最难排查的遇到诡异问题先怀疑参数配置别急着改代码。希望这套排查思路和部署路径能帮你把这台设备管理系统稳稳跑起来——也让你真正看懂了它而不是只会双击启动。本文还有配套的精品资源点击获取
返回列表