如何高效更新JPA Entity实体,无需逐个判断非空字段手动赋值
方案1:使用Spring自带的BeanUtils工具类(快速实现,适合中小项目)
无需引入额外依赖,通过反射自动识别非空字段完成赋值,注意不要导错包,必须使用Spring自带的org.springframework.beans.BeanUtils,不要用Apache Commons的同名工具类。
首先编写通用工具方法获取对象所有为null的属性名:
import org.springframework.beans.BeanWrapper; import org.springframework.beans.BeanWrapperImpl; import java.beans.PropertyDescriptor; import java.util.HashSet; import java.util.Set; public static String[] getNullPropertyNames (Object source) { final BeanWrapper src = new BeanWrapperImpl(source); PropertyDescriptor[] pds = src.getPropertyDescriptors(); Set<String> emptyNames = new HashSet<>(); for(PropertyDescriptor pd : pds) { Object srcValue = src.getPropertyValue(pd.getName()); // 如果需要把空字符串也判定为不更新,可修改判断条件为 srcValue == null || "".equals(srcValue) if (srcValue == null) { emptyNames.add(pd.getName()); } } return emptyNames.toArray(new String[0]); }
改造后的更新方法:
import org.springframework.beans.BeanUtils; public void updateCustomer(Customer customer) { Customer existingCustomer = customerRepository.findById(customer.getId()) .orElseThrow(() -> new RuntimeException("对应客户不存在")); // 拷贝属性时忽略所有传入对象为null的字段 BeanUtils.copyProperties(customer, existingCustomer, getNullPropertyNames(customer)); customerRepository.save(existingCustomer); }
方案2:使用MapStruct(高性能,适合中大型、高并发场景)
MapStruct是编译期生成映射代码,没有反射开销,性能和手写代码一致,同时会在编译期校验字段映射合法性,避免运行时异常。
首先引入Maven依赖:
<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>
编写映射接口:
import org.mapstruct.Mapper; import org.mapstruct.MappingTarget; import org.mapstruct.NullValuePropertyMappingStrategy; // 配置null值属性不参与映射,自动注册为Spring Bean @Mapper(componentModel = "spring", nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE) public interface CustomerMapper { // 把源对象的非空字段更新到目标对象上 void updateNonNullField(Customer source, @MappingTarget Customer target); }
改造后的业务代码:
@Autowired private CustomerMapper customerMapper; public void updateCustomer(Customer customer) { Customer existingCustomer = customerRepository.findById(customer.getId()) .orElseThrow(() -> new RuntimeException("对应客户不存在")); customerMapper.updateNonNullField(customer, existingCustomer); customerRepository.save(existingCustomer); }
可选优化:配合JPA @DynamicUpdate注解
在Customer实体类上加@DynamicUpdate注解,JPA生成更新SQL时只会包含实际被修改的字段,不会全字段更新,可进一步降低数据库写入开销、减少行锁冲突:
@Entity @DynamicUpdate public class Customer { @Id @GeneratedValue(strategy = GenerationType.AUTO) public long id; public String name; public String phone; //... }
内容的提问来源于stack exchange,提问作者Marcin_S
相关产品推荐
相关产品推荐

