Java Web人事管理系统实战:JSP+Servlet+MVC架构完整开发指南

很多Java学习者在掌握了基础语法后,常常面临"不知道如何将知识点串联起来做项目"的困境。人事管理系统作为企业级应用的经典场景,涵盖了用户管理、权限控制、数据增删改查等核心功能,是检验Java Web技术栈掌握程度的绝佳练手项目。

本文将带你从零搭建一个完整的人事管理系统,采用主流的JSP+Servlet技术架构,包含前端界面设计、后端业务逻辑实现、数据库操作等完整环节。无论你是需要完成课程大作业的学生,还是想积累项目经验的开发者,都能通过这个实战案例快速掌握Java Web项目开发全流程。

1. 项目需求分析与技术选型

1.1 系统功能需求

人事管理系统主要面向企业HR部门,需要实现员工信息的全生命周期管理。核心功能包括:

  • 员工信息管理:新增、查询、修改、删除员工基本信息
  • 部门管理:维护公司组织架构,设置部门信息
  • 岗位管理:定义不同岗位职责和权限
  • 用户权限控制:不同角色用户拥有不同的操作权限
  • 数据统计展示:以图表形式展示员工分布情况

1.2 技术栈选择理由

基于项目复杂度和学习成本考虑,我们选择以下技术组合:

  • 后端:Servlet + JSP,作为Java Web开发的基础,适合理解MVC架构原理
  • 前端:JSP + Bootstrap,快速构建响应式界面,降低前端开发难度
  • 数据库:MySQL 8.0,轻量级且功能完善的关系型数据库
  • 服务器:Tomcat 9.x,轻量级Servlet容器,部署简单
  • 开发工具:Eclipse或IntelliJ IDEA,配合Maven进行依赖管理

这种技术组合既保证了项目的完整性,又避免了过度复杂的技术栈给初学者带来困扰。

2. 开发环境准备与项目搭建

2.1 环境配置清单

在开始编码前,请确保你的开发环境包含以下组件:

  • JDK 8或11(建议使用LTS版本)
  • MySQL 8.0数据库
  • Tomcat 9.x服务器
  • Eclipse IDE for Enterprise Java and Web Developers
  • Maven 3.6+

2.2 创建Maven Web项目

在Eclipse中新建Dynamic Web Project,或使用Maven archetype创建项目结构:

<!-- pom.xml 主要依赖配置 --> <dependencies> <!-- Servlet API --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency> <!-- JSTL标签库 --> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <!-- MySQL驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.33</version> </dependency> <!-- 数据库连接池 --> <dependency> <groupId>com.zaxxer</groupId> <artifactId>HikariCP</artifactId> <version>5.0.1</version> </dependency> </dependencies>

2.3 数据库设计

创建人事管理系统所需的数据库表结构:

-- 创建数据库 CREATE DATABASE hr_management CHARACTER SET utf8mb4; -- 部门表 CREATE TABLE department ( id INT PRIMARY KEY AUTO_INCREMENT, dept_name VARCHAR(50) NOT NULL, dept_desc VARCHAR(200), create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- 员工表 CREATE TABLE employee ( id INT PRIMARY KEY AUTO_INCREMENT, emp_name VARCHAR(50) NOT NULL, gender ENUM('男','女'), birth_date DATE, email VARCHAR(100), phone VARCHAR(20), dept_id INT, position VARCHAR(50), salary DECIMAL(10,2), hire_date DATE, status ENUM('在职','离职','休假'), FOREIGN KEY (dept_id) REFERENCES department(id) ); -- 用户表(系统登录) CREATE TABLE user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) UNIQUE NOT NULL, password VARCHAR(100) NOT NULL, role ENUM('admin','hr','employee') DEFAULT 'employee', emp_id INT, FOREIGN KEY (emp_id) REFERENCES employee(id) );

3. 核心架构设计与实现

3.1 MVC模式分层结构

采用经典的三层架构,确保代码的可维护性和扩展性:

src/main/java/com/hrsystem/ ├── controller/ # Servlet控制器 ├── service/ # 业务逻辑层 ├── dao/ # 数据访问层 ├── model/ # 实体类 └── util/ # 工具类

3.2 数据库连接池配置

使用HikariCP实现高效的数据库连接管理:

// 文件路径:src/main/java/com/hrsystem/util/DatabaseUtil.java public class DatabaseUtil { private static HikariDataSource dataSource; static { HikariConfig config = new HikariConfig(); config.setJdbcUrl("jdbc:mysql://localhost:3306/hr_management"); config.setUsername("root"); config.setPassword("your_password"); config.setDriverClassName("com.mysql.cj.jdbc.Driver"); config.setMaximumPoolSize(20); config.setMinimumIdle(5); config.setConnectionTimeout(30000); config.setIdleTimeout(600000); config.setMaxLifetime(1800000); dataSource = new HikariDataSource(config); } public static Connection getConnection() throws SQLException { return dataSource.getConnection(); } }

