简介
说明
本文用示例介绍Spring(SpringBoot)如何使用BeanUtils拷贝对象属性(忽略空值)。
BeanUtils类所在的包
有两个包都提供了BeanUtils类:
- Spring的(推荐):org.springframework.beans.BeanUtils
- Apache的:org.apache.commons.beanutils.BeanUtils
为什么要忽略空值拷贝?
- 默认情况下,BeanUtils会覆盖数据
- 在拷贝属性时,如果源数据的某个字段是null,会把目标数据的值给覆盖为null
忽略null值拷贝属性的用法
BeanUtils.copyProperties(Object source, Object target, String... ignoreProperties)
注意:由于BeanUtils这个方法是浅拷贝,所以本文不考虑对象里边有其他对象的这种情况。
获取null属性名(工具类)
见:SpringBoot-获得对象属性为空值的属性名 – 自学精灵
实例
本处为了全面,将以下情况都考虑进去:
- 继承了某个类
代码结构
Entity
公共Entity
package com.knife.example.common.entity; import lombok.Data; import java.time.LocalDateTime; /** * 公共实体类 */ @Data public class CommonEntity { /** * 创建时间 */ private LocalDateTime createTime; /** * 修改时间 */ private LocalDateTime updateTime; }
ProductVO
package com.example.entity; import lombok.Data; @Data public class User { private Long id; private String userName; private String nickName; // 0:正常 1:被锁定 private Integer status; }
工具类
package com.knife.example.common.util; import org.springframework.beans.BeanWrapper; import org.springframework.beans.BeanWrapperImpl; import org.springframework.util.StringUtils; import java.beans.PropertyDescriptor; import java.util.HashSet; import java.util.Set; public class PropertyUtil { public static String[] readEmptyPropertyNames(Object source) { BeanWrapper src = new BeanWrapperImpl(source); PropertyDescriptor[] pds = src.getPropertyDescriptors(); Set<String> emptyNames = new HashSet<>(); for (PropertyDescriptor pd : pds) { String filedName = pd.getName(); Object srcValue = src.getPropertyValue(pd.getName()); if (srcValue == null) { emptyNames.add(filedName); } else { if (srcValue instanceof String) { if (!StringUtils.hasText((String) srcValue)) { emptyNames.add(filedName); } } } } String[] result = new String[emptyNames.size()]; return emptyNames.toArray(result); } }
Controller
package com.knife.example.business.product.controller; import com.knife.example.business.product.vo.ProductVO; import com.knife.example.common.util.PropertyUtil; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.beans.BeanUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.time.LocalDateTime; import java.util.Arrays; @Api(tags = "商品") @RestController @RequestMapping("product") public class ProductController { @ApiOperation("查询详情") @GetMapping("detail") public ProductVO detail(Long id) { // 源对象 ProductVO productVO = new ProductVO(); productVO.setId(10L); productVO.setName(""); productVO.setStockQuantity(null); productVO.setCreateTime(LocalDateTime.now()); productVO.setUpdateTime(null); System.out.println("------------------------------------------------"); // 目标对象 ProductVO productVOTarget = new ProductVO(); productVOTarget.setId(9L); productVOTarget.setName("键盘"); productVOTarget.setStockQuantity(150); productVOTarget.setCreateTime(LocalDateTime.now().minusHours(3)); productVOTarget.setUpdateTime(LocalDateTime.now().minusHours(3)); // 默认不忽略空值 BeanUtils.copyProperties(productVO, productVOTarget); //读出空值字段名 // String[] emptyPropertyNames = PropertyUtil.readEmptyPropertyNames(productVO); // System.out.println("空值字段名:" + Arrays.toString(emptyPropertyNames)); // //忽略空值拷贝 // BeanUtils.copyProperties(productVO, productVOTarget, emptyPropertyNames); return productVOTarget; } }
测试1:默认情况
结论
- 默认情况下,null和空字符串会覆盖目标对象的属性
测试2:忽略空值拷贝
修改一下Controller,改为如下:
package com.knife.example.business.product.controller; import com.knife.example.business.product.vo.ProductVO; import com.knife.example.common.util.PropertyUtil; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.beans.BeanUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.time.LocalDateTime; import java.util.Arrays; @Api(tags = "商品") @RestController @RequestMapping("product") public class ProductController { @ApiOperation("查询详情") @GetMapping("detail") public ProductVO detail(Long id) { // 源对象 ProductVO productVO = new ProductVO(); productVO.setId(10L); productVO.setName(""); productVO.setStockQuantity(null); productVO.setCreateTime(LocalDateTime.now()); productVO.setUpdateTime(null); // 目标对象 ProductVO productVOTarget = new ProductVO(); productVOTarget.setId(9L); productVOTarget.setName("键盘"); productVOTarget.setStockQuantity(150); productVOTarget.setCreateTime(LocalDateTime.now().minusHours(3)); productVOTarget.setUpdateTime(LocalDateTime.now().minusHours(3)); // 默认不忽略空值 // BeanUtils.copyProperties(productVO, productVOTarget); //读出空值字段名 String[] emptyPropertyNames = PropertyUtil.readEmptyPropertyNames(productVO); System.out.println("空值字段名:" + Arrays.toString(emptyPropertyNames)); //忽略空值拷贝 BeanUtils.copyProperties(productVO, productVOTarget, emptyPropertyNames); return productVOTarget; } }
结果:
结论:
成功的避免了空值覆盖。
请先
!