从零到一:手把手教你用SpringBoot+MyBatis搭建企业级员工管理系统(附完整源码)
从零到一:手把手教你用SpringBoot+MyBatis搭建企业级员工管理系统(附完整源码)
当企业规模逐渐扩大,传统Excel表格管理员工信息的方式开始捉襟见肘。我曾接手过一个客户案例:他们的人事部门每月要花3天时间手动合并各部门提交的Excel,还经常出现版本冲突和数据错误。这正是我们选择SpringBoot+MyBatis技术栈构建员工管理系统的现实意义——用标准化系统替代碎片化手工操作。
这个系统将实现部门架构管理、员工信息CRUD、文件上传等核心功能,采用主流的三层架构设计。与教学演示项目不同,我们会特别关注企业真实场景中的分页查询优化、事务一致性等实战细节。下面就从开发环境准备开始,带你体验完整的项目搭建过程。
1. 项目初始化与环境搭建
1.1 开发工具准备
工欲善其事,必先利其器。推荐使用以下工具组合:
- IDE:IntelliJ IDEA Ultimate(学生可免费申请许可证)
- JDK:Amazon Corretto 17(LTS版本,企业级支持)
- 数据库:MySQL 8.0 + Navicat Premium(可视化操作更高效)
- 接口测试:Postman + Swagger UI(自动生成API文档)
# 验证Java环境 java -version # 应输出类似内容 openjdk version "17.0.8" 2023-07-18 LTS1.2 项目骨架创建
使用Spring Initializr生成项目基础结构时,需要特别注意依赖选择:
| 依赖项 | 作用 | 企业级考量 |
|---|---|---|
| Spring Web | MVC框架 | 需配置线程池应对高并发 |
| MyBatis | ORM框架 | 建议开启二级缓存 |
| MySQL Driver | 数据库连接 | 生产环境需配置连接池 |
| Lombok | 代码简化 | 需统一团队注解规范 |
// 典型的主启动类配置 @SpringBootApplication @MapperScan("com.yourpackage.mapper") // 自动扫描Mapper接口 public class EmployeeSystemApplication { public static void main(String[] args) { SpringApplication.run(EmployeeSystemApplication.class, args); } }1.3 数据库设计规范
企业级系统需要严谨的数据库设计。以下是核心表结构示例:
CREATE TABLE `dept` ( `id` int NOT NULL AUTO_INCREMENT COMMENT '部门ID', `name` varchar(50) COLLATE utf8mb4_bin NOT NULL COMMENT '部门名称', `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `idx_name` (`name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; CREATE TABLE `emp` ( `id` int NOT NULL AUTO_INCREMENT COMMENT '员工ID', `name` varchar(50) COLLATE utf8mb4_bin NOT NULL COMMENT '员工姓名', `gender` tinyint unsigned NOT NULL COMMENT '性别:1男 2女', `dept_id` int NOT NULL COMMENT '所属部门', `avatar` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '头像URL', `entry_date` date NOT NULL COMMENT '入职日期', `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_dept` (`dept_id`), CONSTRAINT `fk_emp_dept` FOREIGN KEY (`dept_id`) REFERENCES `dept` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;注意:实际项目中建议增加creator_id、modifier_id等审计字段,满足企业合规要求
2. 核心功能模块实现
2.1 部门管理模块
部门作为组织架构的基础单元,其管理功能需要特别注意数据一致性。我们采用MyBatis注解方式实现基础CRUD:
@Mapper public interface DeptMapper { @Select("SELECT * FROM dept WHERE id = #{id}") Dept getById(Integer id); @Insert("INSERT INTO dept(name) VALUES(#{name})") @Options(useGeneratedKeys = true, keyProperty = "id") int insert(Dept dept); @Update("UPDATE dept SET name=#{name} WHERE id=#{id}") int update(Dept dept); @Delete("DELETE FROM dept WHERE id=#{id}") int delete(Integer id); }在Service层添加事务控制是必须的:
@Service @RequiredArgsConstructor public class DeptService { private final DeptMapper deptMapper; private final EmpMapper empMapper; @Transactional(rollbackFor = Exception.class) public void deleteDeptWithEmp(Integer deptId) { empMapper.deleteByDeptId(deptId); // 先删除关联员工 deptMapper.delete(deptId); // 再删除部门 } }2.2 员工管理模块
员工信息管理面临的主要挑战是复杂查询场景。我们采用XML配置方式实现动态SQL:
<!-- src/main/resources/mapper/EmpMapper.xml --> <mapper namespace="com.yourpackage.mapper.EmpMapper"> <sql id="baseColumn">id, name, gender, dept_id, avatar, entry_date</sql> <select id="selectByCondition" resultType="com.yourpackage.pojo.Emp"> SELECT <include refid="baseColumn"/> FROM emp <where> <if test="name != null and name != ''"> name LIKE CONCAT('%', #{name}, '%') </if> <if test="gender != null"> AND gender = #{gender} </if> <if test="deptId != null"> AND dept_id = #{deptId} </if> <if test="beginDate != null and endDate != null"> AND entry_date BETWEEN #{beginDate} AND #{endDate} </if> </where> ORDER BY entry_date DESC </select> </mapper>分页查询推荐使用PageHelper插件,避免手动计算偏移量:
public PageInfo<Emp> queryByPage(EmpQuery query, Integer pageNum, Integer pageSize) { PageHelper.startPage(pageNum, pageSize); List<Emp> list = empMapper.selectByCondition(query); return new PageInfo<>(list); }2.3 文件上传方案对比
企业系统通常需要处理员工头像等文件上传,不同存储方案各有优劣:
| 方案类型 | 实现方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| 本地存储 | MultipartFile.transferTo() | 实现简单,零成本 | 难扩展,备份困难 | 小型内部系统 |
| 云存储OSS | 阿里云SDK | 弹性扩展,高可用 | 产生费用,需网络访问 | 中大型企业应用 |
| 分布式存储 | FastDFS/MinIO | 自主可控,性价比高 | 运维复杂度高 | 有专门运维团队 |
以阿里云OSS为例的核心上传逻辑:
public class OssUtil { private final OSS ossClient; private final String bucketName; public String upload(MultipartFile file, String path) throws IOException { String fileName = generateUniqueName(file.getOriginalFilename()); try (InputStream is = file.getInputStream()) { ossClient.putObject(bucketName, path + fileName, is); return "https://" + bucketName + ".oss-cn-hangzhou.aliyuncs.com/" + path + fileName; } } private String generateUniqueName(String originalName) { return UUID.randomUUID() + originalName.substring(originalName.lastIndexOf(".")); } }3. 系统安全与稳定性设计
3.1 登录认证方案
JWT是目前主流的无状态认证方案,其核心流程包括:
- 用户登录成功后生成Token
- 客户端存储Token并在后续请求的Header中携带
- 服务端验证Token有效性
public class JwtUtil { private static final String SECRET_KEY = "your-256-bit-secret"; private static final long EXPIRATION = 86400000; // 24小时 public static String generateToken(Integer userId) { return Jwts.builder() .setSubject(userId.toString()) .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION)) .signWith(SignatureAlgorithm.HS256, SECRET_KEY) .compact(); } public static Integer parseToken(String token) { Claims claims = Jwts.parser() .setSigningKey(SECRET_KEY) .parseClaimsJws(token) .getBody(); return Integer.valueOf(claims.getSubject()); } }3.2 统一异常处理
企业系统需要规范的错误响应格式,通过@ControllerAdvice实现全局异常捕获:
@RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(BusinessException.class) public Result<Void> handleBusinessException(BusinessException e) { log.error("业务异常: {}", e.getMessage(), e); return Result.fail(e.getCode(), e.getMessage()); } @ExceptionHandler(Exception.class) public Result<Void> handleUnknownException(Exception e) { log.error("系统异常: ", e); return Result.fail(500, "系统繁忙,请稍后重试"); } }3.3 操作日志审计
通过AOP实现关键操作的日志记录,满足企业合规要求:
@Aspect @Component @RequiredArgsConstructor public class OperationLogAspect { private final HttpServletRequest request; private final OperationLogMapper logMapper; @Around("@annotation(operationLog)") public Object around(ProceedingJoinPoint pjp, OperationLog operationLog) throws Throwable { long begin = System.currentTimeMillis(); Object result = pjp.proceed(); long end = System.currentTimeMillis(); OperationLogEntity log = new OperationLogEntity(); log.setOperation(operationLog.value()); log.setUserId(JwtUtil.getCurrentUserId(request)); log.setCostTime(end - begin); log.setMethod(pjp.getSignature().toShortString()); logMapper.insert(log); return result; } }4. 项目优化与部署实践
4.1 性能优化策略
面对企业级数据量,需要实施多层次的性能优化:
- MyBatis二级缓存:在mapper接口添加@CacheNamespace注解
- 连接池配置:推荐使用HikariCP替代默认连接池
spring: datasource: hikari: maximum-pool-size: 20 connection-timeout: 30000 idle-timeout: 600000 - Nginx动静分离:将静态资源交由Nginx直接处理
4.2 容器化部署
Docker部署能显著提高环境一致性,示例Dockerfile:
FROM amazoncorretto:17-alpine WORKDIR /app COPY target/employee-system.jar app.jar EXPOSE 8080 ENTRYPOINT ["java","-jar","app.jar"]配套的docker-compose.yml可整合MySQL和Redis:
version: '3' services: app: build: . ports: - "8080:8080" depends_on: - mysql - redis mysql: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: yourpassword MYSQL_DATABASE: employee_db redis: image: redis:alpine4.3 监控与告警
企业系统需要建立完善的监控体系:
- Spring Boot Actuator:暴露健康检查端点
management: endpoints: web: exposure: include: health,metrics,info - Prometheus + Grafana:实现可视化监控
- 日志收集:ELK或Loki+Graylog方案
5. 源码结构与扩展建议
完整项目采用标准Maven多模块结构:
employee-system ├── employee-common # 公共模块 ├── employee-dao # 数据访问层 ├── employee-service # 业务逻辑层 ├── employee-web # Web接口层 └── employee-start # 启动模块对于需要扩展的功能建议:
- 组织架构树:使用闭包表(Closure Table)实现多级部门
- 员工离职流程:添加状态机管理员工生命周期
- 数据权限控制:基于部门树实现数据隔离
- 导入导出:集成EasyExcel处理大批量数据
在真实项目开发中,我们通常会建立标准的Git分支策略:
master:生产环境对应分支release/*:预发布分支develop:集成开发分支feature/*:功能开发分支