3.3 实体类设计

定义与数据库表对应的JavaBean:

// 文件路径:src/main/java/com/hrsystem/model/Employee.java public class Employee { private Integer id; private String empName; private String gender; private Date birthDate; private String email; private String phone; private Integer deptId; private String position; private BigDecimal salary; private Date hireDate; private String status; private String deptName; // 联表查询时使用 // 构造方法、getter和setter省略 // 实际开发中可以使用Lombok简化代码 }

4. 数据访问层(DAO)实现

4.1 基础DAO接口设计

定义通用的数据操作接口:

// 文件路径:src/main/java/com/hrsystem/dao/BaseDAO.java public interface BaseDAO<T> { int insert(T entity) throws SQLException; int update(T entity) throws SQLException; int delete(Integer id) throws SQLException; T findById(Integer id) throws SQLException; List<T> findAll() throws SQLException; }

4.2 员工DAO实现类

实现员工数据的CRUD操作:

// 文件路径:src/main/java/com/hrsystem/dao/EmployeeDAO.java public class EmployeeDAO implements BaseDAO<Employee> { @Override public int insert(Employee emp) throws SQLException { String sql = "INSERT INTO employee(emp_name, gender, birth_date, email, phone, " + "dept_id, position, salary, hire_date, status) " + "VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; try (Connection conn = DatabaseUtil.getConnection(); PreparedStatement pstmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) { pstmt.setString(1, emp.getEmpName()); pstmt.setString(2, emp.getGender()); pstmt.setDate(3, new java.sql.Date(emp.getBirthDate().getTime())); pstmt.setString(4, emp.getEmail()); pstmt.setString(5, emp.getPhone()); pstmt.setInt(6, emp.getDeptId()); pstmt.setString(7, emp.getPosition()); pstmt.setBigDecimal(8, emp.getSalary()); pstmt.setDate(9, new java.sql.Date(emp.getHireDate().getTime())); pstmt.setString(10, emp.getStatus()); int affectedRows = pstmt.executeUpdate(); if (affectedRows > 0) { try (ResultSet rs = pstmt.getGeneratedKeys()) { if (rs.next()) { return rs.getInt(1); } } } return affectedRows; } } @Override public List<Employee> findAll() throws SQLException { String sql = "SELECT e.*, d.dept_name FROM employee e " + "LEFT JOIN department d ON e.dept_id = d.id " + "ORDER BY e.id DESC"; List<Employee> employees = new ArrayList<>(); try (Connection conn = DatabaseUtil.getConnection(); PreparedStatement pstmt = conn.prepareStatement(sql); ResultSet rs = pstmt.executeQuery()) { while (rs.next()) { Employee emp = resultSetToEmployee(rs); emp.setDeptName(rs.getString("dept_name")); employees.add(emp); } } return employees; } private Employee resultSetToEmployee(ResultSet rs) throws SQLException { Employee emp = new Employee(); emp.setId(rs.getInt("id")); emp.setEmpName(rs.getString("emp_name")); emp.setGender(rs.getString("gender")); emp.setBirthDate(rs.getDate("birth_date")); emp.setEmail(rs.getString("email")); emp.setPhone(rs.getString("phone")); emp.setDeptId(rs.getInt("dept_id")); emp.setPosition(rs.getString("position")); emp.setSalary(rs.getBigDecimal("salary")); emp.setHireDate(rs.getDate("hire_date")); emp.setStatus(rs.getString("status")); return emp; } }

5. 业务逻辑层(Service)实现

5.1 员工服务接口

定义业务操作规范:

// 文件路径:src/main/java/com/hrsystem/service/EmployeeService.java public interface EmployeeService { boolean addEmployee(Employee employee); boolean updateEmployee(Employee employee); boolean deleteEmployee(Integer id); Employee getEmployeeById(Integer id); List<Employee> getAllEmployees(); List<Employee> getEmployeesByDept(Integer deptId); }

5.2 员工服务实现类

处理复杂的业务逻辑和事务管理:

// 文件路径:src/main/java/com/hrsystem/service/impl/EmployeeServiceImpl.java public class EmployeeServiceImpl implements EmployeeService { private EmployeeDAO employeeDAO = new EmployeeDAO(); @Override public boolean addEmployee(Employee employee) { try { // 业务规则验证 if (employee.getEmpName() == null || employee.getEmpName().trim().isEmpty()) { throw new IllegalArgumentException("员工姓名不能为空"); } if (employee.getEmail() != null && !isValidEmail(employee.getEmail())) { throw new IllegalArgumentException("邮箱格式不正确"); } int result = employeeDAO.insert(employee); return result > 0; } catch (SQLException e) { System.err.println("添加员工失败: " + e.getMessage()); return false; } } @Override public List<Employee> getAllEmployees() { try { return employeeDAO.findAll(); } catch (SQLException e) { System.err.println("获取员工列表失败: " + e.getMessage()); return new ArrayList<>(); } } private boolean isValidEmail(String email) { String emailRegex = "^[A-Za-z0-9+_.-]+@(.+)$"; return email.matches(emailRegex); } }

