前言
- 最近在设计通用的 Service 和 Controller 层
- 设计过程中涉及到实体对象(JPA)的更新操作
- 原因1:JPA 的
saveAndFlush
方法会将把 null 也更新上去
- 原因2:spring 的
BeanUtils.copyBeanProperties
方法会把 src 所有属性值包括 null 覆盖到 dest,不符合要求
- 所以,利用反射,写一个属性复制方法代替 spring 的工具方法
- 另外,controller 层使用
@ModelAttribut
也可以解决这个问题,这就是另一个主题
代码 copyBeanPropertiesIgoreNull
/**
* 对象属性值拷贝,null 值覆盖修复版
* @param beanSrc
* @param beanDest
*/
public static void copyBeanPropertiesIgoreNull(Object beanSrc, Object beanDest){
Class<?> clazzSrc = beanSrc.getClass();
Class<?> clazzDest = beanDest.getClass();
//获取所有属性,包括私有的和继承的
List<Field> allFields = getAllFields(beanSrc);
try {
for(Field field:allFields) {
String fieldName = field.getName(); //属性名
if("serialVersionUID".equals(fieldName)) {
continue;
}
PropertyDescriptor pd1 = getPropertyDescriptor(fieldName, clazzSrc);
if(pd1!=null) {
Method rMethod = pd1.getReadMethod();
if(rMethod!=null) {
Object fieldValue = rMethod.invoke(beanSrc); //属性值,引用类型,所以一般实体的属性用 Integer instead of int
if(fieldValue!=null) { //关键:属性值为 null 就不要覆盖了
PropertyDescriptor pd2 = getPropertyDescriptor(fieldName, clazzDest);
if(pd2!=null) {
Method wMethod = pd2.getWriteMethod();
if(wMethod!=null) {
wMethod.invoke(beanDest, fieldValue);
}
}
}
}
}
}
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
throw new RuntimeException(">> copyPropertiesIgoreNull exception", e);
}
}