核心是让Java层真实感知数据库NULL,需用包装类声明字段且禁用默认值、ORM显式支持NULL穿透、更新仅刷非null字段、DTO转Entity手动判空。

Java 中包装类处理数据库 NULL 映射,核心是让 Java 层真实感知 NULL,而不是被默认值“吃掉”或静默转成 0/false。关键不在用不用 Integer,而在整条链路是否对 NULL 敏感且可控。
字段声明必须用包装类,且禁止初始化默认值
所有可能为 NULL 的数据库字段(如 status、score、updateTime),在实体类中必须声明为 Integer、Boolean、LocalDateTime 等包装类型,且不能写初始值:
- ✅ 正确:
private Integer stock;(成员变量天然为 null) - ❌ 错误:
private Integer stock = 0;或private int stock;
写了 = 0,哪怕数据库查出来是 NULL,反序列化后也会变成 0,业务上就丢失了“未设置”的语义。
ORM 配置要显式支持 NULL 穿透
框架默认行为常会掩盖 NULL,需主动干预:
立即学习“Java免费学习笔记(深入)”;
-
JPA/Hibernate:加
@Column(nullable = true),禁用无参构造器里赋默认值;避免@NotNull注解干扰 -
MyBatis:全局设
callSettersOnNulls=true,确保 NULL 字段也调用 setter;<resultMap>中明确写jdbcType=INTEGER对应javaType=Integer -
MyBatis-Plus:慎用
@TableField(fill = FieldFill.INSERT),它会在 INSERT 时强行塞默认值,覆盖原始 NULL 意图
更新操作只刷非 null 字段
防止“没传的字段被默认值覆盖”,不能依赖全量 UPDATE:
-
JPA:启用
@DynamicUpdate,Hibernate 自动只更新已修改字段 -
MyBatis:用动态 SQL,例如:
<choose><when test="stock != null">stock = #{stock}</when></choose> -
Service 层:构建 UpdateWrapper 时,手动判空——
dto.getStock() == null就不调set("stock", ...),保留数据库原值
DTO 转 Entity 必须手写判空,禁用自动拷贝
BeanUtils.copyProperties() 会跳过 null 值字段,结果是旧实体里的默认值(比如 0)被保留,覆盖了“用户想清空”的意图:
- ❌ 错误:
copyProperties(dto, entity)—— dto.score 为 null 时,entity.score 不变 - ✅ 正确:手写或用 MapStruct 显式控制:
if (dto.getScore() != null) { entity.setScore(dto.getScore()); } - 布尔字段特别注意:
isDeleted容易被框架误解析,建议统一用deleted+getDeleted()/setDeleted()
前端不传字段 → DTO 中对应字段为 null → 实体保持原值不变,这才是 NULL 映射该有的样子。


















