简介基于SSM框架SpringSpringMVCMyBatis的中小企业人力资源管理系统采用B/S架构面向Java Web学习者和毕业设计学生实现员工管理、考勤打卡、奖惩绩效、工资核算、培训岗位等业务的一体化操作。系统区分管理员与普通用户角色提供登录注册、个人中心、密码修改、合同下载、离职退休费用管理等完整功能模块界面清晰、操作流畅适合作为课程设计或毕业设计的参考实践项目。资源压缩包约18.01MB内含项目完整源码、MySQL数据库脚本以及开题报告、毕业论文等配套文档支持JDK1.8及以上版本可在Eclipse或IDEA中直接导入运行。目前已吸引75人学习下载借助配套说明可快速完成环境搭建、功能演示与设计文档梳理有效提升毕业设计完成效率。1. 把中小企业HR管理搬到Web端这套SSM毕设资源先看这三件事很多还在用手工Excel做考勤和工资的小企业其实卡在一个很尴尬的位置买一套商业HR系统动辄几万块自己从零写又没时间。这套基于SSM框架的中小企业人力资源管理系统恰好填了这个空档——它用SpringSpringMVCMyBatis搭了一个完整的B/S架构Web应用管理员和普通员工都能在浏览器里完成打卡、查工资、下载合同这些事。对毕设党来说它的价值在于源码和数据库脚本齐全功能覆盖面广对刚入行的Java开发来说这套代码是一个标准的SSM三层项目范本能清楚看到Controller、Service、Mapper是怎么串起来的。这篇笔记我用拆过十来个SSM项目的经验把它的架构、部署、核心模块代码和踩坑记录都过一遍。2. SSM框架在HR系统中的角色拆解Spring、SpringMVC、MyBatis如何各司其职2.1 分包结构与Spring容器的职责边界拿到源码后第一件事别急着跑先把包结构看明白。这个项目的分包方式是典型的SSM三层架构核心包路径大致是controller、service、mapper或dao、entity或pojo。以登录功能为例entity包里放着User这样的实体类属性对应数据库表的字段mapper接口只声明方法对应的XML里写SQLservice层处理业务逻辑比如校验用户名密码controller接收前端参数并调用service。Spring在这套系统里管的不是具体某个业务而是对象的创建和依赖注入。你会在applicationContext.xml或者spring-*.xml里看到一堆component-scan配置它能把标了Controller、Service、Repository的类自动扫描进容器。这里有个实际好处工资模块要调用考勤模块的数据不用手动new一个AttendanceService直接在工资Service里用Autowired注入就行。如果你是第一次跑SSM项目看到XML里那一大堆bean定义别慌大部分是框架底层配置项目能跑通之前基本不用动。2.2 SpringMVC的请求链路与JSP视图返回这个系统的前端是JSP加jQuery和后端交互的方式有两种一种是表单直接提交Controller返回ModelAndView或字符串指向JSP页面另一种是页面用$.ajax()发请求后端返回JSON数据前端渲染。SpringMVC处理请求的过程是线性的前端请求先经过DispatcherServlet它根据HandlerMapping找到对应的Controller方法方法执行完返回视图名视图解析器再把视图名拼接成实际的JSP路径。比如Controller里返回admin/employeeList那视图解析器的prefix和suffix如果配置成/WEB-INF/views/和.jsp实际指向的就是/WEB-INF/views/admin/employeeList.jsp。翻代码的时候你会发现登录Controller拿到用户提交的用户名密码后不会直接查数据库而是调用UserService里的login方法Service拿到结果后再决定跳转主页还是返回错误信息。这种分层的好处是后续改逻辑不用动前端和Controller只改Service就行。2.3 MyBatis的Mapper接口与XML映射MyBatis在这个项目里是绕不开的重点。你打开mapper目录会发现每个接口对应一个XML比如UserMapper.java对应UserMapper.xml里面的namespace必须完整写接口的全限定名比如com.example.mapper.UserMapper。写错了运行时就报BindingException这是SSM项目最常见的翻车点之一。以员工查询为例XML里的select语句可以写成动态SQL支持按姓名、部门、岗位多条件过滤。代码大致是这样select idselectEmployeeList parameterTypemap resultTypecom.example.entity.Employee SELECT * FROM employee where if testempName ! null and empName ! AND emp_name LIKE CONCAT(%, #{empName}, %) /if if testdeptId ! null AND dept_id #{deptId} /if if testpostId ! null AND post_id #{postId} /if /where ORDER BY emp_id DESC /select这里说明几个关键点parameterTypemap表示外部传入的是一个Map对象Service层把查询条件塞进Map传给MapperresultType指定每行数据映射成哪个实体#{}是预编译占位符能防SQL注入比直接拼字符串安全。动态SQL里的if标签是MyBatis最常用的工具条件为空就不拼进SQL避免查询结果被空条件干扰。3. 从零到一跑通整套系统IDEA导入、数据库初始化和登录验证3.1 环境版本核对JDK、Maven、Tomcat缺一不可先对版本这一步不做后面全是坑。这个项目要求JDK 1.8及以上MySQL 5.7Maven负责管理依赖。这三个是硬性约束我建议按这个基准来。JDK 1.8是最稳的版本SSM框架在1.8下不会有任何兼容问题。虽然Oracle早就停止了对Java 8的免费商业更新但开发调试不受影响。MySQL用5.7是因为项目里的SQL脚本和驱动的兼容性都是按5.7设计的如果你非要上MySQL 8.0后面驱动和连接参数得改改动量不大但很烦。Maven版本3.6以上都行IDEA自带的Maven也能用关键是settings.xml要配阿里云镜像不然拉依赖能等半小时。Tomcat这块不用单独下载IDEA里配置本地Tomcat或者用Smart Tomcat插件都行。注意Tomcat 8.5和9.0对Servlet版本支持的差异如果项目web.xml按3.1规范写的Tomcat 8.5最稳妥。3.2 导入项目与数据库初始化第一步打开IDEAFile - New - Project from Existing Sources选中解压后的源码目录选择Maven导入方式。IDEA会自动读取pom.xml开始下载依赖。这个阶段能通过看右侧Maven窗口的依赖列表判断是否正常所有包变成白色而不是红色就算成功。第二步初始化数据库。源码包里一般带一份SQL脚本名字大概是hr_management.sql或hrms.sql里面包含建库、建表和初始数据。用Navicat或命令行执行都一样推荐命令行先跑一遍看有没有报错mysql -uroot -p CREATE DATABASE IF NOT EXISTS hr_management DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE hr_management; SOURCE /path/to/hr_management.sql;PRIMARY KEY和索引的创建语句顺序不要乱如果报错说表已存在先DROP DATABASE再重新建。数据库字符集一定选utf8mb4不然中文数据进去全变问号。第三步改数据库连接配置。找到src/main/resources目录下的jdbc.properties文件内容一般是jdbc.drivercom.mysql.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/hr_management?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai jdbc.usernameroot jdbc.password123456把username和password改成你自己MySQL的账号密码。url里的serverTimezone参数在MySQL 5.7下可以不写但MySQL 8.0必须写useSSLfalse是为了避免本地连接时的SSL握手警告这不算安全问题因为本地开发环境本来就不加密。3.3 IDEA中Tomcat的运行配置数据库弄好后开始配置Tomcat运行环境。点顶部Run - Edit Configurations - 左上角加号 - Tomcat Server - Local。在Server选项卡里选本机Tomcat路径端口默认8080如果被占用改成8081。Deployment选项卡点加号选Artifact把项目的war包加进去Application context这里要特别注意写/就行不要写成项目名再加斜杠否则后面访问路径全都要带前缀登录跳转的URL会和实际页面路径对不上。配置完成后启动看到控制台出现INFO: Server startup in [xxx] milliseconds说明Tomcat起来了。然后浏览器访问http://localhost:8080/正常情况下会跳转到登录页。如果看到的是Tomcat默认首页而不是项目登录页问题出在两个地方第一Application context没配对第二Artifact根本没部署上去。重新检查Deployment选项卡。4. 核心业务模块的代码视角考勤、奖惩、工资、离职费用这样落库4.1 考勤打卡模块一天一条记录的状态流转考勤是这个系统里最贴近真实业务的模块也是答辩时最容易被问细节的地方。它涉及两张表一张存放打卡记录字段大致有attendance_id、user_id、work_date、clock_in_time、clock_out_time、status另一张存放考勤规则比如上下班时间、允许迟到分钟数。打卡逻辑的常见做法是这样员工点击上班打卡后端先查当前日期有没有记录。查不到就insert一条新记录把上班时间写入clock_in_time状态设为已上班查到就把下班时间写入clock_out_time同时计算工时状态改成已下班。核心Service代码如下public ResultBean clockIn(Integer userId) { Date now new Date(); Attendance attendance attendanceMapper.selectByUserIdAndDate(userId, now); if (attendance null) { attendance new Attendance(); attendance.setUserId(userId); attendance.setWorkDate(now); attendance.setClockInTime(now); attendance.setStatus(1); // 1上班已打卡 2下班已打卡 3异常 attendanceMapper.insert(attendance); return ResultBean.success(上班打卡成功); } attendance.setClockOutTime(now); attendance.setStatus(2); attendanceMapper.update(attendance); return ResultBean.success(下班打卡成功); }这段逻辑要注意一个小坑判断上下班不能只靠status字段一定要先查work_date对应日期的记录是否存在。否则员工跨天没打下班卡第二天上班打卡时会把第一天记录的下班时间更新掉数据就乱了。另外status字段建议用Integer而不用String数据库查询和比较都快而且不容易被JSP页面的空格坑到。4.2 工资与奖惩模块组合算薪的前后端协作工资模块是整个系统里关联表最多的模块。月份的应发工资等于基本工资加绩效工资加岗位津贴再加减奖惩金额最后扣社保和个税。这里的计算逻辑一般写在Service层而不是直接写SQL原因是奖惩、绩效数据来自不同表先分别查出来再在Java代码里组合更直观。一个实用的SQL是查询某员工某月的工资明细select idselectSalaryDetail parameterTypemap resultTypemap SELECT e.emp_name, p.post_name, s.base_salary, s.post_salary, s.performance_salary, IFNULL(r.reward_amount, 0) AS reward_amount, IFNULL(r.punish_amount, 0) AS punish_amount, (s.base_salary s.post_salary s.performance_salary IFNULL(r.reward_amount, 0) - IFNULL(r.punish_amount, 0) - s.social_security - s.tax) AS final_salary FROM salary s LEFT JOIN employee e ON s.emp_id e.emp_id LEFT JOIN post p ON e.post_id p.post_id LEFT JOIN ( SELECT emp_id, SUM(reward_amount) AS reward_amount, SUM(punish_amount) AS punish_amount FROM reward_punish WHERE DATE_FORMAT(occur_date, %Y-%m) #{month} GROUP BY emp_id ) r ON e.emp_id r.emp_id WHERE s.salary_month #{month} /select这个查询说明几个实操要点LEFT JOIN保底因为不是每个人当月都有奖惩记录用INNER JOIN会把部分员工漏掉IFNULL函数把NULL转成0否则Java端算final_salary时会出现null相加直接变成NULL的情况子查询里用DATE_FORMAT格式化月份字段保证和salary_month的存储格式一致。前端页面拿到这个final_salary后直接展示后端不用再做循环累加。4.3 合同下载与离职退休费用管理合同下载的实现是文件操作加HTTP响应头的组合核心代码如下RequestMapping(/downloadContract) public void downloadContract(Integer contractId, HttpServletResponse response) throws IOException { Contract contract contractService.getById(contractId); String filePath contract.getContractFile(); File file new File(filePath); // 设置响应头告诉浏览器这是一个需要下载的附件 response.setContentType(application/octet-stream); response.setHeader(Content-Disposition, attachment; filename URLEncoder.encode(contract.getFileName(), UTF-8)); FileInputStream fis new FileInputStream(file); OutputStream out response.getOutputStream(); byte[] buffer new byte[1024]; int len; while ((len fis.read(buffer)) ! -1) { out.write(buffer, 0, len); } fis.close(); out.flush(); }这里filename用URLEncoder.encode是防中文文件名在部分浏览器里变成乱码的关键一步我见过很多人不编码Chrome下正常IE和Edge直接崩。合同文件路径建议存相对路径而不是绝对路径否则换一台电脑部署就找不到文件。离职退休费用管理比较特殊它不是单纯的增删改查而是联动员工状态。员工离职时要算结算工资、补偿金、社保停缴日期退休时要算退休补助。比较稳妥的做法是在员工表里加一个status字段0在职、1离职、2退休离职结算单生成时把status同步更新这样管理员在员工列表里一眼能看出谁不在职。5. 跑这个项目的七个常见问题排查配置、路径、端口和数据库连接5.1 Maven依赖下载慢或报红配置阿里云镜像现象IDEA导入项目后pom.xml文件大量报红右键没有Run选项或者其他SSM项目能跑但就这个项目起不来。原因默认中央仓库在国内访问极慢部分jar包下载超时后IDEA会标记为依赖缺失。解决修改Maven的settings.xml配置阿里云镜像。在mirrors节点下加mirror idaliyunmaven/id mirrorOfcentral/mirrorOf name阿里云公共仓库/name urlhttps://maven.aliyun.com/repository/public/url /mirror配置完在IDEA里点击Maven窗口的刷新按钮让依赖重新解析。如果还是报红检查IDEA的Maven settings确认User settings file指向的是你改的那个settings.xml。5.2 MySQL连接报Public Key Retrieval not allowed现象启动Tomcat后控制台报错Public Key Retrieval is not allowed或者Communications link failure。原因MySQL 8.0及以上默认使用caching_sha2_password认证方式JDBC驱动首次连接时需要获取服务器的公钥而连接参数里没允许这个行为。解决在jdbc.properties的url后面追加allowPublicKeyRetrievaltrue和useSSLfalse。如果你用的是MySQL 5.7这个报错不太会出现但加上也无妨。5.3 启动后404Artifact没有正确部署现象Tomcat起来了端口也通了但访问localhost:8080显示404或者Tomcat默认首页。原因IDEA里Tomcat配置的Deployment标签页没有添加Artifact或者Application context设置错误。解决打开Run Configurations找到Tomcat Server的配置切到Deployment选项卡点加号选择Artifact确认下拉列表里能看到项目名的war包。Application context写/。然后重启Tomcat注意要杀掉之前的进程再重新启动避免热部署状态下配置没生效。5.4 MyBatis报BindingException: Invalid bound statement现象访问某个页面时后台报org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.xxx.mapper.UserMapper.selectUser。原因Mapper接口和XML映射文件没有正确绑定。要么是XML的namespace没写接口全限定名要么是XML没被MyBatis扫描到。解决先检查XML文件里namespace是否和接口全限定名一致。再检查spring-mybatis.xml里的mapperLocations配置确保路径匹配用classpath*:mapper/*.xml通配符最稳。最后在启动类或配置类上加MapperScan(com.xxx.mapper)把Mapper接口注册进去。5.5 JSP页面中文乱码现象登录页、员工列表等页面中文全是问号或者插入到数据库的中文读出后是乱码。原因三层编码不一致——JSP页面本身、数据库连接、数据库表字符集。解决JSP文件顶部写% page languagejava contentTypetext/html; charsetUTF-8 pageEncodingUTF-8%数据库连接url里加characterEncodingutf8建库时用utf8mb4。另外Tomcat的server.xml里Connector配置追加URIEncodingUTF-8这样GET请求参数里的中文才不会乱。5.6 端口被占用导致Tomcat启动失败现象控制台报Port 8080 was already in use。原因系统里有其他进程占用了8080端口比如之前启动的Tomcat没关干净或者某Java进程在后台运行。解决在IDEA中打开Terminal执行netstat -ano | findstr 8080看占用进程的PID然后再执行taskkill /PID 进程号 /F强制杀掉。或者干脆把Tomcat端口改成8081改完记得访问地址也跟着变。5.7 数据库表结构与实体类字段对不上现象登录成功但某个页面报NullPointerException或者列表页某些列显示为0或空。原因SQL脚本和Java实体类的字段名不一致比如数据库字段是emp_name实体类属性是empName但列名和resultMap的映射没配好。解决检查resources下的mybatis目录里有没有mybatis-config.xml配置了mapUnderscoreToCamelCase为true。开了这项后emp_name能自动转成empName没开就必须在resultMap里手动写column和property的对应关系。翻车记录显示大多数人是在新加字段时忘了同步这两处所以每次改表结构后把实体类、MapperXML、XML的resultMap三处同时过一遍。6. 把毕设做成加分项给HR系统加一个考勤可视化看板的改造思路项目跑通只是第一步答辩时的加分项是你能在原有基础上做增量开发。这里给一个低风险、见效快的改造方向在首页加一个按部门统计月度出勤率的可视化面板。具体思路分三步。第一步数据库层新增一个统计查询按月分组计算每个部门的平均出勤天数SELECT d.dept_name, COUNT(CASE WHEN a.clock_in_time IS NOT NULL THEN 1 END) / COUNT(DISTINCT a.user_id) AS avg_attendance_days FROM attendance a JOIN employee e ON a.user_id e.emp_id JOIN department d ON e.dept_id d.dept_id WHERE DATE_FORMAT(a.work_date, %Y-%m) #{month} GROUP BY d.dept_id, d.dept_name第二步后端新增一个StatsController接收month参数调用上面的Mapper查询返回JSON数组。第三步在首页的JSP里用ECharts接收这个JSON并渲染柱状图初始化代码大致如下$.ajax({ url: /stats/attendanceRate, data: {month: 2025-06}, dataType: json, success: function(res) { var chart echarts.init(document.getElementById(attendanceChart)); chart.setOption({ xAxis: {data: res.map(item item.deptName)}, yAxis: {}, series: [{type: bar, data: res.map(item item.avgAttendanceDays)}] }); } });这个改造绕开了复杂的权限和业务流程只动统计查询和前端展示但对评委来说它证明了你理解数据流转的完整链路。做这个功能时我吃过一个亏当月没有考勤记录的部门会被查询结果漏掉柱状图缺失部门。后来在Java端先查一次部门全列表再和统计结果做一次Map合并缺失的部门补0问题才解决。从那以后我每次写统计类SQL都会先问自己这个查询结果集能不能覆盖全量主表没有覆盖就补一次外层映射。希望这篇笔记能帮你把这套SSM人力资源系统跑通该踩的坑我都踩过了照着做能省不少功夫。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?