6. 控制器层(Servlet)实现

6.1 员工列表查询Servlet

处理前端请求并返回数据:

// 文件路径:src/main/java/com/hrsystem/controller/EmployeeListServlet.java @WebServlet("/employee/list") public class EmployeeListServlet extends HttpServlet { private EmployeeService employeeService = new EmployeeServiceImpl(); protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { List<Employee> employees = employeeService.getAllEmployees(); request.setAttribute("employees", employees); request.getRequestDispatcher("/WEB-INF/views/employee_list.jsp").forward(request, response); } catch (Exception e) { response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "系统错误"); } } }

6.2 添加员工Servlet

处理表单提交和数据验证:

// 文件路径:src/main/java/com/hrsystem/controller/AddEmployeeServlet.java @WebServlet("/employee/add") public class AddEmployeeServlet extends HttpServlet { private EmployeeService employeeService = new EmployeeServiceImpl(); protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); try { Employee employee = new Employee(); employee.setEmpName(request.getParameter("empName")); employee.setGender(request.getParameter("gender")); employee.setEmail(request.getParameter("email")); employee.setPhone(request.getParameter("phone")); employee.setDeptId(Integer.parseInt(request.getParameter("deptId"))); employee.setPosition(request.getParameter("position")); employee.setSalary(new BigDecimal(request.getParameter("salary"))); // 日期转换 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); employee.setBirthDate(sdf.parse(request.getParameter("birthDate"))); employee.setHireDate(sdf.parse(request.getParameter("hireDate"))); employee.setStatus("在职"); boolean success = employeeService.addEmployee(employee); if (success) { response.sendRedirect(request.getContextPath() + "/employee/list?msg=add_success"); } else { request.setAttribute("error", "添加员工失败"); request.getRequestDispatcher("/WEB-INF/views/add_employee.jsp").forward(request, response); } } catch (Exception e) { request.setAttribute("error", "数据格式错误: " + e.getMessage()); request.getRequestDispatcher("/WEB-INF/views/add_employee.jsp").forward(request, response); } } }

7. 前端界面设计与实现

7.1 员工列表页面

使用Bootstrap构建响应式数据表格:

<%-- 文件路径:WEB-INF/views/employee_list.jsp --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <html> <head> <title>员工管理</title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet"> </head> <body> <div class="container mt-4"> <h2>员工信息列表</h2> <div class="mb-3"> <a href="<c:url value='/employee/add'/>" class="btn btn-primary">新增员工</a> </div> <c:if test="${not empty param.msg}"> <div class="alert alert-success">操作成功!</div> </c:if> <table class="table table-striped"> <thead> <tr> <th>ID</th> <th>姓名</th> <th>性别</th> <th>部门</th> <th>岗位</th> <th>薪资</th> <th>状态</th> <th>操作</th> </tr> </thead> <tbody> <c:forEach var="emp" items="${employees}"> <tr> <td>${emp.id}</td> <td>${emp.empName}</td> <td>${emp.gender}</td> <td>${emp.deptName}</td> <td>${emp.position}</td> <td>¥${emp.salary}</td> <td> <span class="badge bg-${emp.status eq '在职' ? 'success' : 'secondary'}"> ${emp.status} </span> </td> <td> <a href="edit?id=${emp.id}" class="btn btn-sm btn-warning">编辑</a> <a href="delete?id=${emp.id}" class="btn btn-sm btn-danger" onclick="return confirm('确定删除该员工吗?')">删除</a> </td> </tr> </c:forEach> </tbody> </table> </div> </body> </html>

7.2 添加员工表单页面

实现数据录入和前端验证:

