MyBatis-Plus 实战踩坑:updateById 不更新 null 字段的“幽灵问题“
📅 2026/7/11 6:50:45
👁️ 阅读次数
📝 编程学习
一、问题背景
最近在做一个采购系统的重新提交功能。业务场景是:采购员退回采购单后,删除 1 个物资明细,重新提交。
我需要把"被前端删除的明细"从 task 上"解绑"——回退到"待处理"状态。具体来说:
- source_task_id 设为 null(解除与当前 task 的关联)
- demand_pool_status 设为 '0'(回到待处理)
- 等等字段
诡异的现象 :
- updateById 返回 rows=1 (看起来更新成功了)
- 解绑后 demandPoolStatus=0 生效了 (待处理状态变更成功)
- 但 sourceTaskId 还是原来的值 (没被解绑)
二、排查过程
MyBatis-Plus 的 updateById 默认使用 FieldStrategy.NOT_NULL 策略 ——当字段值为 null 时, 不会 包含到 UPDATE SQL 中。
detail.setSourceTaskId(null); detail.setDemandPoolStatus("0"); detailMapper.updateById(detail);生成的 SQL 是:
UPDATE zdk_purchase_detail SET demand_pool_status = '0', flow_status = '1', current_stage = 2 WHERE id = ?没有 SET source_task_id = NULL 这部分!所以 source_task_id 在 DB 中保持原值。
三、解决方案
方案 1:使用 UpdateWrapper.set("field", null) 强制更新(推荐)
UpdateWrapper<ZdkPurchaseDetail> uw = new UpdateWrapper<>(); uw.eq("id", detailId); uw.set("source_task_id", null); uw.set("source_stage_id", null); uw.set("demand_pool_status", "0"); uw.set("flow_status", "1"); uw.set("current_stage", 2); uw.set("audit_quantity", null); uw.set("delivery_info", null); uw.set("payment_terms", null); zdkPurchaseDetailMapper.update(null, uw);原理 : UpdateWrapper.set("field", null) 会 强制 生成 SET field = NULL 的 SQL,绕开 FieldStrategy.NOT_NULL 的检查。
方案 2:字段级配置 @TableField 注解
@TableField(value = "source_task_id", updateStrategy = FieldStrategy.IGNORED) private String sourceTaskId;适用场景 :单个字段频繁需要 set null。
方案 3:全局配置 application.yml
mybatis-plus: global-config: db-config: field-strategy: ignored
编程学习
技术分享
实战经验