尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Java就业信息管理系统:Servlet+JSP+MySQL实战开发指南

Java就业信息管理系统:Servlet+JSP+MySQL实战开发指南 简介这是一套基于Java开发的就业信息管理系统完整源码面向高校计算机专业学生、Java后端初学者及中小型招聘平台开发者解决就业数据集中管理、多角色协同操作与可视化分析等实际问题。系统采用前后端分离架构后端基于Spring BootMySQL实现用户管理、权限控制与SQL注入防护前端使用Vue.js构建响应式界面并集成小程序适配能力支持二次开发与定制部署。资源包共825个文件含115个核心Java业务逻辑文件、45个Vue组件、164个JS交互脚本、79个GIF动效资源及53个CSS样式文件整体24.36MB结构清晰涵盖启动脚本bat、数据库初始化SQL、配置文件yml/properties及完整文档docx/md便于快速运行与理解分层设计。目前已有66人学习下载附带备份文件.bak与多环境部署脚本适合用于课程设计、毕设参考或企业级就业服务平台原型开发。1. 为什么一个“就业信息管理系统”要用 Java 写——不是为了炫技而是因为招聘方真在筛这个能力你打开某招聘网站搜“Java 开发”岗位描述里高频出现的不是“精通 Spring Cloud”而是“熟悉企业级业务系统开发”“能独立完成中小型管理类系统从需求到上线的闭环”。这个“就业信息管理系统”就是这类岗位最常考的课程设计、毕设、甚至入职后第一个接手的模块它不追求高并发或低延迟但必须稳、可维护、边界清晰、权限分明、数据可追溯。它不是玩具项目而是校招面试官判断你是否真懂“Java 工程化落地”的黑匣子——能不能把 JDBC 封装成可复用的数据访问层能不能用 Filter Session 做出像样的登录态校验能不能让 Excel 导入导出不崩在中文乱码上能不能把“学生查岗位”和“管理员删记录”用不同 Controller 分开而不是全塞在一个 Servlet 里硬 if-else本篇不讲 Spring Boot 自动装配原理只讲怎么用最朴素的 Java SE Servlet JSP MySQL 组合在本地跑通一个真实可用、能交差、能演示、能写进简历的就业信息管理系统。所有代码基于 JDK 8兼容性最强、Tomcat 8.5部署最稳、MySQL 5.7高校机房标配不碰任何云服务或第三方 SaaS 接口——因为你要的不是“能跑”而是“能讲清楚每一行为什么这么写”。2. 从零搭起骨架Servlet JSP MySQL 的最小可行组合2.1 为什么不用 Spring Boot——先过“手写三层”的关再谈自动装配很多新手一上来就搜“Spring Boot 就业系统源码”结果跑起来发现全是注解、全是配置、全是 starter连web.xml都找不到在哪。这不是懒是跳过了最关键的工程直觉训练HTTP 请求怎么一层层穿过 Servlet → Service → DAO → 数据库每层该承担什么职责错误该在哪一层捕获并转成用户友好的提示所以本方案坚持用原生 Servlet JSP原因很实在Tomcat 启动快调试时改完 JSP 刷新即见效果不用等 Spring Boot 热加载web.xml里servlet-mapping明明白白告诉你“/student/list” 这个 URL 最终由哪个 Java 类处理HttpServletRequest和HttpServletResponse参数强制你思考“请求里带了什么参数响应要返回什么格式”没有Autowired你得亲手new StudentService()自然理解依赖注入的本质是解耦而不是魔法。提示这不是反对 Spring Boot而是建议——如果你简历上写“熟悉 Spring MVC”请确保你能徒手画出 DispatcherServlet 如何分发请求、HandlerAdapter 怎么调用 Controller 方法、ViewResolver 怎么找 JSP 文件。否则面试官问“如果 Controller 返回 String ‘success’框架怎么知道去渲染 success.jsp”你就只能答“它自己找的”。2.2 创建标准 Web 项目结构4 个核心目录不能少在 Eclipse 或 IDEA 中新建 Dynamic Web Project项目名job-info-system目标运行时选 Tomcat v8.5Dynamic web module version 选 3.1。完成后确保以下目录结构清晰存在这是后续所有代码落点的基础job-info-system/ ├── src/ # Java 源码包结构按功能分 │ ├── dao/ # 数据访问层StudentDao.java, CompanyDao.java │ ├── service/ # 业务逻辑层StudentService.java, CompanyService.java │ ├── servlet/ # 控制层StudentListServlet.java, LoginServlet.java │ └── entity/ # 实体类Student.java, Company.java, User.java ├── WebContent/ # Web 资源根目录Tomcat 部署后直接映射为 / │ ├── WEB-INF/ │ │ ├── web.xml # 全局配置入口Servlet 映射、过滤器、监听器都在这 │ │ └── lib/ # 放 mysql-connector-java-5.1.47.jar必须 │ ├── index.jsp # 首页欢迎页 or 登录页 │ ├── student/ # 学生相关页面 │ │ ├── list.jsp # 学生列表页含搜索框、分页 │ │ └── detail.jsp # 学生详情页显示投递记录 │ ├── company/ # 企业相关页面 │ │ └── add.jsp # 企业发布岗位页 │ └── common/ # 公共组件 │ └── header.jsp # 顶部导航栏含登录状态判断 └── db/ # 数据库脚本非必须但强烈建议放这里 └── init.sql # 创建表、插入测试数据注意WebContent是 Tomcat 认的 Web 根目录所有.jsp、.css、.js必须放在这里或其子目录src下的.java编译后自动输出到WebContent/WEB-INF/classes/WEB-INF/lib/下的 jar 包会被 Tomcat 加载进 classpath。2.3 写第一个 Servlet用最简方式验证环境通了创建src/servlet/LoginServlet.java内容如下package servlet; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class LoginServlet extends HttpServlet { private static final long serialVersionUID 1L; protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 1. 获取表单参数nameusername 和 namepassword String username request.getParameter(username); String password request.getParameter(password); // 2. 简单校验实际应查数据库此处先硬编码模拟 if (admin.equals(username) 123456.equals(password)) { // 3. 登录成功存 session重定向到首页 request.getSession().setAttribute(user, username); response.sendRedirect(request.getContextPath() /student/list.jsp); } else { // 4. 登录失败回传错误信息留在登录页 request.setAttribute(errorMsg, 用户名或密码错误); request.getRequestDispatcher(/index.jsp).forward(request, response); } } // GET 请求也走 doPost避免重复代码 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } }然后在WebContent/WEB-INF/web.xml中注册该 Servletservlet servlet-nameLoginServlet/servlet-name servlet-classservlet.LoginServlet/servlet-class /servlet servlet-mapping servlet-nameLoginServlet/servlet-name url-pattern/login/url-pattern /servlet-mapping最后WebContent/index.jsp写一个最简登录表单% page languagejava contentTypetext/html; charsetUTF-8 pageEncodingUTF-8% !DOCTYPE html html headmeta charsetUTF-8title就业信息系统 - 登录/title/head body h2就业信息系统登录/h2 form action${pageContext.request.contextPath}/login methodpost 用户名input typetext nameusername requiredbrbr 密码input typepassword namepassword requiredbrbr input typesubmit value登录 /form !-- 显示错误信息 -- % if (request.getAttribute(errorMsg) ! null) { % p stylecolor:red;% request.getAttribute(errorMsg) %/p % } % /body /html启动 Tomcat访问http://localhost:8080/job-info-system/输入admin/123456应跳转到list.jsp即使该文件还不存在也能看到 404证明 Servlet 已生效。这一步的意义在于确认你的开发环境、编译路径、Servlet 映射、JSP 解析全部打通。如果卡在这一步请立刻检查web.xml是否在WEB-INF下LoginServlet.class是否生成在WEB-INF/classes/servlet/Tomcat 日志里有没有ClassNotFoundException——这些才是真实项目里每天要面对的“血泪经验”。3. 数据层落地JDBC 封装与 MySQL 表设计3.1 表结构设计紧扣“就业”场景的 4 张核心表不要一上来就建 10 张表。本系统聚焦“学生找岗、企业招人、管理员审核”三个角色只需 4 张表即可覆盖主干流程且字段命名直白、无歧义表名字段说明关键约束t_studentid(PK),name,gender,major,phone,email,resume_url,create_timemajor用 VARCHAR(50)不建外键关联专业表简化t_companyid(PK),name,contact_person,phone,address,industry,create_timeindustry用 ENUM(IT, Finance, Education, Manufacturing)避免冗余字典表t_job_postingid(PK),company_id(FK),title,salary_range,requirement,publish_time,status(ENUM: published,closed)status控制岗位是否可投递比删记录更安全t_applicationid(PK),student_id(FK),job_id(FK),apply_time,status(ENUM: applied,interviewing,offer,rejected)复合索引(student_id, job_id)防止重复投递注意t_application是核心关联表它把学生、岗位、申请状态三者串起来。面试官最爱问“如果一个学生对同一岗位重复投递你怎么拦”答案就藏在这里——插入前查SELECT COUNT(*) FROM t_application WHERE student_id? AND job_id?有结果则拒绝。3.2 JDBC 工具类封装 getConnection 和 close避免连接泄漏创建src/utils/JDBCUtils.java这是整个数据层的基石。它不追求复杂连接池只做三件事统一获取连接、统一关闭资源、统一处理 SQLExceptionpackage utils; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class JDBCUtils { // 数据库连接参数实际项目应放 properties 文件 private static final String URL jdbc:mysql://localhost:3306/job_info?useUnicodetruecharacterEncodingUTF-8; private static final String USER root; private static final String PASSWORD 123456; static { try { // 加载 MySQL 驱动JDBC 4.0 可省略但显式写更稳妥 Class.forName(com.mysql.jdbc.Driver); } catch (ClassNotFoundException e) { throw new RuntimeException(MySQL Driver not found!, e); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } // 关闭 ResultSet、Statement、Connection按反向顺序 public static void close(ResultSet rs, Statement stmt, Connection conn) { 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) { /* 忽略 */ } } } }关键点说明URL中?useUnicodetruecharacterEncodingUTF-8是解决中文乱码的后悔药漏掉它后面所有INSERT都会变问号static { }块确保驱动只加载一次避免重复Class.forNameclose()方法里每个close()都套try-catch因为任一资源关闭失败都不应影响其他资源释放——这是生产环境的铁律。3.3 DAO 层实现以 StudentDao 为例展示标准 CRUD 模板创建src/dao/StudentDao.java严格遵循“一个方法只做一件事”原则package dao; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import entity.Student; import utils.JDBCUtils; public class StudentDao { // 查询所有学生带分页 public ListStudent findAll(int offset, int limit) { ListStudent list new ArrayList(); String sql SELECT id, name, gender, major, phone, email, resume_url, create_time FROM t_student ORDER BY create_time DESC LIMIT ?, ?; try (Connection conn JDBCUtils.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setInt(1, offset); // 第一个 ? 是 offset ps.setInt(2, limit); // 第二个 ? 是 limit ResultSet rs ps.executeQuery(); while (rs.next()) { Student s new Student(); s.setId(rs.getInt(id)); s.setName(rs.getString(name)); s.setGender(rs.getString(gender)); s.setMajor(rs.getString(major)); s.setPhone(rs.getString(phone)); s.setEmail(rs.getString(email)); s.setResumeUrl(rs.getString(resume_url)); s.setCreateTime(rs.getTimestamp(create_time)); list.add(s); } } catch (SQLException e) { e.printStackTrace(); // 实际项目应记日志而非打印 } return list; } // 根据 ID 查询单个学生 public Student findById(int id) { String sql SELECT id, name, gender, major, phone, email, resume_url, create_time FROM t_student WHERE id ?; try (Connection conn JDBCUtils.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setInt(1, id); ResultSet rs ps.executeQuery(); if (rs.next()) { Student s new Student(); s.setId(rs.getInt(id)); s.setName(rs.getString(name)); s.setGender(rs.getString(gender)); s.setMajor(rs.getString(major)); s.setPhone(rs.getString(phone)); s.setEmail(rs.getString(email)); s.setResumeUrl(rs.getString(resume_url)); s.setCreateTime(rs.getTimestamp(create_time)); return s; } } catch (SQLException e) { e.printStackTrace(); } return null; } // 插入新学生 public boolean insert(Student student) { String sql INSERT INTO t_student (name, gender, major, phone, email, resume_url, create_time) VALUES (?, ?, ?, ?, ?, ?, NOW()); try (Connection conn JDBCUtils.getConnection(); PreparedStatement ps conn.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS)) { ps.setString(1, student.getName()); ps.setString(2, student.getGender()); ps.setString(3, student.getMajor()); ps.setString(4, student.getPhone()); ps.setString(5, student.getEmail()); ps.setString(6, student.getResumeUrl()); int rows ps.executeUpdate(); if (rows 0) { // 获取自增主键 ResultSet rs ps.getGeneratedKeys(); if (rs.next()) { student.setId(rs.getInt(1)); } return true; } } catch (SQLException e) { e.printStackTrace(); } return false; } }逻辑说明所有方法都用try-with-resources确保Connection、PreparedStatement、ResultSet在方法结束时自动关闭insert()方法中PreparedStatement.RETURN_GENERATED_KEYS是关键它让ps.getGeneratedKeys()能拿到刚插入的id并赋值给student.setId()这样调用方无需再查一遍findAll()的LIMIT ?, ?是分页基础offset从 0 开始limit是每页条数前端传参时offset (pageNum - 1) * pageSizefindById()返回null表示未找到这是 DAO 层的契约Service 层据此决定抛异常还是返回默认值。4. 业务层与表现层让 JSP 页面真正“活”起来4.1 Service 层加校验、加事务、加日志把 DAO 串成业务流DAO 只管数据Service 才管“事”。以学生投递岗位为例src/service/ApplicationService.java需要协调StudentDao、JobPostingDao、ApplicationDao三方并保证原子性package service; import dao.ApplicationDao; import dao.JobPostingDao; import dao.StudentDao; import entity.Application; import entity.JobPosting; import entity.Student; import utils.JDBCUtils; import java.sql.Connection; import java.sql.SQLException; public class ApplicationService { private ApplicationDao appDao new ApplicationDao(); private JobPostingDao jobDao new JobPostingDao(); private StudentDao stuDao new StudentDao(); // 学生投递岗位含事务控制 public boolean applyForJob(int studentId, int jobId) { Connection conn null; try { conn JDBCUtils.getConnection(); conn.setAutoCommit(false); // 开启事务 // 1. 检查学生是否存在 Student student stuDao.findById(studentId); if (student null) { throw new RuntimeException(学生不存在ID studentId); } // 2. 检查岗位是否存在且已发布 JobPosting job jobDao.findById(jobId); if (job null || !published.equals(job.getStatus())) { throw new RuntimeException(岗位不可投递ID jobId); } // 3. 检查是否已投递过防重复 if (appDao.exists(studentId, jobId)) { throw new RuntimeException(您已投递过该岗位); } // 4. 插入申请记录 Application app new Application(); app.setStudentId(studentId); app.setJobId(jobId); app.setStatus(applied); boolean success appDao.insert(app); if (!success) { throw new RuntimeException(投递失败); } conn.commit(); // 全部成功提交事务 return true; } catch (Exception e) { if (conn ! null) { try { conn.rollback(); // 任一环节失败回滚 } catch (SQLException ex) { ex.printStackTrace(); } } e.printStackTrace(); return false; } finally { if (conn ! null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } } }关键点conn.setAutoCommit(false)是事务起点conn.commit()是终点中间任何throw都触发conn.rollback()appDao.exists(studentId, jobId)是防重核心其实现就是SELECT COUNT(*) FROM t_application WHERE student_id? AND job_id?所有校验学生存在、岗位有效、未重复投递都在 Service 层做DAO 层只负责“执行”不负责“判断”——这是分层清晰的标志。4.2 JSP 页面用 JSTL EL 替代 scriptlet让 HTML 干净可维护WebContent/student/list.jsp是学生列表页传统写法满屏% %易出错难维护。改用 JSTLJava Standard Tag Library和 ELExpression Language% page languagejava contentTypetext/html; charsetUTF-8 pageEncodingUTF-8% % taglib prefixc urihttp://java.sun.com/jsp/jstl/core% % taglib prefixfmt urihttp://java.sun.com/jsp/jstl/fmt% !DOCTYPE html html head meta charsetUTF-8 title学生列表/title style table { border-collapse: collapse; width: 100%; } th, td { border: 1px solid #ccc; padding: 8px; text-align: left; } .btn { background: #007bff; color: white; padding: 4px 8px; text-decoration: none; } /style /head body h2学生信息列表/h2 !-- 搜索表单 -- form action${pageContext.request.contextPath}/student/list methodget 姓名input typetext namename value${param.name} 专业input typetext namemajor value${param.major} input typesubmit value搜索 /form !-- 数据表格 -- table tr thID/thth姓名/thth性别/thth专业/thth电话/thth邮箱/thth操作/th /tr c:choose c:when test${empty students} trtd colspan7暂无学生数据/td/tr /c:when c:otherwise c:forEach items${students} varstu tr td${stu.id}/td td${stu.name}/td td${stu.gender}/td td${stu.major}/td td${stu.phone}/td td${stu.email}/td td a classbtn href${pageContext.request.contextPath}/student/detail?id${stu.id}详情/a /td /tr /c:forEach /c:otherwise /c:choose /table !-- 分页导航 -- c:if test${not empty students} div 共 ${total} 条当前第 ${currentPage} 页共 ${totalPages} 页 c:if test${currentPage 1} a href${pageContext.request.contextPath}/student/list?currentPage${currentPage-1}name${param.name}major${param.major}上一页/a /c:if c:if test${currentPage totalPages} a href${pageContext.request.contextPath}/student/list?currentPage${currentPage1}name${param.name}major${param.major}下一页/a /c:if /div /c:if /body /htmlEL 表达式${students}对应 Servlet 中request.setAttribute(students, studentList)c:forEach替代% for(...) { %c:choose替代% if...else %。这样写的好处是HTML 工程师可以只改样式Java 工程师只改后台逻辑互不干扰。如果你还在写% student.getName() %那离被团队淘汰就不远了。4.3 Servlet 串联把请求、业务、视图串成一条线src/servlet/StudentListServlet.java是典型的 MVC 控制器它只做三件事取参 → 调 Service → 设属性 → 转发package servlet; import service.StudentService; import entity.Student; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.List; public class StudentListServlet extends HttpServlet { private StudentService stuService new StudentService(); protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 1. 获取分页和搜索参数 String name request.getParameter(name); String major request.getParameter(major); String currentPageStr request.getParameter(currentPage); int currentPage currentPageStr null ? 1 : Integer.parseInt(currentPageStr); int pageSize 10; int offset (currentPage - 1) * pageSize; // 2. 调用 Service 获取数据 ListStudent students stuService.findByNameAndMajor(name, major, offset, pageSize); int total stuService.countByNameAndMajor(name, major); // 3. 计算分页参数 int totalPages (int) Math.ceil((double) total / pageSize); // 4. 设置请求属性转发到 JSP request.setAttribute(students, students); request.setAttribute(total, total); request.setAttribute(currentPage, currentPage); request.setAttribute(totalPages, totalPages); request.setAttribute(name, name); // 保持搜索条件 request.setAttribute(major, major); request.getRequestDispatcher(/student/list.jsp).forward(request, response); } }注意request.setAttribute()的 key如students必须和 JSP 中${students}完全一致forward()是服务器端跳转URL 不变适合 MVCsendRedirect()是客户端跳转URL 变适合登录成功后跳转。选错会导致“页面空白”或“参数丢失”——这是新手最常翻车的点。5. 避坑指南那些让你加班到凌晨的“小问题”5.1 中文乱码不只是charsetUTF-8还有这 3 个地方要同步现象MySQL 里存的是中文但 JSP 页面显示为??或者表单提交后数据库里变成????。原因乱码是链条断裂任何一个环节编码不一致都会导致。常见断点有 3 个JSP 页面声明% page contentTypetext/html; charsetUTF-8 %缺少或写成GBKTomcat 配置conf/server.xml中Connector标签没加URIEncodingUTF-8导致 GET 请求参数乱码MySQL 连接 URLjdbc:mysql://...?useUnicodetruecharacterEncodingUTF-8漏掉或写错。解决检查web.xml顶部是否有jsp-configjsp-property-groupurl-pattern*.jsp/url-patternpage-encodingUTF-8/page-encoding/jsp-property-group/jsp-configTomcat 8 可省略但加上更保险修改server.xmlConnector port8080 protocolHTTP/1.1 connectionTimeout20000 redirectPort8443 URIEncodingUTF-8/运行SHOW VARIABLES LIKE character_set%;确认 MySQL 服务端编码为utf8mb4建表时用CHARSETutf8mb4 COLLATEutf8mb4_unicode_ci。提示用curl -v http://localhost:8080/job-info-system/student/list?name张三查看响应头Content-Type是否含charsetUTF-8这是最终判决依据。5.2 Session 失效登录后刷新就登出不是浏览器问题现象登录成功跳转到list.jsp但刷新页面又回到登录页session.getAttribute(user)为 null。原因Session 默认依赖 Cookie而 Cookie 的Path属性默认是当前应用路径如/job-info-system但如果你在web.xml里配了session-configcookie-confighttp-onlytrue/http-only/cookie-config/session-config却没设path或前端 JS 用document.cookie手动删了 Cookie就会失效。解决在web.xml中显式设置 Session Cookie 路径session-config cookie-config http-onlytrue/http-only securefalse/secure !-- 开发环境设 false -- path//path !-- 关键设为 /使 Cookie 对整个域名有效 -- /cookie-config /session-config检查浏览器开发者工具 → Application → Cookies确认JSESSIONID的Path是/而不是/job-info-system不要在 JS 里执行document.cookie JSESSIONID; expiresThu, 01 Jan 1970 00:00:00 GMT;这是自杀行为。5.3 JSP 编译失败org.apache.jasper.JasperException: Unable to compile class for JSP现象启动 Tomcat 后访问 JSP报错Unable to compile class for JSP堆栈指向org.apache.jasper.compiler.TldLocationsCache。原因JSP 编译依赖tools.jarJDK 提供但 Tomcat 9 默认不再自动加载它或者web.xml的web-app版本与 Tomcat 不匹配如 Tomcat 8.5 用version4.0会失败。解决确认你用的是JDK 8不是 JRE并在 Tomcatbin/catalina.batWindows或catalina.shLinux中添加set JAVA_HOMEC:\Program Files\Java\jdk1.8.0_202Windowsexport JAVA_HOME/usr/lib/jvm/java-8-openjdk-amd64Linuxweb.xml头部必须匹配 Tomcat 版本Tomcat 8.5 对应web-app_3_1.xsd即web-app xmlnshttp://xmlns.jcp.org/xml/ns/javaee xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd version3.1删除work/Catalina/localhost/job-info-system/目录Tomcat 的 JSP 编译缓存重启 Tomcat。5.4 MySQL 连接超时Communications link failure不是网络问题现象系统空闲 8 小时后首次访问报Communications link failure但手动 ping 数据库 IP 是通的。原因MySQL 服务端默认wait_timeout288008 小时空闲连接会被主动断开而你的JDBCUtils.getConnection()每次都复用旧连接导致conn.isValid()为 false。解决在JDBCUtils.getConnection()中增加有效性检查public static Connection getConnection() throws SQLException { Connection conn DriverManager.getConnection(URL, USER, PASSWORD); if (!conn.isValid(2)) { // 检查连接是否有效超时 2 秒 throw new SQLException(Invalid database connection); } return conn; }更彻底的方案在 MySQL 配置文件my.cnf中加大wait_timeout[mysqld] wait_timeout 288000 # 80 小时 interactive_timeout 288000生产环境必须用 HikariCP 等连接池它内置心跳检测但课程设计阶段加isValid()就够用。5.5 JSTL 标签不解析页面显示c:forEach原样文本现象JSP 里写了c:forEach但浏览器源码里就是c:forEach字符串没被解析成 HTML。原因JSTL 库没引入或引入版本与 JSP 版本不兼容。解决下载jstl-1.2.jar和standard-1.1.2.jarJSP 2.0 用jstl-1.2.jar即可放入WEB-INF/lib/确保web.xml是 Servlet 2.4 规范即version2.4或更高否则taglib无法识别JSP 顶部必须有% taglib prefixc urihttp://java.sun.com/jsp/jstl/core%URI 不能写错常见错写成http://jakarta.apache.org/taglibs/core清理 Tomcatwork目录重启——JSP 编译缓存有时会记住旧的 taglib 解析失败状态。6. 进阶技巧让系统真正“可用”不止于交差6.1 Excel 导出用 Apache POI 生成带样式的岗位汇总表很多同学以为“导出 Excel”就是拼 CSV但企业真正在意的是列宽自适应、标题居中加粗、数字右对齐、日期格式化、表头冻结。Apache POI 是唯一选择。在src/servlet/ExportJobServlet.java中package servlet; import dao.JobPostingDao; import entity.JobPosting; import org.apache.poi.ss.usermodel.*; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.OutputStream; import java.net.URLEncoder; import java.text.SimpleDateFormat; import java.util.List; public class ExportJobServlet extends HttpServlet { private JobPostingDao jobDao new JobPostingDao(); protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ListJobPosting jobs jobDao.findAll(); // 获取所有岗位 // 1. 创建工作簿和工作表 Workbook workbook new XSSFWorkbook(); Sheet sheet workbook.createSheet(岗位汇总); // 2. 创建表头样式居中、加粗、背景色 CellStyle headerStyle workbook.createCellStyle(); Font headerFont workbook.createFont(); headerFont.setBold(true); headerStyle.setFont(headerFont p a hrefhttps://download.csdn.net/download/weixin_43894652/87724158 stylecolor:#ec7500;font-size:14px; 本文还有配套的精品资源点击获取 /a img altmenu-r.4af5f7ec.gif srchttps://csdnimg.cn/release/wenkucmsfe/public/img/menu-r.4af5f7ec.gif stylewidth:16px;margin-left:4px;vertical-align:text-bottom;cursor:text; /p
返回列表