MyBatis-Plus字段更新策略详解与实战方案
1. MyBatis-Plus更新策略深度解析MyBatis-Plus作为MyBatis的增强工具包在数据库操作层面提供了诸多便捷功能。其中字段更新策略是日常开发中最容易遇到问题的场景之一。默认情况下MyBatis-Plus会忽略值为null的字段更新这个设计背后有着合理的考量数据安全性防止前端传递null意外覆盖数据库已有值性能优化减少不必要的SQL字段更新业务合理性null通常表示无意义而非要清空这种策略在3.5.17版本中依然保持对应的Spring Boot版本通常为2.7.x系列。理解这个机制需要先了解FieldStrategy枚举类它定义了五种字段策略public enum FieldStrategy { IGNORED, // 忽略判断 NOT_NULL, // 非NULL判断 NOT_EMPTY, // 非空判断 DEFAULT, // 跟随全局配置 NEVER // 不加入SQL }关键提示在3.4.0版本中NOT_EMPTY对字符串还会检查blank状态2. 四种解决方案实战指南2.1 全局配置方案在application.yml中设置全局策略mybatis-plus: global-config: db-config: update-strategy: ignored这种方式的优缺点非常明显优点配置简单一劳永逸缺点影响所有更新操作可能带来安全隐患实测发现在3.5.17版本中这种配置会影响BaseMapper的updateById方法但对UpdateWrapper不产生作用。如果项目中使用多种更新方式需要特别注意这点。2.2 字段注解方案在实体类字段上使用TableField注解TableField(updateStrategy FieldStrategy.IGNORED) private String phoneNumber;这种方案更加精准可控只对特定字段生效支持不同字段采用不同策略与全局配置可以组合使用踩坑记录当字段同时设置insertStrategy和updateStrategy时更新操作只会读取updateStrategy配置2.3 UpdateWrapper动态方案使用UpdateWrapper进行条件更新UpdateWrapperUser wrapper new UpdateWrapper(); wrapper.set(email, null) .eq(id, 1); userMapper.update(null, wrapper);这种方法的特点是完全绕过实体类字段策略SQL直接生成SET emailnull需要手动处理字段名映射问题特别注意在3.5.x版本中如果wrapper.set()传入的是实体对象仍然会受到字段策略影响。2.4 自定义SQL方案在Mapper.xml中编写完整SQLupdate idupdateWithNull UPDATE user SET username#{username}, phone#{phone,jdbcTypeVARCHAR}, emailNULL WHERE id#{id} /update这种方案的适用场景需要精确控制个别字段行为复杂更新逻辑性能敏感型操作3. 深度对比与选型建议3.1 方案对比表方案精确度侵入性维护成本适用场景全局配置低低低快速原型开发字段注解中中中常规业务开发UpdateWrapper高高高动态条件更新自定义SQL最高最高最高复杂业务逻辑3.2 版本兼容性注意事项不同MyBatis-Plus版本存在行为差异3.0.xFieldStrategy枚举值较少3.4.x引入NOT_EMPTY对字符串的blank检查3.5.x优化了UpdateWrapper的处理逻辑在Spring Boot集成时要注意2.5.x → MyBatis-Plus 3.4.x2.7.x → MyBatis-Plus 3.5.x3.0.x → MyBatis-Plus 最新版4. 生产环境实战经验4.1 分页查询的特殊情况当使用分页查询结合更新操作时PageUser page new Page(1, 10); page.addOrder(OrderItem.asc(create_time)); // 注意null值排序在不同数据库表现不同 userMapper.selectPage(page, queryWrapper);常见问题MySQL中NULL在ASC排序时最先出现Oracle需要NVL函数处理分页后再更新可能导致数据不一致4.2 批量更新性能优化对于批量更新null值的场景// 错误示范 - N1问题 list.forEach(item - userMapper.updateById(item)); // 正确做法 - 批量更新 userMapper.updateBatchById(list);性能对比测试1000条数据单条更新约1200ms批量更新约200ms注意批量更新需要开启事务4.3 JSON字段处理技巧当处理包含null的JSON字段时TableField(typeHandler FastjsonTypeHandler.class) private JSONObject extInfo;使用Hutool工具类转换时JSONUtil.parseObj({\key\:null}); // 注意这里的null会被保留5. 常见问题排查指南5.1 问题现象更新不生效排查步骤检查MyBatis-Plus版本确认全局配置是否加载查看实体类注解检查SQL日志输出5.2 问题现象NPE异常可能原因实体类字段未加TableField注解使用LambdaUpdateWrapper时方法引用错误类型处理器不匹配解决方案// 使用Wrapper时要判空 wrapper.set(ObjectUtil.isNotNull(value), column, value);5.3 问题现象字段被意外更新防护措施使用TableField(updatefalse)在Wrapper中明确指定字段启用SQL拦截器审计6. 高级技巧与最佳实践6.1 动态策略切换方案通过自定义MetaObjectHandler实现public class CustomMetaObjectHandler implements MetaObjectHandler { Override public void updateFill(MetaObject metaObject) { if (needUpdateNull()) { this.strictUpdateFill(metaObject, field, String.class, null); } } }6.2 多租户场景下的处理在Saas系统中要注意租户ID字段永远不能为null使用自动填充避免遗漏更新前校验租户权限6.3 审计字段的特殊处理对于createTime/updateTime等字段TableField(fill FieldFill.INSERT_UPDATE) private LocalDateTime updateTime;即使设置为null自动填充也会覆盖这个值