<%-- 文件路径:WEB-INF/views/add_employee.jsp --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>新增员工</title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet"> </head> <body> <div class="container mt-4"> <h2>新增员工信息</h2> <c:if test="${not empty error}"> <div class="alert alert-danger">${error}</div> </c:if> <form action="add" method="post" class="needs-validation" novalidate> <div class="row"> <div class="col-md-6"> <div class="mb-3"> <label class="form-label">姓名 *</label> <input type="text" class="form-control" name="empName" required> </div> <div class="mb-3"> <label class="form-label">性别</label> <select class="form-select" name="gender"> <option value="男">男</option> <option value="女">女</option> </select> </div> <div class="mb-3"> <label class="form-label">出生日期</label> <input type="date" class="form-control" name="birthDate"> </div> </div> <div class="col-md-6"> <div class="mb-3"> <label class="form-label">邮箱</label> <input type="email" class="form-control" name="email"> </div> <div class="mb-3"> <label class="form-label">电话</label> <input type="tel" class="form-control" name="phone"> </div> <div class="mb-3"> <label class="form-label">入职日期 *</label> <input type="date" class="form-control" name="hireDate" required> </div> </div> </div> <div class="row"> <div class="col-md-4"> <div class="mb-3"> <label class="form-label">部门</label> <select class="form-select" name="deptId"> <option value="1">技术部</option> <option value="2">人事部</option> <option value="3">财务部</option> </select> </div> </div> <div class="col-md-4"> <div class="mb-3"> <label class="form-label">岗位</label> <input type="text" class="form-control" name="position"> </div> </div> <div class="col-md-4"> <div class="mb-3"> <label class="form-label">薪资</label> <input type="number" class="form-control" name="salary" step="0.01"> </div> </div> </div> <button type="submit" class="btn btn-primary">提交</button> <a href="list" class="btn btn-secondary">返回</a> </form> </div> <script> // 表单验证 (function () { 'use strict' var forms = document.querySelectorAll('.needs-validation') Array.prototype.slice.call(forms).forEach(function (form) { form.addEventListener('submit', function (event) { if (!form.checkValidity()) { event.preventDefault() event.stopPropagation() } form.classList.add('was-validated') }, false) }) })() </script> </body> </html>

8. 系统部署与运行测试

8.1 Tomcat服务器配置

将项目部署到Tomcat服务器:

  1. 在Eclipse中右键项目 → Export → WAR file
  2. 将生成的WAR文件复制到Tomcat的webapps目录
  3. 启动Tomcat服务器,访问 http://localhost:8080/your-project-name

8.2 数据库初始化脚本

创建初始数据用于测试:

-- 插入测试部门数据 INSERT INTO department (dept_name, dept_desc) VALUES ('技术部', '负责产品研发和技术支持'), ('人事部', '负责招聘和员工关系管理'), ('财务部', '负责公司财务和薪酬管理'); -- 插入测试员工数据 INSERT INTO employee (emp_name, gender, birth_date, email, phone, dept_id, position, salary, hire_date, status) VALUES ('张三', '男', '1990-01-15', 'zhangsan@company.com', '13800138001', 1, 'Java工程师', 15000.00, '2020-03-01', '在职'), ('李四', '女', '1992-05-20', 'lisi@company.com', '13800138002', 2, 'HR专员', 8000.00, '2021-06-15', '在职');

8.3 功能测试流程

按照以下顺序验证系统功能:

  1. 访问员工列表页面,确认数据正常显示
  2. 测试新增员工功能,包括表单验证和数据提交
  3. 验证编辑功能,修改员工信息并保存
  4. 测试删除功能,确认数据删除成功
  5. 检查不同浏览器的兼容性

9. 常见问题与解决方案

9.1 数据库连接问题

问题现象:启动时报数据库连接失败解决方案

  • 检查MySQL服务是否启动
  • 确认数据库连接字符串中的用户名密码正确
  • 验证数据库驱动版本兼容性

9.2 中文乱码问题

问题现象:页面显示或数据库存储出现乱码解决方案

<!-- 在web.xml中添加字符编码过滤器 --> <filter> <filter-name>encodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>

9.3 依赖冲突问题

问题现象:ClassNotFoundException或NoSuchMethodError解决方案

  • 使用Maven的dependency:tree命令分析依赖关系
  • 排除冲突的依赖版本
  • 统一框架组件的版本号

10. 项目优化与扩展建议

10.1 性能优化方案

  1. 数据库优化:为常用查询字段添加索引
  2. 缓存策略:使用Redis缓存热点数据
  3. 连接池调优:根据并发量调整连接池参数
  4. 前端优化:压缩静态资源,使用CDN加速

10.2 功能扩展方向

  1. 权限管理:集成Spring Security实现细粒度权限控制
  2. 文件上传:支持员工照片和证件上传功能
  3. 数据导出:实现Excel格式的数据导出
  4. 消息通知:集成邮件或短信通知功能
  5. 移动端适配:开发响应式界面或独立移动端应用

10.3 安全加固措施

  1. SQL注入防护:始终使用PreparedStatement
  2. XSS防护:对用户输入进行过滤和转义
  3. 会话安全:设置合理的Session超时时间
  4. 密码安全:使用BCrypt等算法加密存储密码

这个人事管理系统项目涵盖了Java Web开发的核心技术要点,通过完整的实战演练,你不仅能够掌握MVC设计模式的实际应用,还能深入理解Web应用的完整生命周期。建议在完成基础功能后,尝试实现扩展功能来进一步提升技术水平。