
简介这是一份基于JavaWebJSPMySQL的B/S架构人才招聘网站完整毕业设计项目面向计算机相关专业毕业生及课程设计学习者可用于快速搭建招聘类业务系统并完成配套文档。项目涵盖前台首页设计、网站导航、职位信息列表、用户注册、后台登录、职位信息管理及退出后台等核心模块能够支撑求职者浏览职位与管理员维护数据的典型业务场景。资源共930个文件以JSP页面、Java类、HTML/CSS/JS前端代码为主并包含MySQL数据库脚本、jar依赖包、XML配置及图片图标等文件压缩包整体19.32MB目录结构清晰便于导入运行。目前已有206人学习下载。随包附带的完整设计文档有助于理解系统架构与功能实现适合作为毕业设计、课程设计或JavaWeb自学实践的参考范本。1. 这份 JavaWebJSPMySQL 招聘网站资源先跑通再改毕业设计交完初稿导师让你现场演示浏览器却一直转圈最后甩给你一个 500——这个画面做 JavaWeb 课设的同学应该都不陌生。这份资源就是典型的 JavaWebJSPMySQL 三件套项目BS 架构下前台展示职位、后台管理职位、用户注册登录一条线走完没有 Spring 全家桶包装ServletJDBC 全是裸写恰恰是毕设和课设最常用的形态。适合两类人一类是拿到源码但被环境卡住、想尽快跑起来看到页面的人另一类是准备把项目拆开改成自己题目的学生。它能解决的核心问题是别把时间耗在踩版本坑上把精力留给改功能和凑字数。2. 环境与工程导入JDK/Tomcat/MySQL 版本对齐的兼容性清单2.1 版本选型JDK 8 Tomcat 8.5 MySQL 5.7 的稳定组合先别急着装最新版。这个项目是原生 JSPServlet 结构对运行环境非常敏感版本错配是九成启动失败的根源。我测试过一套兼容性最好的组合JDK 8 Tomcat 8.5 MySQL 5.7。三者都是老技术栈的黄金版本网上资料最多出问题随便一搜就有答案。组件推荐版本选型理由JDK1.8JSP 编译稳定Tomcat 8.5 官方支持的最佳基线Tomcat8.5.x原生 Servlet/JSP 容器配置简单不挑部署方式MySQL5.7.x驱动类名com.mysql.jdbc.Driver认证方式默认无兼容问题如果你非要用 JDK 17 配 Tomcat 10大概率会碰到两个麻烦一是 JSP 编译报PWC6117之类的文件访问错误二是 Tomcat 10 把包名从javax.servlet改成了jakarta.servlet项目里的import javax.servlet.*会全部标红。新手没必要在这个地方自找麻烦。MySQL 8.0 也能跑但连接串要多加参数后面的避坑章节我会单独讲。2.2 导入前检查环境三条命令确认基线在你双击 IDEA 图标之前先打开命令行把环境确认一遍。这一步能省掉后面一半的绝望。java -version 21 | head -n 1 mysql --version catalina.bat version 2nul || catalina.sh version 2/dev/null || echo Tomcat 未配置环境变量第一条看 JDK 版本号确认不是 17 或 21第二条看 MySQL 版本第三条是查 Tomcat 版本Windows 用catalina.batLinux/macOS 用catalina.sh。如果第三条直接 echo 了说明你的 Tomcat 没配环境变量或者根本没解压。导入 IDEA 时可以直接指定 Tomcat 安装目录不强制配环境变量但命令行查版本是排查问题最快的路子。检查完再进下一步别急着把源码拖进 IDE。2.3 看懂资源包结构再决定从哪里下手下载完别急着解压就跑先用一分钟搞清楚目录里放了什么。这类毕设资源的典型结构是这样的目录/文件作用你要做的src/Java 源码Servlet、Service、Dao 都在这导入后标记为源码根目录WebRoot/或web/JSP 页面、静态资源、WEB-INF配置为 Web 资源目录sql/或database/建库建表脚本和初始数据先在 MySQL 里执行文档/开题报告、任务书、论文正文抄作业也要先读一遍IDEA 导入时选Open找到项目根目录等 Maven 或普通项目结构识别完成。这个项目如果是普通工程没 pom.xml就用New - Module from Existing Sources导入然后把WebRoot右键Add as Frameworks Support标记成 Web 目录。部署方式选war exploded开发时不用反复打 war 包改完 JSP 直接刷新浏览器就能看到效果这点对课设调页面非常重要。2.4 jdbc.properties 与数据库连接串的细节项目里一般会有一个jdbc.properties或者直接在DBUtil.java里写死了连接参数。我习惯集中管理配置文件方式最省心。jdbc.drivercom.mysql.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/recruit?useUnicodetruecharacterEncodingUTF-8 jdbc.usernameroot jdbc.password你的数据库密码jdbc.driver用的是com.mysql.jdbc.Driver这是 MySQL 5.x 的驱动类名对应驱动包mysql-connector-java-5.1.x.jar。如果你换用了 MySQL 8.x这里必须改成com.mysql.cj.jdbc.Driver并且 URL 末尾最好追加serverTimezoneAsia/Shanghai否则时间字段会偏移。characterEncodingUTF-8这一项是中文不出乱码的底线建议不要动。密码字段是唯一需要改成你自己 MySQL 密码的地方我见过有人运行报错Access denied for user排查半天发现是密码没改。数据库连接串验证建议用命令行先试一次确保 MySQL 服务和密码没问题再启动 Tomcat。mysql -uroot -p能正常进入mysql提示符再回到 IDEA 启动项目。否则后端报Communications link failure时你会分不清是项目问题还是 MySQL 没启动。3. 数据库设计六张表的外键关系与联表查询落地3.1 六张表分别管什么这个招聘网站的表结构不大但角色划分得很清楚。六张表分别是t_user前台注册用户、admin后台管理员、company企业信息、job职位信息、resume简历、apply投递记录。前四张表是核心简历和投递是扩展功能但设计风格保持一致。看表结构时重点理解一点职位job表通过company_id挂靠企业投递apply表通过user_id和job_id把两边串起来。表名核心字段职责t_userusername, password, phone前台求职者注册登录adminusername, password后台管理人员登录companyname, address, industry招聘企业信息jobtitle, company_id, salary, description职位发布与展示resumeuser_id, education, experience简历信息applyuser_id, job_id, status投递行为记录3.2 建库建表字符集、外键和时间默认值一次说清打开sql目录下的脚本如果你决定手动重建下面这段 SQL 是最小可运行版本。字符集统一用utf8mb4别用utf8后者存不了特殊字符表情虽然招聘网站用不上但统一标准没坏处。CREATE DATABASE recruit DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_general_ci; USE recruit; CREATE TABLE company ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100) NOT NULL, address VARCHAR(200), industry VARCHAR(50) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE job ( id INT PRIMARY KEY AUTO_INCREMENT, title VARCHAR(100) NOT NULL, company_id INT NOT NULL, salary VARCHAR(50), description TEXT, publish_time DATETIME DEFAULT CURRENT_TIMESTAMP, CONSTRAINT fk_job_company FOREIGN KEY (company_id) REFERENCES company(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE t_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL, real_name VARCHAR(50), phone VARCHAR(20), create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;三条值得注意的点第一publish_time用了DEFAULT CURRENT_TIMESTAMP插入时不用手动写时间MySQL 会自动补当前时间这个细节在论文里可以当设计亮点写一句。第二job表的company_id加了外键约束保证了不会有孤儿职位——但代价是删除企业时会被外键拦住。第三t_user的username加了UNIQUE约束注册时重复用户名直接抛异常比在代码里先查一遍再插要可靠得多。引擎统一InnoDB支持事务和外键MyISAM 在这两类需求上都支持不了。3.3 联表查询与分页职位列表为什么必须 JOIN前台职位列表展示时页面上要同时显示职位标题、薪资、企业名称。企业名称在company表里职位信息在job表里这意味着必须联表。用LEFT JOIN而不是INNER JOIN考虑的是边界情况如果企业信息被删除职位仍然要在列表里显示出来挂一个空企业名总比整条记录消失好。SELECT j.id, j.title, j.salary, c.name AS company_name, j.publish_time FROM job j LEFT JOIN company c ON j.company_id c.id ORDER BY j.publish_time DESC LIMIT 0, 10;LIMIT 0, 10的语义是跳过 0 条取 10 条对应第一页数据。分页参数换算规则是第 n 页的偏移量 (n - 1) * pageSize。比如每页 10 条第三页就是LIMIT 20, 10。这个公式写在哪一层都行但千万别在 JSP 里现算Servlet 里算好了传过去JSP 只负责显示。列表页还要把总页数算出来常见做法是先查 countSELECT COUNT(*) FROM job;然后前端循环输出页码。这套思路是原生 JSP 项目分页的标配面试被问到也是这套回答。3.4 密码不要存明文MD5 处理与管理员表后台管理员表和前台用户表结构类似但密码存储必须做哈希处理。毕设答辩老师最喜欢问的问题是“密码安全怎么考虑的”如果你回答“直接存数据库”印象分会掉一半。用 MD5 虽然不算强加密但作为课程设计足够说明你有安全意识。CREATE TABLE admin ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL ); INSERT INTO admin (username, password) VALUES (admin, MD5(admin123));注意密码字段长度要足够存放 MD5 字符串MD5 固定输出 32 位十六进制所以VARCHAR(64)留了余量是为了以后升级 SHA-256。Java 端登录校验时把用户输入的密码取 MD5 后再拿 SQL 比对而不是明文查询。注册功能同理密码入库前先DigestUtils.md5DigestAsHex(password.getBytes())Spring 的spring-core里带这个方法没引 Spring 就自己写一个工具类网上模板很多。4. 前后台联动职位列表展示到后台增删改的完整链路4.1 前台职位列表Servlet 取数、JSP 渲染的分工这个项目的经典链路是浏览器请求job/listServlet 接收参数、调用 DAO 查询、把结果放到 request 作用域再forward到 JSP 渲染 HTML。职责分离的准则是 JSP 里尽量不写 Java 代码只做显示。下面这段是职位列表 Servlet 的核心逻辑WebServlet(/job/list) public class JobListServlet extends HttpServlet { protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding(UTF-8); int page 1; String pageStr req.getParameter(page); if (pageStr ! null !pageStr.isEmpty()) { page Integer.parseInt(pageStr); } int pageSize 10; JobDao dao new JobDao(); ListJob list dao.findByPage((page - 1) * pageSize, pageSize); int total dao.count(); req.setAttribute(list, list); req.setAttribute(currentPage, page); req.setAttribute(totalPage, (total pageSize - 1) / pageSize); req.getRequestDispatcher(/jobList.jsp).forward(req, resp); } }分页参数在 Servlet 层完成计算(page - 1) * pageSize算偏移量(total pageSize - 1) / pageSize是向上取整的总页数公式。forward是请求转发地址栏不会变request 域里的数据 JSP 能直接拿到如果用sendRedirect数据会丢必须放 session那是另一种用法。JSP 侧用 JSTL 循环渲染注意项目要带上jstl.jar和standard.jar不然c:forEach不生效。table border1 c:forEach items${list} varjob tr td${job.title}/td td${job.companyName}/td td${job.salary}/td tdfmt:formatDate value${job.publishTime} patternyyyy-MM-dd//td /tr /c:forEach /table${job.companyName}取的是 DAO 查询时映射好的属性对应 SQL 里c.name AS company_name的别名。别直接在 JSP 里写job.company.name如果是联表对象还好但原生 JDBC 映射成的是扁平化对象没有嵌套对象可用。4.2 关键字搜索LIKE 参数化与页码重置职位列表页通常会放一个搜索框按职位名称模糊搜索。常见误用是直接字符串拼接 SQL比如WHERE title LIKE % keyword %——用户输入一个单引号你的 SQL 就断了。参数化是必须的。public ListJob searchByTitle(String keyword, int offset, int pageSize) { String sql SELECT j.id, j.title, j.salary, c.name AS company_name FROM job j LEFT JOIN company c ON j.company_id c.id WHERE j.title LIKE ? ORDER BY j.publish_time DESC LIMIT ?, ?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, % keyword %); ps.setInt(2, offset); ps.setInt(3, pageSize); // 执行查询并封装返回 } }PreparedStatement的占位符能在数据库端做参数转义单引号、反斜杠都会被处理掉这是防线。另外要注意MySQL 的LIMIT参数不能用字符串占位符拼接必须用setInt设置否则部分版本会报语法错误。搜索时分页参数要从第 1 页重新开始计数不然用户搜完词之后跳到第 5 页而第 5 页可能只有 2 条数据看起来就像系统 bug。4.3 后台登录、职位增删改与退出后台登录的流程是表单提交用户名密码Servlet 里做校验成功就把管理员对象放进 session后面每个后台页面前都查这个 session 判断是否登录。WebServlet(/admin/login) public class AdminLoginServlet extends HttpServlet { protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String username req.getParameter(username); String password DigestUtils.md5DigestAsHex( req.getParameter(password).getBytes()); Admin admin adminDao.findByUsernameAndPassword(username, password); if (admin ! null) { req.getSession().setAttribute(admin, admin); resp.sendRedirect(req.getContextPath() /admin/job/list); } else { req.setAttribute(error, 用户名或密码错误); req.getRequestDispatcher(/admin/login.jsp).forward(req, resp); } } }登录成功用sendRedirect作用是让浏览器重新发起一次全新请求地址栏变成/admin/job/list同时也能避免刷新页面时重复提交表单。密码比较用的是 MD5 加密后再查库如果数据库里管理员记录是通过 SQL 脚本插入的脚本里也要写MD5(admin123)保持一致。职位新增和更新共用一个编辑页区别是有没有id参数。新增时id为空执行INSERT修改时按id执行UPDATE。这里有一个容易翻车的点JDBC 预编译时参数顺序必须和 SQL 语句里的占位符顺序完全一致我见过参数写反导致薪资和职位名称互换的情况调试时画面很诡异。删除职位不建议用 GET 请求因为浏览器地址栏直接访问就会触发删除爬虫扫到也可能误删。规范做法是包一层POST /admin/job/deleteWebServlet(/admin/job/delete) public class DeleteJobServlet extends HttpServlet { protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { int id Integer.parseInt(req.getParameter(id)); jobDao.deleteById(id); resp.sendRedirect(req.getContextPath() /admin/job/list); } }页面上的删除按钮放进一个form methodpost里而不是a hrefdelete?id1。退出后台的核心是销毁 sessionreq.getSession().invalidate(); resp.sendRedirect(req.getContextPath() /login.jsp);invalidate()会把 session 里所有属性清掉并作废会话比单独removeAttribute(admin)更彻底。退出之后点浏览器后退按钮能否看到管理页取决于页面有没有做 session 校验这一块在避坑章节展开。5. 常见问题排查五条把新人卡住的踩坑记录5.1 富文本编辑器目录混入 .afp 文件上传功能神秘失效现象职位描述编辑器加载出来了但点上传图片没反应浏览器 F12 能看到 404解压源码包时发现编辑器目录下躺着一堆sample01.afp、fckeditor.afp、class_upload.asp这类文件。原因这个项目带的是 FCKeditor 编辑器原始压缩包里混入了其他平台的上传脚本.asp是 IIS 平台用的打包的人没清理导致 JSP 环境下的上传 connector 路径指向错误。解决确认编辑器目录下的connector指向server/connector/jsp而不是asp把无用的.afp、.asp文件删掉避免混淆。如果编辑器完全起不来直接换用CKEditor 3.x替代接口兼容性最好网上有现成的替换步骤。5.2 中文乱码请求、响应、数据库三层都要指定编码现象注册用户输入“张三”插入 MySQL 后变成“å¼ ä¸‰”或“???”职位名称同样乱码。原因三层各少一步请求没设编码request.getParameter()拿到的是 ISO-8859-1 解码的乱码数据库连接串没有characterEncodingUTF-8表字符集不是utf8mb4。解决写一个过滤器统一设置请求和响应编码顺序很重要必须放在chain.doFilter()之前WebFilter(/*) public class EncodingFilter implements Filter { public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { req.setCharacterEncoding(UTF-8); resp.setCharacterEncoding(UTF-8); resp.setContentType(text/html;charsetUTF-8); chain.doFilter(req, resp); } }同时确保jdbc.url里带useUnicodetruecharacterEncodingUTF-8。三者对齐后乱码基本绝迹。如果改完仍乱检查web.xml中 filter 的url-pattern是否是/*漏掉 JSP 路径等于没配。5.3 Tomcat 8080 端口被占用启动无疾而终现象IDEA 控制台启动 Tomcat 后立刻报Address already in use: JVM_Bind或者日志里出现BindException。原因之前某次运行没正常关闭 Tomcat进程还占用着 8080或者系统里有其他程序比如某个本地服务抢先用了这个端口。解决Windows 下用命令行找进程再干掉它netstat -ano | findstr 8080 taskkill /F /PID 12345第一行会打印占用 8080 的进程 PID第二行强制结束该进程。如果 8080 上跑的是别的业务服务不想动它就改 Tomcat 的端口。改conf/server.xml里Connector port8080为 8081但要注意访问 URL 同步改成http://localhost:8081/项目名/。改完必须重启 Tomcat 才生效。5.4 MySQL 8.0 认证插件导致连接失败现象项目启动后控制台报java.sql.SQLException: Public Key Retrieval is not allowed或Communications link failure。原因MySQL 8.x 默认认证插件是caching_sha2_password项目里用的是老驱动 5.1.x两者不兼容。新驱动做 RSA 公钥传输时需要额外的连接参数放行。解决最省事的方案是换回 MySQL 5.7。如果你已经装了 8.x 不想重装把jdbc.properties改成下面这份jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/recruit?useSSLfalseallowPublicKeyRetrievaltrueserverTimezoneAsia/ShanghaicharacterEncodingUTF-8useSSLfalse是跳过 SSL 握手本地开发没必要折腾证书allowPublicKeyRetrievaltrue是允许客户端从服务端获取公钥serverTimezone必填否则报时区错误。别把两个驱动类名混着用5.7 的库用 8.x 驱动也可能出问题。5.5 退出后台后点浏览器后退管理页面还能看现象点了“退出系统”按钮地址栏跳回登录页但按浏览器后退键职位管理页面原封不动出现在眼前。原因浏览器后退时优先从本地缓存渲染页面如果后台页面代码没做 session 校验退出后这个缓存页面依然能显示。解决在每个后台 JSP 页面的最顶部加会话校验短路未登录请求。% Object admin session.getAttribute(admin); if (admin null) { response.sendRedirect(request.getContextPath() /login.jsp); return; } %return保证后续 HTML 不再输出。这能阻断缓存页面的内容渲染因为你虽然看到了 URL但 JSP 已经重定向了。更好的方案是写一个AdminAuthFilter统一拦截/admin/*路径比在每个页面里贴代码更优雅。如果用了 filter 还要配合response.setHeader(Cache-Control, no-store)防止浏览器缓存后台页面。6. 部署验证与扩展一张验收清单和两个加分项6.1 打包部署的两种方式和一条验证命令开发环境用war exploded跑没问题但要给导师演示或部署到服务器建议打成 war 包。IDEA 的操本文还有配套的精品资源点击获取