ARTICLE DETAIL

资讯详情

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

JSP人事管理系统源码实战:从环境搭建到二次开发避坑指南

JSP人事管理系统源码实战:从环境搭建到二次开发避坑指南 简介这是一份面向Java Web初学者与课程设计学习者的完整项目资料围绕JSP技术构建人事管理系统适合需要完成毕业设计、课程实训或想系统理解Java Web开发流程的读者。压缩包为zip格式约1.11MB内含项目报告、任务书、中英文翻译文档及完整源代码报告覆盖需求分析、系统架构、数据库设计与测试总结任务书则给出目标与进度安排源代码包含Servlet、JSP页面及MVC各层组件。项目涉及员工信息管理、考勤记录、薪资计算与绩效评估等核心模块读者可从中学习JSP内置对象、Servlet协同、JDBC数据库操作、MVC设计模式以及HTML、CSS与JavaScript的前端展示并理解ER模型与软件工程方法。目前已有54人学习下载适合作为Java Web入门到实践的参考案例。1. 从一份 JSP 人事管理系统源码说起谁还在用它怎么跑起来招聘季一过总有人翻出硬盘里那份基于jsp的人事管理系统设计与实现(源代码项目报告).zip。它多半是课程设计或毕设产物技术栈是 JSP Servlet JDBC MySQL跑在 Tomcat 上没有 Spring没有前后端分离页面里还嵌着% %脚本片段。很多人第一反应是这玩意儿过时了但真到要交报告、要答辩、要在一个老项目上做二次开发时它反而是最省事的起点——依赖少、结构直白、改一个字段能立刻看到效果。这篇笔记不吹这套架构多先进只讲清楚三件事这套 JSP 人事管理系统到底由哪些模块组成、怎么在本地把它跑通、以及改需求时哪些地方最容易翻车。适合手里已经拿到源码包、想快速复现的在校生也适合被派去维护一套老 JSP 系统的 Java 开发。读完你应该能独立完成环境搭建、数据库导入、登录验证和一次完整的员工信息增删改查。2. 拆开这套 JSP 人事管理系统模块划分与技术选型2.1 典型目录结构与各层职责拿到源码包先别急着导入 IDE用文件管理器把目录展开看一遍心里有个地图。JSP 时代的项目结构高度雷同常见形态是这样hrms/ ├── src/ # Java 源码 │ └── com/hrms/ │ ├── dao/ # 数据库访问层写 JDBC │ ├── entity/ # 实体类和表字段一一对应 │ ├── servlet/ # 控制器接收请求、调 DAO、转发 │ └── util/ # DBUtil 等工具类 ├── WebContent/ 或 web/ │ ├── css/ js/ images/ │ ├── WEB-INF/ │ │ ├── web.xml # Servlet 与过滤器映射 │ │ └── lib/ # mysql-connector、jstl 等 jar │ ├── login.jsp │ ├── index.jsp │ └── emp/ dept/ salary/ # 按模块分的页面 └── sql/hrms.sql # 建库建表脚本dao层是这套系统的核心所有 SQL 都堆在这里。servlet层负责取参数、做简单校验、调 DAO、把结果塞进 request 再forward到 JSP。JSP 页面既做展示又做表单提交脚本片段里偶尔还会直接调 DAO——这是老项目的通病改的时候要留意。提示如果src下没有dao只有servlet说明 SQL 直接写在 Servlet 里了这种项目改动量会大很多先做好心理准备。2.2 为什么是 JSP Servlet JDBC 这套组合从今天的视角看这套组合确实笨重但它有三个绕不开的优点。第一零框架依赖只要 JDK 和 Tomcat 就能跑不需要配 Maven 私服、不需要联网拉依赖对网络受限的机房环境特别友好。第二请求流转全在明面上浏览器发一个请求web.xml里查映射进 Servlet调 DAO转发 JSP整条链路没有黑匣子调试时打断点一目了然。第三和项目报告这种交付物天然契合报告里画个三层架构图、贴几段 Servlet 代码逻辑自洽。选型上真正需要你决策的只有两处数据库连接方式和页面数据展示方式。连接方式我一般直接用DBUtil手写DriverManager不引入连接池因为课程设计级别的并发量根本压不出连接池的价值反而多一个配置文件多一个出错点。数据展示优先用 JSTL 的c:forEach比脚本片段干净但如果源码里已经全是% %就别强行重构保持一致更省事。2.3 数据库表设计员工、部门、薪资三张主表怎么关联人事系统的表设计万变不离其宗核心是三张表加一张用户表。下面是我从这类项目里总结出的最小可用结构字段名按常见命名习惯给表名关键字段说明t_userid, username, password, role登录账号role 区分管理员/普通用户t_deptdept_id, dept_name, dept_head部门表dept_head 存负责人姓名t_employeeemp_id, emp_name, gender, birthday, dept_id, position, phone, entry_date员工主表dept_id 外键关联部门t_salarysalary_id, emp_id, base_salary, bonus, pay_month薪资表emp_id 关联员工关联关系是t_employee.dept_id → t_dept.dept_idt_salary.emp_id → t_employee.emp_id。查询员工列表时要LEFT JOIN t_dept把部门名带出来否则页面上只能显示一个数字 ID体验很差。这里有个容易忽略的点t_employee的dept_id建议允许为 NULL因为新员工入职时可能还没分配部门如果设成 NOT NULL插入就会失败。CREATE TABLE t_employee ( emp_id INT PRIMARY KEY AUTO_INCREMENT, emp_name VARCHAR(50) NOT NULL, gender CHAR(2) DEFAULT 男, birthday DATE, dept_id INT, position VARCHAR(50), phone VARCHAR(20), entry_date DATE, CONSTRAINT fk_emp_dept FOREIGN KEY (dept_id) REFERENCES t_dept(dept_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;建表语句里utf8mb4别写成utf8否则员工姓名里出现生僻字或 emoji 会插入失败。外键约束在课程设计里可以保留显得规范但如果后续要做批量导入外键会拖慢速度那时再考虑去掉。3. 本地跑通的最小步骤从建库到登录成功3.1 环境准备与版本匹配跑这套系统需要三样东西JDK、Tomcat、MySQL。版本搭配是新手最容易翻车的地方我踩过的坑是 JDK 17 配 Tomcat 8 直接启动报错。稳妥组合如下组件推荐版本备注JDK8 或 11JSP 老项目对高版本 JDK 兼容差Tomcat8.5 或 9.0不要用 Tomcat 10包名从 javax 变 jakarta全崩MySQL5.7 或 8.08.0 要改驱动类和连接串驱动 jarmysql-connector-java 5.1.x 或 8.0.x与 MySQL 版本对应Tomcat 10 的坑必须单独强调它把javax.servlet.*全部换成了jakarta.servlet.*而这份源码里的 import 语句几乎不可能已经改过部署上去就是一片ClassNotFoundException。所以要么用 Tomcat 9要么手动全局替换包名后者工作量大且容易漏。3.2 导入数据库并修改连接配置第一步用命令行或 Navicat 把hrms.sql导进去mysql -u root -p -e CREATE DATABASE hrms DEFAULT CHARSET utf8mb4; mysql -u root -p hrms sql/hrms.sql第二步找到DBUtil.java改连接参数。这是全项目唯一需要改配置的地方public class DBUtil { // MySQL 8.0 用 com.mysql.cj.jdbc.Driver5.7 用 com.mysql.jdbc.Driver private static final String DRIVER com.mysql.cj.jdbc.Driver; // 8.0 必须带时区和 SSL 参数否则连接报错 private static final String URL jdbc:mysql://localhost:3306/hrms?useUnicodetruecharacterEncodingutf8 serverTimezoneAsia/ShanghaiuseSSLfalseallowPublicKeyRetrievaltrue; private static final String USER root; private static final String PWD 你的密码; public static Connection getConn() throws Exception { Class.forName(DRIVER); return DriverManager.getConnection(URL, USER, PWD); } }serverTimezone不写会报The server time zone value is unrecognized这是 MySQL 8.0 最经典的报错。allowPublicKeyRetrievaltrue是 8.0 默认加密方式导致的不加会提示Public Key Retrieval is not allowed。这两个参数记住就行不用深究原理。3.3 部署到 Tomcat 并验证登录把项目导入 Eclipse 或 IDEA确认WebContent/WEB-INF/lib下有mysql-connector-java-x.x.x.jar和jstl.jar然后配置 Tomcat 运行。启动后访问http://localhost:8080/hrms/login.jsp用 SQL 脚本里预置的账号登录常见是admin/123456。登录逻辑在LoginServlet里核心就一句查询String sql SELECT * FROM t_user WHERE username? AND password?;如果登录后跳转 404先看web.xml里url-pattern和表单action是否一致如果提示账号密码错误但数据库里明明有数据检查密码字段是不是被 MD5 加密过而表单传的是明文。这类项目一半用明文一半用 MD5没有统一标准只能打开表看实际存的值。注意登录成功后如果页面样式全丢是 CSS 路径问题。JSP 里写相对路径css/style.css在/emp/list.jsp这种二级目录下会解析成/emp/css/style.css改成${pageContext.request.contextPath}/css/style.css即可。4. 增删改查落地Servlet 与 JSP 的配合细节4.1 员工列表分页查询的实现列表页是使用频率最高的功能也是最容易写崩的地方。不分页的话几百条数据一次性渲染页面卡到怀疑人生。分页的核心是LIMIT加两个参数// pageNo 当前页码pageSize 每页条数 public ListEmployee listByPage(int pageNo, int pageSize) throws Exception { ListEmployee list new ArrayList(); String sql SELECT e.*, d.dept_name FROM t_employee e LEFT JOIN t_dept d ON e.dept_id d.dept_id ORDER BY e.emp_id DESC LIMIT ?, ?; try (Connection conn DBUtil.getConn(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setInt(1, (pageNo - 1) * pageSize); // 偏移量从 0 开始 ps.setInt(2, pageSize); ResultSet rs ps.executeQuery(); while (rs.next()) { Employee emp new Employee(); emp.setEmpId(rs.getInt(emp_id)); emp.setEmpName(rs.getString(emp_name)); emp.setDeptName(rs.getString(dept_name)); // 关联出来的部门名 list.add(emp); } } return list; }偏移量公式(pageNo - 1) * pageSize必须记牢写成pageNo * pageSize会漏掉第一页数据。ORDER BY一定要加否则 MySQL 返回顺序不保证翻页时会出现同一条记录重复出现或消失这种玄学问题排查起来很费时间。总记录数用单独的SELECT COUNT(*)查别想着一次查询搞定。4.2 新增与修改共用一个表单页新增和修改的字段完全一样没必要写两个 JSP。常见做法是共用一个empForm.jsp靠 URL 上有没有empId参数区分// Servlet 中判断 String empId request.getParameter(empId); if (empId ! null !empId.isEmpty()) { // 有 id是修改先查出来回填 Employee emp empDao.getById(Integer.parseInt(empId)); request.setAttribute(emp, emp); } request.getRequestDispatcher(/emp/empForm.jsp).forward(request, response);JSP 里用 EL 表达式回填value${emp.empName}。当emp为 null 时 EL 会输出空字符串不会报错正好满足新增场景。这个技巧省掉一个页面也省掉两份校验逻辑。4.3 删除操作为什么要用 POST 而不是 GET很多源码里删除写成a hrefempDelete?id5删除/a点一下就把数据删了。这在功能上没问题但有两个隐患一是浏览器预加载或爬虫可能误触发删除二是没法加二次确认。正确做法是改成表单提交form actionempDelete methodpost onsubmitreturn confirm(确认删除该员工); input typehidden nameempId value${emp.empId} button typesubmit删除/button /formonsubmit返回 false 时表单不提交这是最轻量的二次确认方案不需要引入任何弹窗库。删除时还要注意外键如果该员工在t_salary里有薪资记录直接删t_employee会因外键约束失败。要么先删薪资记录要么把外键改成ON DELETE CASCADE前者更安全后者更省事看你的数据重要性决定。5. 避坑与排查这套 JSP 人事系统最常见的 5 个翻车点5.1 中文乱码POST 和 GET 要分开治现象表单提交后数据库里存的是????或者页面显示问号。原因JSP 默认用 ISO-8859-1 解码。解决POST 请求在 Servlet 第一行加request.setCharacterEncoding(UTF-8)GET 请求改 Tomcat 的server.xml在 Connector 上加URIEncodingUTF-8。数据库连接串里的characterEncodingutf8和建表的utf8mb4也要对齐三处缺一不可。5.2 空指针异常DAO 返回 null 没判空现象点开员工详情页直接 500日志里NullPointerException。原因getById查不到记录返回 nullServlet 里直接emp.getEmpName()。解决拿到对象先判空为空就转发到错误页或列表页。更稳妥的做法是 DAO 层查不到时返回一个空对象而不是 null但老项目里改这个动静大先在调用处判空更实际。5.3 修改后数据没变事务没提交现象页面提示修改成功刷新数据库还是旧值。原因JDBC 默认自动提交但如果代码里手动开了conn.setAutoCommit(false)却忘了conn.commit()改动就丢了。解决检查 DAO 里有没有手动事务有就补上 commit异常分支里补 rollback。用 try-with-resources 时连接会自动关闭未提交的事务会被回滚所以这个坑特别隐蔽。5.4 部署后 404web.xml 映射和访问路径对不上现象Tomcat 启动正常访问任何页面都 404。原因web.xml里url-pattern写的是/login浏览器访问的是/LoginServlet或者项目 context path 不是hrms。解决看 Tomcat 启动日志里项目部署的 context path再看web.xml的映射两边对齐。IDEA 里可以在 Run Configuration 的 Deployment 页签确认 Application context。5.5 日期格式报错String 和 Date 转换失败现象提交生日或入职日期时抛IllegalArgumentException。原因表单传的是2024-01-01字符串代码里直接new java.sql.Date(...)或SimpleDateFormat格式不匹配。解决统一用SimpleDateFormat(yyyy-MM-dd)解析注意parse会抛ParseException要 try-catch。如果表单用的是input typedate提交格式固定是yyyy-MM-dd按这个格式解析就不会错。6. 二次开发进阶给这套系统加一个部门人数统计跑通之后真正体现价值的是按需扩展。我拿部门人数统计举例这是人事系统里最常被要求加的功能也能顺带把 JSTL 和 SQL 聚合练一遍。需求是在部门列表页每个部门后面显示当前人数。最笨的做法是循环里再查一次数据库N 个部门查 N1 次部门一多就慢。正确做法是一条 SQL 用GROUP BY搞定SELECT d.dept_id, d.dept_name, COUNT(e.emp_id) AS emp_count FROM t_dept d LEFT JOIN t_employee e ON d.dept_id e.dept_id GROUP BY d.dept_id, d.dept_name ORDER BY emp_count DESC;这里必须用LEFT JOIN而不是INNER JOIN否则人数为 0 的部门会直接从列表里消失这是统计类需求最常见的逻辑错误。COUNT(e.emp_id)而不是COUNT(*)因为COUNT(*)会把 LEFT JOIN 产生的 NULL 行也算成 1空部门会显示 1 人。DAO 里把emp_count读进一个扩展字段JSP 用c:forEach渲染table trth部门/thth人数/th/tr c:forEach items${deptList} vard tr td${d.deptName}/td td${d.empCount}/td /tr /c:forEach /table验证方法很直接先在数据库里手动删掉某个部门的所有员工刷新页面看该部门是否还显示且人数为 0再新增一个员工看对应部门人数是否加 1。两步都通过说明 JOIN 和聚合逻辑没问题。再往深一层如果要做按部门筛选员工别在 JSP 里用 JavaScript 过滤数据量大时前端扛不住。正确做法是给列表 Servlet 加一个deptId参数SQL 里动态拼WHEREStringBuilder sql new StringBuilder( SELECT e.*, d.dept_name FROM t_employee e LEFT JOIN t_dept d ON e.dept_id d.dept_id WHERE 11); ListObject params new ArrayList(); if (deptId ! null deptId 0) { sql.append( AND e.dept_id ?); params.add(deptId); } sql.append( ORDER BY e.emp_id DESC LIMIT ?, ?);WHERE 11是个老技巧为的是后续条件都能以AND开头不用判断是不是第一个条件。参数用List收集再统一setObject避免下标错乱。这套写法在 JSP 项目里足够用不需要引入 MyBatis 动态 SQL。最后说个习惯每次改完 DAO我都会先在数据库客户端把 SQL 单独跑一遍确认结果集对了再回代码里调。JSP 项目没有单元测试的传统这个手动验证就是最后的后悔药。这套系统技术不新但把上面这些细节吃透改需求时能少熬好几个夜。希望帮到你。本文还有配套的精品资源点击获取
返回列表