如何在JPA更新操作中避免Null检查?属性名不同时最优方案?
避免Null检查实现部分字段更新,MapStruct vs BeanUtils选型
一、核心需求拆解
你要实现的是仅将UpdateUserRequest中的非空值更新到JPA实体,同时消除重复的null判断,还要处理属性名不一致的场景(比如sendDate对应userRequestedDate)。以下是按推荐优先级排序的解决方案:
二、最优方案:使用MapStruct(强烈推荐)
MapStruct是编译期生成映射代码的工具,天生支持忽略null值,能轻松处理属性名/类型差异,性能远优于反射实现的工具。
1. 配置依赖与Mapper接口
先添加Maven依赖(Gradle同理):
<dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct</artifactId> <version>1.5.5.Final</version> </dependency> <dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-processor</artifactId> <version>1.5.5.Final</version> <scope>provided</scope> </dependency>
定义映射接口,配置忽略null值和属性映射规则:
import org.mapstruct.BeanMapping; import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.NullValuePropertyMappingStrategy; @Mapper(componentModel = "spring") // 交给Spring管理Bean public interface UserTransactionMapper { // 忽略源对象的null属性,同时处理不同名属性映射 @BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE) @Mapping(source = "sendDate", target = "userRequestedDate") void updateUserTransactionFromRequest(UpdateUserRequest request, UserTransaction target); }
2. 改造Service代码
注入Mapper后,一行完成映射,彻底消除null判断:
@Transactional public void update(String userId, UpdateUserRequest updateUserRequest) { dataRepository.findByUserId(userId) .ifPresent(userTransaction -> { userTransactionMapper.updateUserTransactionFromRequest(updateUserRequest, userTransaction); dataRepository.save(userTransaction); }); }
三、轻量方案:自定义工具方法(无第三方依赖)
如果不想引入新依赖,可以封装通用的null判断+赋值逻辑:
// 通用更新工具类 public class BeanUpdateUtils { // 通用类型的非空更新 public static <T> void updateIfNotNull(T value, Consumer<T> setter) { if (value != null) { setter.accept(value); } } // 针对日期字符串转Date的重载 public static void updateIfNotNull(String dateStr, Consumer<Date> dateSetter) { if (dateStr != null) { dateSetter.accept(Date.valueOf(dateStr)); } } }
Service代码简化为:
@Transactional public void update(String userId, UpdateUserRequest updateUserRequest) { dataRepository.findByUserId(userId) .ifPresent(userTransaction -> { BeanUpdateUtils.updateIfNotNull(updateUserRequest.getAccountId(), userTransaction::setAccountId); BeanUpdateUtils.updateIfNotNull(updateUserRequest.getUserAmount(), userTransaction::setUserAmount); BeanUpdateUtils.updateIfNotNull(updateUserRequest.getSendDate(), userTransaction::setUserRequestedDate); BeanUpdateUtils.updateIfNotNull(updateUserRequest.getDelDate(), userTransaction::setDelDate); dataRepository.save(userTransaction); }); }
四、为什么不推荐BeanUtils.copyProperties?
不管是Spring还是Apache的BeanUtils,都存在明显缺陷:
- 默认会用源对象的null值覆盖目标对象,要忽略null需额外编写复杂配置,容易出错
- 属性名不同时无法直接映射,需手动处理字段,反而增加复杂度
- 基于反射实现,性能远不如MapStruct的编译期代码,高频调用时损耗明显
- 缺乏类型转换安全校验,比如日期字符串转Date容易抛出运行时异常
总结
- 若需处理属性名/类型差异、追求性能与可维护性,优先选MapStruct
- 简单场景且不想加依赖,用自定义工具方法封装null判断逻辑
- 尽量避免使用BeanUtils.copyProperties,坑多且灵活性差
内容的提问来源于stack exchange,提问作者jcrshankar
相关产品推荐
相关产品推荐

