ARTICLE DETAIL

资讯详情

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

SSM+Maven+MySQL企业人事管理系统实战:从建表到部署完整指南

SSM+Maven+MySQL企业人事管理系统实战:从建表到部署完整指南 做JavaWeb课设或者毕设的同学SSMMavenMySQL这套组合应该是接触频率最高的搭配了。基于javaweb和mysql的ssmmaven企业人事管理系统我用SSM框架完整实现了一个可运行、可部署、可扩展的企业人事管理系统覆盖员工信息管理、部门维护、考勤记录、薪资处理等典型人事业务场景。这篇文章不是简单的“项目介绍”而是把我从建表到部署全过程的思路、踩坑和实操细节都拆开讲适合正在做类似课设/毕设的同学参考也适合想系统搞懂SSM整合链路的开发者拿来当实战案例。1. 项目到底需要管什么人事管理系统的需求与选型逻辑1.1 人事系统要解决的业务问题很多同学一上来就急着建表写代码结果做出来的系统成了“换个名字的CRUD演示”看着功能齐全实际经不起推敲。做企业人事管理系统第一步不是写代码而是把“人事”这事拆明白。一个典型的中小型企业人事管理核心业务无非四块第一员工档案管理包括入职信息、联系方式、岗位、所属部门这是最基础的数据底座第二部门管理部门不是独立的字典员工和部门之间必须有清晰归属第三考勤管理记录日常出勤情况这决定了后续工资计算的依据第四薪资管理根据基础工资、考勤结果、奖惩记录生成工资条。除此之外还需要一个管理员入口区分普通员工能看什么、能改什么这是系统的权限骨架。我最终把功能模块划分成六块管理员登录与权限控制、员工信息管理、部门管理、考勤管理、薪资管理、个人信息维护。这六个模块已经能完整覆盖“管人、管部门、管出勤、管钱”的人事业务闭环同时每一块都有独立的增删改查逻辑非常适合拿来学习SSM每一层的职责划分。1.2 为什么是 SSM Maven MySQL 这个组合选技术栈不是跟风而是要回答“为什么是它”。在我这个项目里SSMSpring SpringMVC MyBatis组合的价值在于三层职责极其清晰Spring 管对象的创建和依赖注入SpringMVC 管请求的接收和响应分发MyBatis 管Java对象和数据库表之间的映射。三层各管一摊遇到问题定位非常快。对比传统的 JSP Servlet 模式SSM 最大的优势是解耦。Servlet 模式下你要手动创建对象、手动获取参数、手动操作 JDBC代码量翻倍不说改一个字段要牵扯好多文件。SSM 把重复劳动交给框架开发者只需要关注业务逻辑本身。MySQL 作为数据存储端理由就更直接了免费、轻量、资料多、生态成熟学生和企业都在用。Maven 在这个项目里的地位常被低估——它不只是“下载依赖的工具”更是项目生命周期管理工具。我可以通过 pom.xml 统一管理 jar 包版本、配置打包方式还能用一套命令完成编译、测试、打包比手动往 lib 目录里塞 jar 包强太多。1.3 这套系统适合谁看如果你是正在做课程设计或者毕业设计的计算机专业学生这个项目的业务复杂度和技术覆盖度正好卡在一个“能学到东西又不会失控”的区间。如果你是想系统入门 SSM 整合的开发者从数据库设计到后端分层再到前端页面整个链路是完整的比零散看教程有用得多。我写这篇也会把我在实现过程中的真实选择和踩坑经历放进去希望你看完能少走点弯路。2. 从数据库开始设计核心表结构如何支撑业务2.1 核心表怎么拆数据库设计是很多人容易敷衍过去的一步但说句实在话人事系统后面所有模块写起来顺不顺手90%取决于表设计得好不好。我采用的设计原则是“一张表只干一件事”宁可多拆一张表也不要在一张表里堆满含义混乱的字段。最终落地的表结构是五张核心表管理员表 admin 管系统登录账号部门表 dept 管组织架构员工表 employee 管人员信息考勤表 attendance 管每日出勤薪资表 salary 管月度工资。员工表通过 dept_id 字段关联部门表考勤表和薪资表通过 emp_id 字段关联员工表。这样的关联关系非常直观后端的 SQL Join 写起来也是顺理成章。2.2 建表 SQL 实战我直接把核心建表语句放出来这是我实际调试通过的版本字符集和字段类型都做了处理复制就能跑CREATE DATABASE IF NOT EXISTS company_hr DEFAULT CHARACTER SET utf8mb4; USE company_hr; -- 管理员表 CREATE TABLE admin ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(255) NOT NULL, role VARCHAR(20) DEFAULT ADMIN, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB; -- 部门表 CREATE TABLE dept ( id INT PRIMARY KEY AUTO_INCREMENT, dept_name VARCHAR(100) NOT NULL UNIQUE, dept_leader VARCHAR(50), dept_phone VARCHAR(20), create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB; -- 员工表 CREATE TABLE employee ( id INT PRIMARY KEY AUTO_INCREMENT, emp_no VARCHAR(30) NOT NULL UNIQUE COMMENT 员工编号, name VARCHAR(50) NOT NULL, gender VARCHAR(10), dept_id INT, position VARCHAR(50), phone VARCHAR(20), email VARCHAR(100), entry_date DATE, status VARCHAR(20) DEFAULT 在职, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, CONSTRAINT fk_emp_dept FOREIGN KEY (dept_id) REFERENCES dept(id) ) ENGINEInnoDB; -- 考勤表 CREATE TABLE attendance ( id INT PRIMARY KEY AUTO_INCREMENT, emp_id INT NOT NULL, work_date DATE NOT NULL, sign_in_time VARCHAR(20), sign_out_time VARCHAR(20), status VARCHAR(20) DEFAULT 正常, CONSTRAINT fk_att_emp FOREIGN KEY (emp_id) REFERENCES employee(id) ) ENGINEInnoDB; -- 薪资表 CREATE TABLE salary ( id INT PRIMARY KEY AUTO_INCREMENT, emp_id INT NOT NULL, salary_month VARCHAR(20) NOT NULL COMMENT 工资月份如2025-01, base_salary DECIMAL(10,2) DEFAULT 0, bonus DECIMAL(10,2) DEFAULT 0, deduction DECIMAL(10,2) DEFAULT 0, actual_salary DECIMAL(10,2) DEFAULT 0, is_issued TINYINT DEFAULT 0 COMMENT 是否已发放, issue_time DATETIME, CONSTRAINT fk_sal_emp FOREIGN KEY (emp_id) REFERENCES employee(id) ) ENGINEInnoDB;2.3 字段设计的几处关键细节第一个经验是员工编号 emp_no 一定要设唯一索引。企业场景里员工编号是天然的业务主键身份证号不能直接拿来当主键因为涉及隐私且长度不可控但员工号必须保证唯一。第二个经验是金额字段用 DECIMAL 不用 DOUBLE。DOUBLE 是浮点数存金额会出现精度丢失工资算出来多几分钱少几分钱这在人事系统里是不能接受的。第三个经验是状态字段 status 用字符串存“在职/离职/休假”这种可读值比用 0/1 数字更直观。虽然数字存起来省空间但实际项目里可读性非常重要尤其你半年后回头看自己的代码时字符串一眼能看懂数字你得去翻注释。第四个细节是关于时间的“入职日期”用 DATE“创建时间”用 DATETIME日期和时间精度不同混用会导致查询逻辑很别扭。我在建表时如果存在多个时间字段都会统一检查一遍类型这个习惯让我后面写 SQL 时省了不少麻烦。3. SSM整合时的那些坑配置文件与工程骨架3.1 Maven 的 pom 依赖清单工程骨架我用 Maven 搭建标准打包方式是 war 包。这里直接给一份我调通的 pom.xml 核心依赖版本号都是经过实际验证能稳定跑通的组合properties maven.compiler.source1.8/maven.compiler.source maven.compiler.target1.8/maven.compiler.target spring.version5.3.20/spring.version /properties dependencies !-- Spring核心 -- dependency groupIdorg.springframework/groupId artifactIdspring-context/artifactId version${spring.version}/version /dependency dependency groupIdorg.springframework/groupId artifactIdspring-jdbc/artifactId version${spring.version}/version /dependency !-- SpringMVC -- dependency groupIdorg.springframework/groupId artifactIdspring-webmvc/artifactId version${spring.version}/version /dependency !-- MyBatis和整合包 -- dependency groupIdorg.mybatis/groupId artifactIdmybatis/artifactId version3.5.10/version /dependency dependency groupIdorg.mybatis/groupId artifactIdmybatis-spring/artifactId version2.0.7/version /dependency !-- MySQL驱动注意 8.x 驱动 -- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.28/version /dependency !-- 连接池 -- dependency groupIdcom.alibaba/groupId artifactIddruid/artifactId version1.2.8/version /dependency !-- Servlet/JSP/JSTL -- dependency groupIdjavax.servlet/groupId artifactIdjavax.servlet-api/artifactId version4.0.1/version scopeprovided/scope /dependency dependency groupIdjavax.servlet/groupId artifactIdjstl/artifactId version1.2/version /dependency !-- Jackson用于JSON交互 -- dependency groupIdcom.fasterxml.jackson.core/groupId artifactIdjackson-databind/artifactId version2.13.3/version /dependency !-- PageHelper分页插件 -- dependency groupIdcom.github.pagehelper/groupId artifactIdpagehelper/artifactId version5.3.0/version /dependency /dependencies build finalNameHRSystem/finalName plugins plugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-compiler-plugin/artifactId version3.8.1/version configuration source1.8/source target1.8/target encodingUTF-8/encoding /configuration /plugin /plugins /build这里有一个非常容易踩的坑JDK 8 环境下 spring 不要用 6.xmysql-connector 不要混用 5.x 和 8.x。我一开始用的 Spring 6.0 配上 JDK 8启动直接报错后来统一降到 5.3.x 才稳定。MySQL 8.x 的驱动类名是com.mysql.cj.jdbc.Driver如果你还用 5.x 的com.mysql.jdbc.Driver启动会提示驱动类已废弃甚至直接连接失败。3.2 Spring MyBatis MVC 配置文件SSM 项目通常有三份核心配置文件applicationContext.xmlSpring 主配置、springMVC.xmlSpringMVC 配置、jdbc.properties数据库连接信息。我建议再拆一个 mybatis-config.xml 放 MyBatis 的全局设置虽然非必需但结构更清爽。jdbc.properties 这里要注意 MySQL 8 的时区参数jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/company_hr?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalse jdbc.usernameroot jdbc.password123456serverTimezoneAsia/Shanghai不加的话MySQL 8 通常会报 CST 时区错误这个错误很典型会出现在控制台第一屏很多同学看到英文报错就懵了其实就是时区没指定。useSSLfalse是避免本地连接时 SSL 握手警告刷屏不影响功能。applicationContext.xml 的核心配置段是开启注解扫描、加载 properties、配置 Druid 数据源、声明 SqlSessionFactory、配置 Mapper 扫描器context:component-scan base-packagecom.hr context:exclude-filter typeannotation expressionorg.springframework.stereotype.Controller/ /context:component-scan context:property-placeholder locationclasspath:jdbc.properties/ bean iddataSource classcom.alibaba.druid.pool.DruidDataSource property namedriverClassName value${jdbc.driver}/ property nameurl value${jdbc.url}/ property nameusername value${jdbc.username}/ property namepassword value${jdbc.password}/ /bean bean idsqlSessionFactory classorg.mybatis.spring.SqlSessionFactoryBean property namedataSource refdataSource/ property nameconfigLocation valueclasspath:mybatis-config.xml/ property namemapperLocations valueclasspath:mapper/*.xml/ /bean bean classorg.mybatis.spring.mapper.MapperScannerConfigurer property namebasePackage valuecom.hr.mapper/ /bean关键点在于context:component-scan里要把 Controller 排除掉否则 Spring 和 SpringMVC 两套容器都会扫描 Controller导致事务代理混乱、请求映射冲突。这是 SSM 整合中很隐蔽的一个问题报错往往不是立刻出现而是表现为某些功能偶尔失效排查起来特别费时间。springMVC.xml 的配置核心是开启注解驱动、设置 JSP 视图解析器、放行静态资源mvc:annotation-driven/ context:component-scan base-packagecom.hr.controller/ bean classorg.springframework.web.servlet.view.InternalResourceViewResolver property nameprefix value/WEB-INF/views// property namesuffix value.jsp/ /bean mvc:default-servlet-handler/mvc:default-servlet-handler这个配置容易被忽略但它很重要。如果不配你的 CSS、JS、图片这些静态资源会被前端控制器拦截页面上所有样式全部失效控制台会报 404。我见过很多同学配好了后端接口却卡在静态资源上页面白板一片其实就是这一行配置的事。3.3 用 IDEA 跑通 JavaWeb 项目的环境配置工欲善其事必先利其器。我用的是 IntelliJ IDEA这里分享一下 IDEA 跑 JavaWeb 项目最关键的几个配置点。第一Maven 配置。打开 Settings - Maven把 Maven home path 指向你本地解压的 Maven 目录User settings file 指向conf/settings.xml。强烈建议在 settings.xml 里配置阿里云镜像否则你下载 Spring 全家桶依赖能等到怀疑人生。我用的镜像配置是这样的mirror idaliyunmaven/id mirrorOfcentral/mirrorOf name阿里云公共仓库/name urlhttps://maven.aliyun.com/repository/public/url /mirror第二Project Structure 里确认 SDK 是 JDK 1.8Language level 也是 8否则代码里用 Lambda 表达式或者 try-with-resources 会报语法错误。第三配置 Tomcat。点击 Run - Edit Configurations - 左上角加号 - Tomcat Server - Local然后配置 Deployment把 artifact 选成HRSystem:war exploded。这里记住一个经验开发调试用 war exploded 热部署模式上线部署用 war 包。exploded 模式修改 JSP 或静态资源不用重启就能生效能省大量时间。第四如果启动时端口冲突Tomcat 默认 8080 端口改一下 HTTP port 就行。MySQL 如果也是 3306 端口被占用需要在 jdbc.properties 里同步更新。4. 人事业务模块落地从 Controller 到 Mapper 的完整链路4.1 登录与权限拦截SSM 里的拦截器实践登录功能是人事系统的门面所有操作都建立在“你是谁、你能干什么”的基础上。我用 SpringMVC 的拦截器做了一套轻量级权限控制。登录的 Controller 接收用户名和密码调用 Service 查 admin 表校验通过后把用户信息放进 session。同时实现一个LoginInterceptor继承HandlerInterceptorAdapter在preHandle方法里判断 session 中是否有用户没有就重定向到登录页。public class LoginInterceptor extends HandlerInterceptorAdapter { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session request.getSession(); Object admin session.getAttribute(loginUser); if (admin null) { response.sendRedirect(request.getContextPath() /login); return false; } return true; } }在 springMVC.xml 注册拦截器并配置拦截规则mvc:interceptors mvc:interceptor mvc:mapping path/**/ mvc:exclude-mapping path/login/ mvc:exclude-mapping path/css/**/ mvc:exclude-mapping path/js/**/ mvc:exclude-mapping path/images/**/ /mvc:interceptor /mvc:interceptors这里有两个细节值得注意。第一静态资源必须放行否则登录页都加载不出 CSS。第二/login这个路径必须排除掉否则你永远进不了登录页面会陷入“无法登录所以无法进入系统系统不允许匿名访问所以无法登录”的死循环。4.2 员工分页与多条件查询PageHelper 的实践员工管理是人事系统最核心的模块。我在实现时给它设计了三个维度完整列表展示、按部门筛选、按关键字搜索。为了不让页面一次加载几千条数据我还接入了 PageHelper 分页插件。Controller 层的代码逻辑比较典型Controller RequestMapping(/employee) public class EmployeeController { Autowired private EmployeeService employeeService; RequestMapping(/list) public String list(RequestParam(defaultValue 1) Integer pageNum, RequestParam(defaultValue 5) Integer pageSize, String deptId, String keyword, Model model) { PageInfoEmployee pageInfo employeeService.findPage( pageNum, pageSize, deptId, keyword); model.addAttribute(pageInfo, pageInfo); model.addAttribute(deptId, deptId); model.addAttribute(keyword, keyword); return employee/list; } }Service 层只做一件事调用 Mapper 并启动分页Override public PageInfoEmployee findPage(Integer pageNum, Integer pageSize, String deptId, String keyword) { PageHelper.startPage(pageNum, pageSize); ListEmployee list employeeMapper.selectByCondition(deptId, keyword); return new PageInfo(list); }Mapper 接口对应的方法是ListEmployee selectByCondition(Param(deptId) String deptId, Param(keyword) String keyword);Mapper XML 里的动态 SQL 是核心where标签可以自动去掉多余的 ANDif标签实现条件拼接select idselectByCondition resultTypecom.hr.entity.Employee SELECT * FROM employee e where if testdeptId ! null and deptId ! AND e.dept_id #{deptId} /if if testkeyword ! null and keyword ! AND (e.name LIKE CONCAT(%, #{keyword}, %) OR e.emp_no LIKE CONCAT(%, #{keyword}, %)) /if /where ORDER BY e.id DESC /select这里我吃过一个亏就是 MySQL 的 like 查询如果不加 CONCAT 而直接写LIKE %#{keyword}%MyBatis 会把它当成普通字符串处理结果查询出来永远为空。正确写法是CONCAT(%, #{keyword}, %)。这种细节不实际跑一遍根本发现不了属于典型业务开发经验。4.3 考勤与薪资模块两种常见业务模式的实现考勤模块我采用的是“每天打卡一次、按月汇总”的轻量模式。数据录入通过表单提交工作日期和员工 ID 唯一重复提交时更新记录而不是插入新记录。我利用INSERT ... ON DUPLICATE KEY UPDATE来做这种处理需要配合唯一索引(emp_id, work_date)insert idinsertOrUpdate INSERT INTO attendance (emp_id, work_date, sign_in_time, sign_out_time, status) VALUES (#{empId}, #{workDate}, #{signInTime}, #{signOutTime}, #{status}) ON DUPLICATE KEY UPDATE sign_in_time #{signInTime}, sign_out_time #{signOutTime}, status #{status} /insert薪资模块的逻辑更贴近业务。工资条的实际发放值actual_salary不是手填的而是根据基础工资、奖金、扣款计算出来的base_salary bonus - deduction。我在 Service 层的generateSalary方法里做计算然后批量写入 salary 表。前端 JSP 层面薪资列表页展示工资条管理员可以按月生成、标记发放。这里我引入了 JSTL 的fmt:formatNumber标签来做金额格式化避免页面上出现 10000.0 这种不好看的数据fmt:formatNumber value${salary.actualSalary} pattern#,##0.00/5. JSP页面与前端交互列表、表单、校验一网打尽5.1 列表页与分页展示后端数据准备好了前端页面是用户体验的第一道关卡。人事系统的页面我用了 JSP JSTL EL 表达式来完成数据渲染再配合 CSS JavaScript 做交互效果。员工列表页最核心的逻辑是分页导航和条件查询的表单保持。分页用 JSTL 的c:forEach遍历页号点击页号时把当前查询条件带回去。我起初犯过一个错误点击第二页后查询条件丢失了因为请求参数里没携带 deptId 和 keyword。解决办法是在分页链接里把查询条件作为参数拼上去c:forEach begin1 end${pageInfo.pages} varpageNum a href${pageContext.request.contextPath}/employee/list?pageNum${pageNum} deptId${deptId}keyword${keyword}${pageNum}/a /c:forEach这种“参数透传”是 JSP 列表页最容易被忽略的细节却是系统是否好用的关键分水岭。用户翻第二页就丢了筛选条件很多系统就是这么被吐槽“难用”的。5.2 表单回显与 JS 校验新增和编辑员工我共用了一张表单页面通过 URL 参数区分模式新增时 form 里的 action 指向/employee/add编辑时指向/employee/update并且后端把当前员工对象放进 Model前端通过${employee.empNo}这样的 EL 表达式回显数据。JavaScript 校验这块我做了三件事必填项非空校验、手机号格式校验、邮箱格式校验。手机上号码校验用正则/^1[3-9]\d{9}$/测试群里反馈说这个表达式在 Chrome 和 Edge 上都很稳定。校验通过后再提交表单不通过则 alert 提示并阻止提交。有一点我觉得值得强调前端校验只是提升体验后端 Service 层必须做二次校验。我在 Controller 层手动判断了姓名是否为空、手机号正则是否匹配避免绕过页面直接构造请求进库。做企业系统不能只防老实人该堵的口子都得堵上。5.3 CSS 与公共资源的组织方式页面样式我采用了统一的公共样式表style.css所有页面都在head里通过link引入。为了让页面更统一我定义了一套公共的按钮类.btn-primary、.btn-danger、表格类.table-bordered这样任何页面要用按钮和表格直接套类名就行不用每页重写样式。这里有一个上线的陷阱要提醒大家JSP 页面里的 CSS、JS 引用路径一定用${pageContext.request.contextPath}不要写死/HRSystem/xxx这种绝对路径。我第一次部署到不同机器上项目名变了所有静态资源全部 404页面直接裸奔。改用${pageContext.request.contextPath}后不管部署成什么名字都能自动适配这个坑踩得记忆深刻。前端和后台的交互方面如果有些动作不想刷新整页可以用 jQuery 的$.ajax配合ResponseBody返回 JSON。我在删除员工、标记工资发放这两个操作上用了这种方式前端弹窗确认后异步请求后端成功后局部刷新表格体验比整页刷新顺滑很多。6. Maven 构建与部署从 IDEA 到 Tomcat 的生产之路6.1 本地运行全流程整个项目的运行流程我梳理成一条清晰的链路。首先确保本机装好了 JDK 8、Maven 3.6、MySQL 5.7/8.0、Tomcat 8.5/9.0。然后按顺序操作启动 MySQL 服务用 Navicat 或命令行执行项目附带的hr_db.sql初始化数据库和五张表。在 IDEA 中打开项目等待 Maven 自动下载依赖第一次可能比较久建议先配好阿里云镜像。修改jdbc.properties确认数据库密码和本机一致。配置 Tomcat 并添加项目 artifact启动。浏览器访问http://localhost:8080/HRSystem/login输入预置的管理员账号 admin/admin123 登录。很多同学卡在第一步“怎么建库建表”上。如果你用的是 MySQL 8建议直接在命令行执行source命令或者用图形化客户端打开 SQL 文件执行。如果你在导入时遇到编码问题优先把 SQL 文件的编码转换成 UTF-8 再执行中文乱码基本都是这个原因。6.2 打包 war 的部署实践开发环境跑通只是第一步真正的项目还要能部署到独立的 Tomcat。Maven 打包命令是mvn clean package执行完毕后在项目target目录下会生成HRSystem.war。把这个 war 文件复制到 Tomcat 的webapps目录启动 Tomcat它会自动解压并部署项目。访问路径就是http://服务器IP:8080/HRSystem/。打包前有几个坑值得专门说。第一pom.xml中的finalName决定了 war 包的名字也决定了访问路径如果想改访问路径改这里比改 Tomcat 配置更直接。第二Maven 打包默认不会把src/main/java下的 XML 文件打包进去如果你的 Mapper XML 不是放在src/main/resources而是和接口放在一起需要在 build 中加入资源处理配置否则部署后所有查询都会报Invalid bound statement。我在实际开发中统一把 Mapper XML 放在resources/mapper目录下就是为了规避这个典型问题。第三部署时数据库连接信息如果是在 jdbc.properties 中写死的每次换环境都要重新打包。更好的做法是用 JVM 环境变量替换密码部分比如${DB_PASSWORD}在 Tomcat 的setenv.sh或系统环境变量中定义这个值。这个技巧对大型项目的多环境部署特别重要我这个系统虽然规模不大但我也提前用了这种写法后续换环境只需要改环境变量而不需要重新编译。6.3 部署之后常见的启动失败问题我把实际运行中遇到过的报错整理成对照表希望能帮你快速定位问题。报错现象可能原因解决思路启动报 ClassNotFoundException依赖没打全或打包时 lib 目录缺失确认 Maven 依赖完整检查 war 中 WEB-INF/lib 是否有 jar访问页面报 404注解路径不对或视图路径拼错检查 RequestMapping 前缀和 InternalResourceViewResolver 的 prefix 是否对应控制台报 NoSuchBeanDefinitionException扫描包路径写错确认 component-scan base-package 是否覆盖到实际包名查询 SQL 报 Invalid bound statementMapper XML 没被扫描到检查 mybatis mapper-locations 路径和 XML 文件是否在 target 中MySQL 连接报 SSL 或时区错误URL 参数缺失确认 serverTimezone 和 useSSL 参数已配置中文乱码字符集不统一数据库、JSP、Tomcat 连接的 characterEncoding 全部统一为 UTF-8这些报错我在调试时基本都碰过一轮现在回头看大多都是配置层面的问题只要按部就班检查基本都能在十分钟内解决。排查报错的第一原则是“看完整堆栈”不要只看第一行就慌SSM 框架的报错往往在中间几行才露出真实原因。7. 把 JSP、CSS 和 JavaScript 的组织再往前推进一步7.1 公共头部与尾部的组件化思路写了几十个 JSP 页面之后你一定会发现大量重复代码每个页面头部都有一样的前缀声明、CSS 引用、导航栏尾部都有一样的脚本引用和版权信息。我可以对 JSP 进行简单的组件化处理把公共部分抽成独立文件通过% include filecommon/header.jsp %静态引入。这种方式虽然比现代前端框架的组件落后但在传统 JSP 项目里已经能极大提升维护效率。比如导航栏多了一个菜单入口只需要改 header.jsp 一个文件所有页面同步生效不用逐个页面去改那个效率提升是肉眼可见的。7.2 JS 函数的复用封装JavaScript 部分我也做了模块化封装。我把常用的提示、弹窗确认、Ajax 请求封装成公共函数放在js/common.js里比如function confirmDelete(url, tips) { if (!tips) { tips 确定要删除这条记录吗; } if (confirm(tips)) { $.ajax({ url: url, type: POST, success: function (resp) { if (resp success) { alert(操作成功); window.location.reload(); } else { alert(操作失败); } } }); } }这样页面里删除按钮只需要写confirmDelete(${pageContext.request.contextPath}/employee/delete/ id, 确认删除该员工)就能实现统一的交互逻辑。一致性上也有保证后期想改提示样式或弹窗样式只改 common.js 一处即可。7.3 页面性能与细节体验JSP 页面虽然简单但性能体验上仍有一些可以做好的地方。我在列表页的数据量边界做了一层保护如果单页数据超过 100 条就提示用户缩小查询范围。这个设计的出发点很简单数据库查询再快浏览器渲染上千行 DOM 都会卡顿用户翻页体验也会变差。还有一个小细节表单提交之后我会做一个“防止重复提交”的处理。方法是在提交按钮上做 JS 控制第一次点击后禁用按钮避免用户因为网络延迟连续点击导致数据库里出现重复数据。这个在实际使用中经常被忽略但对人事系统这种“录入准确第一”的场景特别值得重视。写在最后的一次完整验证我把整个项目从头到尾又跑了一遍完整流程清库重建、IDEA 启动、Tomcat 部署、打包 war 放进独立 Tomcat 运行、浏览器模拟管理员登录、新增部门、新增员工、录入考勤、生成工资、查验数据。每个环节跑完后基本可以确定这个系统的链路是通的数据一致性和权限拦截都符合预期。实际开发这类 SSM 人事管理系统我的个人体会是业务不复杂复杂的是框架整合的细节环境和配置的坑要远比业务代码的坑多。建表时多花半小时思考字段设计工程启动时多花十分钟核对配置文件后面落地模块会顺畅很多。如果你也正在做类似的 JavaWeb 人事管理系统照着这个思路从数据库设计开始一步步搭建跑通后再扩展公告管理、请假审批这些延展功能会比你直接从网上找一套代码抄下来改名字有收获得多。
返回列表