首页 / 资讯中心 / 文章详情

Spring Boot企业工资管理系统设计实战:从数据库到部署全流程解析

Spring Boot企业工资管理系统设计实战:从数据库到部署全流程解析 ★ FEATURED ARTICLE
企业工资管理系统在毕业设计和中小型项目里出现频率极高但真正能拿得出手的交付物代码只是其中一部分。最近我整理了一套基于Spring Boot的企业工资管理系统完整源码附带数据库初始化脚本和配套设计文档从登录、员工档案、部门维护、工资项配置到工资单批量生成、Excel导出核心流程都跑通了。这篇文章不打算照着课程设计说明书念我直接把设计取舍、数据库建表思路、工资计算核心代码、部署上手流程和常见坑都过一遍。适合准备做类似系统的人参考也适合接手这类源码项目、需要快速二次开发的同学按图索骥。1. 项目整体设计与技术选型1.1 这个工资管理系统到底要解决什么问题很多需求方只会给一句话做一个企业工资管理。但你一细问就发现背后的业务痛点很具体。人事每个月要从各部门收Excel工资表不同部门格式还不一样销售提成和绩效经常调整每次改了某个人的工资项公式跟着Excel拉一遍错了还不好查年底要追溯上个月工资明细时历史工资单已经改得面目全非。所以做这个系统之前我先理清边界它不需要大而全的ERP也不需要考勤排班核心就是把“员工基础档案 工资项目设置 月度工资核算 工资单查看与导出”这四件事做成一条闭环。标题里特别提到“源码数据库文档”说明交付物不是单点代码而是一套能初始化、能运行、能说明白的完整资产。项目一开始我就按这个目标把三类内容并行组织。1.2 技术选型为什么是Spring Boot MyBatis-Plus MySQL技术栈并没有选多新潮整套体系用下来就是稳。后端用Spring Boot数据访问用MyBatis-Plus数据库用MySQL 8.x前端用Thymeleaf模板加Bootstrap登录用轻量拦截器实现。具体选型理由如下表模块选择理由后端框架Spring Boot 2.7.18自动配置省掉大量XML配置内置Tomcat打jar包就能跑版本不追太高生态最稳数据访问MyBatis-Plus单表CRUD几乎不用写SQLQueryWrapper/LambdaQueryWrapper动态拼条件很方便数据库MySQL 8.x免费、部署简单、中文文档多InnoDB utf8mb4满足工资数据落库需求前端渲染Thymeleaf Bootstrap单体项目用服务端渲染最省事不需要单独起一个Node服务权限控制拦截器 Session小项目够用如果要上Spring Security后续可以平滑替换这里要重点说一句版本问题。Spring Boot 3.x发布后很多老版本依赖直接失效了因为包名从javax变成了jakarta。如果你拿到的是一个“Spring Boot版本太高”导致启动报错的项目大概率就是组件没跟上。我在这个项目里主动锁定2.7.18不是因为它新而是因为跟MyBatis-Plus、MySQL驱动、Druid这些组件的兼容性已经被大量项目验证过。等基础功能稳定后再考虑往3.x迁移那时候也只是局部替换的问题。1.3 模块拆分与功能边界我按业务密切程度把系统拆成几个模块每个模块只管一件事基础档案模块部门管理、员工管理负责维护工资核算的主数据。工资标准模块工资项配置比如基本工资、岗位工资、绩效、补贴、社保、公积金。工资核算模块月度工资单生成、批量计算、工资单查询和导出。系统管理模块登录用户、角色权限、基础数据初始化。为什么要这样拆因为工资计算最怕“主数据混乱”。员工部门调动、工资项调整都不应该影响已经生成的历史工资单。模块独立后工资核算只依赖员工表、部门表和工资项表的当前快照逻辑清晰排查问题的时候也能快速定位是哪一层出了问题。2. 数据库设计工资核心数据的落点2.1 核心表结构员工、部门、工资项、工资单、用户工资系统的数据库不需要几十张表关键是表与表之间的关系要符合业务直觉。我为这个项目设计了5张核心表分别是部门表、员工表、工资项配置表、工资单表和系统用户表。建表脚本如下CREATE DATABASE IF NOT EXISTS salary_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE salary_db; -- 部门表 CREATE TABLE department ( id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT 主键, dept_name VARCHAR(100) NOT NULL COMMENT 部门名称, dept_code VARCHAR(50) NOT NULL COMMENT 部门编码, parent_id BIGINT DEFAULT 0 COMMENT 父部门ID, status TINYINT DEFAULT 1 COMMENT 状态 1启用 0停用, create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间 ) ENGINEInnoDB COMMENT 部门表; -- 员工表 CREATE TABLE employee ( id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT 主键, emp_no VARCHAR(30) NOT NULL COMMENT 工号, emp_name VARCHAR(50) NOT NULL COMMENT 姓名, dept_id BIGINT NOT NULL COMMENT 部门ID, position VARCHAR(50) COMMENT 岗位, phone VARCHAR(20) COMMENT 手机号, hire_date DATE COMMENT 入职日期, status TINYINT DEFAULT 1 COMMENT 状态 1在职 0离职, create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间 ) ENGINEInnoDB COMMENT 员工表; -- 工资项配置表 CREATE TABLE salary_item ( id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT 主键, employee_id BIGINT NOT NULL COMMENT 员工ID, basic_salary DECIMAL(10,2) NOT NULL DEFAULT 0 COMMENT 基本工资, post_salary DECIMAL(10,2) NOT NULL DEFAULT 0 COMMENT 岗位工资, performance_salary DECIMAL(10,2) NOT NULL DEFAULT 0 COMMENT 绩效工资, allowance DECIMAL(10,2) NOT NULL DEFAULT 0 COMMENT 补贴, overtime_salary DECIMAL(10,2) NOT NULL DEFAULT 0 COMMENT 加班工资, social_security DECIMAL(10,2) NOT NULL DEFAULT 0 COMMENT 个人社保, housing_fund DECIMAL(10,2) NOT NULL DEFAULT 0 COMMENT 个人公积金, effective_date DATE NOT NULL COMMENT 生效日期, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB COMMENT 工资项配置表; -- 工资单表 CREATE TABLE salary_sheet ( id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT 主键, employee_id BIGINT NOT NULL COMMENT 员工ID, salary_month VARCHAR(7) NOT NULL COMMENT 工资月份 格式2025-06, basic_salary DECIMAL(10,2) NOT NULL DEFAULT 0, post_salary DECIMAL(10,2) NOT NULL DEFAULT 0, performance_salary DECIMAL(10,2) NOT NULL DEFAULT 0, allowance DECIMAL(10,2) NOT NULL DEFAULT 0, overtime_salary DECIMAL(10,2) NOT NULL DEFAULT 0, should_salary DECIMAL(10,2) NOT NULL DEFAULT 0 COMMENT 应发工资, social_security DECIMAL(10,2) NOT NULL DEFAULT 0 COMMENT 社保, housing_fund DECIMAL(10,2) NOT NULL DEFAULT 0 COMMENT 公积金, tax_amount DECIMAL(10,2) NOT NULL DEFAULT 0 COMMENT 个人所得税, actual_salary DECIMAL(10,2) NOT NULL DEFAULT 0 COMMENT 实发工资, status TINYINT DEFAULT 0 COMMENT 状态 0未发放 1已发放, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_employee_month (employee_id, salary_month) ) ENGINEInnoDB COMMENT 工资单表; -- 系统用户表 CREATE TABLE sys_user ( id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT 主键, username VARCHAR(50) NOT NULL COMMENT 用户名, password VARCHAR(100) NOT NULL COMMENT 密码BCrypt加密, real_name VARCHAR(50) COMMENT 真实姓名, role VARCHAR(20) DEFAULT USER COMMENT 角色 ADMIN/USER, status TINYINT DEFAULT 1 COMMENT 状态 1启用 0禁用 ) ENGINEInnoDB COMMENT 系统用户表;有没有发现工资单表把工资项里的字段基本复制了一遍这里故意这么做。工资单是“结果表”它必须保存生成那一刻的工资项快照。如果只保存员工ID等以后修改了基本工资历史工资单的数据也跟着变那发过的工资就没有任何追溯依据了。快照设计是工资系统的一个核心原则。2.2 工资项配置、计算公式与状态管理工资项配置表里没有直接用“收入类型/扣除类型”的字典字段而是把每类工资分成独立列这样查询简单、代码直观。工资计算公式我统一放在服务层应发工资 基本工资 岗位工资 绩效工资 补贴 加班工资扣除合计 个人社保 个人公积金 个人所得税实发工资 应发工资 - 扣除合计工资项配置不是一次性的。同一个员工下个月调薪了我不会去修改旧记录而是插入一条新的工资项记录用effective_date指定生效日期。生成某个月工资单时取工资项表中生效日期小于等于当前月份、且日期最大的那条记录。这个逻辑和工资单快照配合起来历史账就非常干净。员工表和部门表状态管理也需要注意。员工离职不能物理删除用status0表示离职否则工资单表的外键引用会直接断裂。部门停用也是一个道理。逻辑状态和逻辑删除不同逻辑删除是给记录加deleted字段但这里用状态字段就足够了还能避免唯一索引和逻辑删除互相打架的问题。2.3 索引、字符集与事务设计的关键细节数据库设计里的几个细节很容易被忽略但直接影响稳定性。字符集必须用utf8mb4数据库里如果存员工姓名里的特殊字符或表情符号utf8mb4才不会有乱码。工资字段一律用DECIMAL(10,2)绝对不要用float或double。二进制浮点数在加减时会出现0.10.2不等于0.3的尴尬工资数据差一分钱都可能被人事盯上。工资单表的UNIQUE KEY uk_employee_month (employee_id, salary_month)是这个系统的保险丝。同一个员工同一个月只能存在一条工资单这是数据库层面最后的屏障。即使代码里有并发请求同时提交生成工资单数据库也能挡住重复数据。事务方面工资生成方法必须加Transactional(rollbackFor Exception.class)避免算到一半出现异常导致只插入了一部分员工的数据。3. 核心功能开发与关键代码实现3.1 项目骨架与依赖配置项目结构按常规Spring Boot单体应用组织即可src/main/java/com/example/salary/ ├── controller/ # 控制层 ├── service/ # 业务层 ├── mapper/ # MyBatis-Plus Mapper接口 ├── entity/ # 实体类 ├── config/ # 配置类比如分页插件、拦截器 ├── common/ # Result、异常处理、工具类 └── SalaryApplication.javapom.xml里的依赖不需要太多够用就好parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.7.18/version relativePath/ /parent dependencies !-- Web -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- Thymeleaf 模板引擎 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-thymeleaf/artifactId /dependency !-- MyBatis-Plus -- dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3.1/version /dependency !-- MySQL 驱动 -- dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId scoperuntime/scope /dependency !-- Lombok -- dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency /dependencies关键配置我写在application.yml里。有两点踩过坑第一是数据库连接URL里的serverTimezoneAsia/Shanghai不设这个在高版本MySQL驱动下容易报时区错误第二是useSSLfalse本地开发环境没必要开SSL省得连接时卡半天。server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/salary_db?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue username: root password: 123456 mybatis-plus: configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0MyBatis-Plus的“实体字段自动转驼峰”默认开启这样数据库字段emp_name能直接映射到实体属性empName。分页插件需要单独注册一个配置类Configuration public class MybatisPlusConfig { Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); PaginationInnerInterceptor paginationInterceptor new PaginationInnerInterceptor(DbType.MYSQL); paginationInterceptor.setMaxLimit(500L); interceptor.addInnerInterceptor(paginationInterceptor); return interceptor; } }3.2 统一返回体与全局异常处理前后端联调时最怕接口返回格式不统一。我定义了一个ResultT通用返回体无论成功失败前端都按固定结构解析。Data public class ResultT { private Integer code; private String message; private T data; public static T ResultT success(T data) { ResultT result new Result(); result.setCode(200); result.setMessage(success); result.setData(data); return result; } public static T ResultT error(String message) { ResultT result new Result(); result.setCode(500); result.setMessage(message); return result; } }全局异常处理类用RestControllerAdvice实现。业务里遇到“本月工资单已存在”“员工不存在”这类情况时直接抛一个自定义BusinessException异常处理器统一转成Result.error。这样做最大的好处是Controller层代码很干净不用每个方法里都写try-catch。3.3 数据访问层MyBatis-Plus的条件构造器与分页查询工资单查询页面往往有多个筛选条件按月、按部门、按员工姓名、按状态。如果写XML手拼动态SQL代码会膨胀得很厉害。MyBatis-Plus的LambdaQueryWrapper非常适合这个场景。public PageResultSalarySheet pageQuery(int page, int size, String salaryMonth, Long deptId, String empName) { PageSalarySheet pageParam new Page(page, size); LambdaQueryWrapperSalarySheet wrapper Wrappers.lambdaQuery(); wrapper.eq(StringUtils.hasText(salaryMonth), SalarySheet::getSalaryMonth, salaryMonth) .like(StringUtils.hasText(empName), SalarySheet::getEmpName, empName) .eq(deptId ! null, SalarySheet::getDeptId, deptId) .orderByDesc(SalarySheet::getCreateTime); PageSalarySheet result salarySheetMapper.selectPage(pageParam, wrapper); return new PageResult(result.getTotal(), result.getRecords()); }这里为了查询方便我让工资单实体里冗余了empName、deptId字段。虽然表结构上工资单和员工、部门是关联关系但查询列表时频繁join会让接口响应变慢冗余几个字段是工资报表系统的常见做法代价只是多存几个字符。3.4 工资批量计算与工资单生成逻辑拆解这是整个系统最核心的一块也是我写代码时最谨慎的地方。工资单生成方法大致分这几步查出当前月份所有在职员工逐个取每个员工的工资项配置计算应发、扣除、个税、实发批量写入工资单表。核心代码片段如下Service RequiredArgsConstructor public class SalarySheetService { private final EmployeeMapper employeeMapper; private final SalaryItemMapper salaryItemMapper; private final SalarySheetMapper salarySheetMapper; Transactional(rollbackFor Exception.class) public int generateSalarySheet(String salaryMonth) { // 防重按月查已有工资单 Long existed salarySheetMapper.selectCount( Wrappers.SalarySheetlambdaQuery() .eq(SalarySheet::getSalaryMonth, salaryMonth) ); if (existed ! null existed 0) { throw new BusinessException(salaryMonth 工资单已生成请勿重复操作); } ListEmployee employees employeeMapper.selectList( Wrappers.EmployeelambdaQuery() .eq(Employee::getStatus, 1) ); ListSalarySheet sheets new ArrayList(); for (Employee emp : employees) { SalarySheet sheet buildSheetByEmployee(salaryMonth, emp); sheets.add(sheet); } if (CollectionUtils.isNotEmpty(sheets)) { salarySheetMapper.insert(sheets); // MyBatis-Plus 3.5.3 支持批量insert } return sheets.size(); } private SalarySheet buildSheetByEmployee(String salaryMonth, Employee emp) { SalaryItem item getEffectiveSalaryItem(emp.getId(), salaryMonth); BigDecimal basic item.getBasicSalary(); BigDecimal post item.getPostSalary(); BigDecimal performance item.getPerformanceSalary(); BigDecimal allowance item.getAllowance(); BigDecimal overtime item.getOvertimeSalary(); BigDecimal should basic.add(post).add(performance).add(allowance).add(overtime); BigDecimal social item.getSocialSecurity(); BigDecimal housing item.getHousingFund(); BigDecimal taxBase should.subtract(social).subtract(housing).max(BigDecimal.ZERO); BigDecimal tax CalcUtils.calculateMonthlyTax(taxBase); BigDecimal actual should.subtract(social).subtract(housing).subtract(tax); SalarySheet sheet new SalarySheet(); sheet.setEmployeeId(emp.getId()); sheet.setEmpName(emp.getEmpName()); sheet.setDeptId(emp.getDeptId()); sheet.setSalaryMonth(salaryMonth); sheet.setBasicSalary(basic); sheet.setPostSalary(post); sheet.setPerformanceSalary(performance); sheet.setAllowance(allowance); sheet.setOvertimeSalary(overtime); sheet.setShouldSalary(should); sheet.setSocialSecurity(social); sheet.setHousingFund(housing); sheet.setTaxAmount(tax); sheet.setActualSalary(actual); sheet.setStatus(0); return sheet; } private SalaryItem getEffectiveSalaryItem(Long employeeId, String salaryMonth) { // 取生效日期当前月份的最近一条工资项配置 return salaryItemMapper.selectOne( Wrappers.SalaryItemlambdaQuery() .eq(SalaryItem::getEmployeeId, employeeId) .le(SalaryItem::getEffectiveDate, salaryMonth -01) .orderByDesc(SalaryItem::getEffectiveDate) .last(limit 1) ); } }有的项目为了演示效果会在工资单里临时加几块钱补贴写成BasicSalary new BigDecimal(0.01)。这里强烈不建议用double做任何运算我在项目里封装了CalcUtils工具类内部统一使用BigDecimal并且保留两位小数四舍五入用RoundingMode.HALF_UP。这样至少不会在演示时出现工资带一堆小数位的情况。个税计算采用简化版的月度速算扣除法。真实场景还要考虑专项附加扣除、累计预扣法但毕设或中小型演示项目里用月度表已经足够说明问题。我贴一下工具类里常用的核心逻辑public static BigDecimal calculateMonthlyTax(BigDecimal taxable) { // 简化示例仅演示计算逻辑实际以税务政策为准 if (taxable.compareTo(new BigDecimal(3000)) 0) { return taxable.multiply(new BigDecimal(0.03)); } else if (taxable.compareTo(new BigDecimal(12000)) 0) { return taxable.multiply(new BigDecimal(0.10)) .subtract(new BigDecimal(210)); } else { return taxable.multiply(new BigDecimal(0.20)) .subtract(new BigDecimal(1410)); } }使用Transactional的原因很明确工资单生成涉及多个表的查询和写入如果批量插入过程中某一条数据有问题整个批次必须回滚不能出现这个部门算了、那个部门没算的中间状态。另外我建议在生成前先按月份查一下是否有数据再用数据库唯一索引兜底双保险。3.5 登录与权限控制的轻量实现权限这块我没有直接上Spring Security而是用拦截器加Session实现。对于这种演示性项目简单方案反而更容易让初学者看懂。拦截器逻辑是检查当前Session有没有loginUser没有就重定向到登录页如果有再看看请求的URL是否以/admin/开头是的话要求用户角色必须是ADMIN。LoginUser的实体只保存用户ID、用户名、真实姓名和角色。密码入库时用BCrypt加密不要存明文。如果后续要升级成Spring Security现有的用户表和角色字段可以平滑过渡不需要重写数据层。4. 数据库脚本、项目文档与部署交付4.1 数据库脚本的交付规范项目交付时我习惯把数据库脚本拆成三个文件放在sql/目录下sql/ ├── 01_create_database.sql # 建库、字符集设置 ├── 02_schema.sql # 表结构 └── 03_init_data.sql # 初始化数据部门、测试员工、测试用户三步分清楚是非常重要的。实际交付时接手的人只要按顺序执行就能得到一套完整的、带测试数据的数据库。如果你把所有内容塞进一个SQL文件一旦某段报错后面全停排查很痛苦。MySQL命令行导入可以这样操作mysql -uroot -p sql/01_create_database.sql mysql -uroot -p sql/02_schema.sql mysql -uroot -p sql/03_init_data.sql测试用户初始化脚本至少要给一个admin / 123456方便验收方登录进去直接看到数据。4.2 项目文档要覆盖哪些内容“源码数据库文档”里的文档如果只是把代码贴一遍那没什么价值。我整理文档时会分成几份README.md项目简介、运行环境、快速启动步骤。需求说明.md业务背景、功能模块、用例说明。数据库设计.md表结构、字段说明、ER关系、核心设计思路。部署文档.md环境准备、打包命令、常见启动问题。测试用例.md登录、员工增删改查、工资生成、工资单导出的操作步骤和预期结果。如果客户要求正式的Word版文档后端生成Word我推荐用poi-tl基于模板填充变量的方式很成熟前端如果只想简单整理记录很多Js库也能把内容导出成Word。但我的实际经验是设计文档先用Markdown维护最后再统一转成Word或PDF交付省去边写边排版的痛苦。4.3 从源码到运行打包部署全流程拿到源码后的运行路径很固定我把完整流程写成部署文档里的标准步骤安装JDK 8、Maven 3.6、MySQL 8.x。创建数据库并依次执行三个SQL脚本。修改application.yml里的数据库账号、密码。在项目根目录执行mvn clean package -DskipTests。启动服务java -jar target/salary-system-0.0.1-SNAPSHOT.jar。浏览器访问http://localhost:8080。Linux服务器上通常用nohup让进程后台运行nohup java -jar target/salary-system-0.0.1-SNAPSHOT.jar logs/run.log 21 用Docker部署的话我一般写一个多阶段构建的Dockerfile先Maven打包再拷贝jar包到JRE镜像。如果服务器上装了宝塔面板通过它的Docker管理器也能一键编排Spring Boot容器主要是把3306端口和8080端口映射关系配好数据库连接地址写成宿主机IP或内网地址即可。5. 常见问题与排查经验5.1 Spring Boot版本太高导致的兼容性问题最近接手过几个“启动就报错”的Spring Boot项目点开pom一看Spring Boot用的3.2.xMyBatis-Plus还停在3.5.1。它们之间的兼容性问题很典型Spring Boot 3.x把Java EE API从javax迁移到jakarta老版本MyBatis-Plus内部还在用javax注解运行时直接抛ClassNotFoundException或NoClassDefFoundError。排查这类问题第一步看启动日志里的Caused by第二步检查依赖版本是否和Spring Boot主版本匹配。对于毕设和内部项目我建议暂时固定用2.7.18把精力放在业务逻辑上。如果非要升级Spring Boot 3.x记得同步升级MyBatis-Plus到3.5.3Druid和Hutool等工具库版本也要一起核对。5.2 工资数据精度与重复生成问题工资计算出现0.30000000000000004这种结果是经典的浮点精度问题。根源就是用了double。解决办法只有一个所有涉及钱的字段都用BigDecimal数据库字段用DECIMAL(10,2)从源头杜绝精度丢失。重复生成工资单的问题我也遇到过不止一次。代码里明明判断了“该月工资单已存在”但前端连点两次生成按钮瞬间并发两个请求两个请求都通过了检查最后插入了两条记录。如果没有唯一索引工资单就重复了。所以我坚持在数据库表上加uk_employee_month唯一索引再从代码层做一次存在性校验做到两级防重。5.3 数据库连接、时区、字符集问题Spring Boot连接MySQL 8经常报的异常有这几个Unknown database salary_db说明没执行建库脚本或者在执行前忘了建库。The server time zone value Öйú±ê׼ʱ¼ä is unrecognizedURL里加serverTimezoneAsia/Shanghai。Public Key Retrieval is not allowedURL里加allowPublicKeyRetrievaltrue。中文乱码数据库、表、连接URL三处都要统一为utf8mb4。我还在application.yml里配置了log-impl: org.apache.ibatis.logging.stdout.StdOutImpl这样开发阶段能在控制台直接看到SQL语句排查数据库问题非常方便。生产环境记得关掉这个输出否则日志文件会膨胀得很快。5.4 数据备份与历史工资单归档工资数据是敏感数据备份不能只图省事。我至少会用mysqldump做每日全量备份mysqldump -uroot -p salary_db salary_backup_$(date %Y%m%d).sql如果后续业务量上来要做主从复制可以开启MySQL的binlog日志然后借助Canal、DataX这类成熟的数据库同步工具把数据实时同步到另一台从库或大数据平台。不建议自己写同步程序这类工具已经有很完整的高可用方案我只需要在主库上配置好账号和binlog_format剩下的交给同步组件就行。最后分享一个我交付这类源码项目时的小习惯。我不会直接把整个项目打包丢给对方而是先找一台干净的机器按照“建库、执行脚本、改连接配置、打包、启动”的顺序完整跑一遍确认从新建数据库到页面登录整个链路没问题才交付。这个习惯救过我很多次最容易翻车的不是业务代码而是数据库脚本没按顺序执行、连接配置里的时区不对、端口没放行。你只要把这一条做进交付流程后面会省掉大量答疑成本。
阅读完成 · 觉得有帮助?
咨询建站