BeanUtils.copyProperties()所有的空值不复制问题

 更新时间:2022年06月15日 10:22:04   作者:Garfield_cat_cat  
这篇文章主要介绍了BeanUtils.copyProperties()所有的空值不复制问题及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

BeanUtils.copyProperties()所有的空值不复制

第一种情况

所有为空值的属性都不copy

直接上代码吧~

public class UpdateUtil {
    /**
     * 所有为空值的属性都不copy
     *
     * @param source
     * @param target
     */
    public static void copyNullProperties(Object source, Object target) {
        BeanUtils.copyProperties(source, target, getNullField(source));
    }
    /**
     * 获取属性中为空的字段
     *
     * @param target
     * @return
     */
    private static String[] getNullField(Object target) {
        BeanWrapper beanWrapper = new BeanWrapperImpl(target);
        PropertyDescriptor[] propertyDescriptors = beanWrapper.getPropertyDescriptors();
        Set<String> notNullFieldSet = new HashSet<>();
        if (propertyDescriptors.length > 0) {
            for (PropertyDescriptor p : propertyDescriptors) {
                String name = p.getName();
                Object value = beanWrapper.getPropertyValue(name);
                if (Objects.isNull(value)) {
                    notNullFieldSet.add(name);
                }
            }
        }
        String[] notNullField = new String[notNullFieldSet.size()];
        return notNullFieldSet.toArray(notNullField);
    }
    public static void main(String[] args) {
        TopMenuConfigEntity topMenuConfigEntity1 = new TopMenuConfigEntity();
        topMenuConfigEntity1.setWardCode("cat");
        topMenuConfigEntity1.setTitle("animal");
        TopMenuConfigEntity topMenuConfigEntity2 = new TopMenuConfigEntity();
        topMenuConfigEntity2.setWardCode("dog");
        UpdateUtil.copyNullProperties(topMenuConfigEntity2,topMenuConfigEntity1);
        System.out.println(topMenuConfigEntity1.getTitle());
    }
}

执行main 方法后,topMenuConfigEntity1的title还是为原来的“animal”值,没有被topMenuConfigEntity2 的空值覆盖。

第二种情况

原对象的属性有值,复制时指定某些字段不复制

调BeanUtils的这个方法

public static void copyProperties(Object source, Object target, String... ignoreProperties) throws BeansException {
        copyProperties(source, target, null, ignoreProperties);
    }
 public static void main(String[] args) {
        TopMenuConfigEntity topMenuConfigEntity1 = new TopMenuConfigEntity();
        topMenuConfigEntity1.setWardCode("cat");
        topMenuConfigEntity1.setTitle("animal");
        topMenuConfigEntity1.setCreateTime(new Date());
        TopMenuConfigEntity topMenuConfigEntity2 = new TopMenuConfigEntity();
        String[] ignoreArray = new String[]{"title","createTime"};
        BeanUtils.copyProperties(topMenuConfigEntity2,topMenuConfigEntity1,ignoreArray);
        System.out.println("title : "+topMenuConfigEntity2.getTitle() +";createTime :" + topMenuConfigEntity2.getCreateTime());
    }

topMenuConfigEntity2的title 和createTime为null,没有复制

BeanUtils.copyProperties()的用法和注意点

属性为null也会被复制,内部类不会复制过去

BeanUtils提供对Java反射和自省API的包装。其主要目的是利用反射机制对JavaBean的属性进行处理。我们知道,一个JavaBean通常包含了大量的属性,很多情况下,对JavaBean的处理导致大量get/set代码堆积,增加了代码长度和阅读代码的难度。

BeanUtils是这个包里比较常用的一个工具类,这里只介绍它的copyProperties()方法。

该方法源码如下:

public static void copyProperties(Object source, Object target) throws BeansException {
        copyProperties(source, target, (Class)null, (String[])null);
    }
 
public static void copyProperties(Object source, Object target, Class<?> editable) throws BeansException {
        copyProperties(source, target, editable, (String[])null);
    }
 
public static void copyProperties(Object source, Object target, String... ignoreProperties) throws BeansException {
        copyProperties(source, target, (Class)null, ignoreProperties);
    }
 
private static void copyProperties(Object source, Object target, Class<?> editable, String... ignoreProperties) throws BeansException {
        Assert.notNull(source, "Source must not be null");
        Assert.notNull(target, "Target must not be null");
        Class<?> actualEditable = target.getClass();
        if(editable != null) {
            if(!editable.isInstance(target)) {
                throw new IllegalArgumentException("Target class [" + target.getClass().getName() + "] not assignable to Editable class [" + editable.getName() + "]");
            }
 
            actualEditable = editable;
        }
 
        PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
        List<String> ignoreList = ignoreProperties != null?Arrays.asList(ignoreProperties):null;
        PropertyDescriptor[] var7 = targetPds;
        int var8 = targetPds.length;
 
        for(int var9 = 0; var9 < var8; ++var9) {
            PropertyDescriptor targetPd = var7[var9];
            Method writeMethod = targetPd.getWriteMethod();
            if(writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
                PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
                if(sourcePd != null) {
                    Method readMethod = sourcePd.getReadMethod();
                    if(readMethod != null && ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {
                        try {
                            if(!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
                                readMethod.setAccessible(true);
                            }
 
                            Object value = readMethod.invoke(source, new Object[0]);
                            if(!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
                                writeMethod.setAccessible(true);
                            }
 
                            writeMethod.invoke(target, new Object[]{value});
                        } catch (Throwable var15) {
                            throw new FatalBeanException("Could not copy property '" + targetPd.getName() + "' from source to target", var15);
                        }
                    }
                }
            }
        }
 
    }

如果你有两个具有很多相同属性的JavaBean,就可以试用该方法将sourse中的属性copy到target中,如果sourse和target间存在名称不相同的属性,则BeanUtils不对这些属性进行处理,需要程序员手动处理。

怎么样,很方便吧!除BeanUtils外还有一个名为PropertyUtils的工具类,它也提供copyProperties()方法,作用与 BeanUtils的同名方法十分相似,主要的区别在于后者提供类型转换功能,即发现两个JavaBean的同名属性为不同类型时,在支持的数据类型范围内进行转换,而前者不支持这个功能,但是速度会更快一些。

BeanUtils支持的转换类型如下:

* java.lang.BigDecimal   
  
* java.lang.BigInteger   
  
* boolean and java.lang.Boolean   
  
* byte and java.lang.Byte   
  
* char and java.lang.Character   
  
* java.lang.Class   
  
* double and java.lang.Double   
  
* float and java.lang.Float   
  
* int and java.lang.Integer   
  
* long and java.lang.Long   
  
* short and java.lang.Short   
  
* java.lang.String   
  
* java.sql.Date   
  
* java.sql.Time   
  
* java.sql.Timestamp

这里要注意一点,java.util.Date是不被支持的,而它的子类java.sql.Date是被支持的。因此如果对象包含时间类型的属性,且希望被转换的时候,一定要使用java.sql.Date类型。否则在转换时会提示argument mistype异常。

现在,还有一个坏消息:使用BeanUtils的成本惊人地昂贵!我做了一个简单的测试,BeanUtils所花费的时间要超过取数 据、将其复制到对应的 value对象(通过手动调用get和set方法),以及通过串行化将其返回到远程的客户机的时间总和。所以要小心使用。

注意点一

apache和spring的工具包中都有BeanUtils,使用其中的copyProperties方法可以非常方便的进行这些工作,但在实际应用中发现,对于null的处理不太符合个人的需要,例如在进行修改操作中只需要对model中某一项进行修改,那么一般我们在页面上只提交model的ID及需要修改项的值,这个时候使用BeanUtils.copyProperties会将其他的null绑定到pojo中去。

大家可以直接调用我们加工类的copyPropertiesIgnoreNull()方法即可忽略null值,避免老数据被null覆盖的尴尬。具体代码如下:

import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware; 
import java.util.HashSet;
import java.util.Set; 

public class SpringUtil implements ApplicationContextAware {
 
    /** 
     * 当前IOC 
     *  
     */  
    private static ApplicationContext applicationContext;  
 
    /** 
     * * 设置当前上下文环境,此方法由spring自动装配 
     *  
     */  
    @Override  
    public void setApplicationContext(ApplicationContext arg0)  
            throws BeansException {  
        applicationContext = arg0;  
    }  
 
    /** 
     * 从当前IOC获取bean 
     * 
     * @param id 
     * bean的id 
     * @return 
     *
     */  
    public static Object getObject(String id) {  
        Object object = null;  
        object = applicationContext.getBean(id);  
        return object;  
    } 
    
    public static String[] getNullPropertyNames (Object source) {
        final BeanWrapper src = new BeanWrapperImpl(source);
        java.beans.PropertyDescriptor[] pds = src.getPropertyDescriptors();
 
        Set<String> emptyNames = new HashSet<String>();
        for(java.beans.PropertyDescriptor pd : pds) {
            Object srcValue = src.getPropertyValue(pd.getName());
            if (srcValue == null) emptyNames.add(pd.getName());
        }
        String[] result = new String[emptyNames.size()];
        return emptyNames.toArray(result);
    }
 
    public static void copyPropertiesIgnoreNull(Object src, Object target){
        BeanUtils.copyProperties(src, target, getNullPropertyNames(src));
    } 
}

调用:copyPropertiesIgnoreNull

public class TestBeanUtiles {
    public static void main(String[] args) {
        NewPerson newPerson = new NewPerson();
        newPerson.setName("bifuguo");//前台用户更新过的数据,例如前台只修改了用户名
        //下面我们假设是从数据库加载出来的老数据
        OldPerson oldPerson = new OldPerson();
        oldPerson.setSex("nv");
        oldPerson.setAge(5);
        //如果我们想把新数据更新到老数据这个对象里面,我们就可以借助BeanUtils.copyProperties()的方法如下:
        //BeanUtils.copyProperties(newPerson, oldPerson);
        SpringUtil.copyPropertiesIgnoreNull(newPerson, oldPerson);
        System.out.println(newPerson.toString());
        System.out.println(oldPerson.toString());
    }
}

打印结果:

NewPerson{name='bifuguo', sex='null', age=0}
OldPerson{name='bifuguo', sex='nv', age=0}

现在就可以看出老数据没有被null覆盖

注意点二

1.Spring的BeanUtils的CopyProperties方法需要对应的属性有getter和setter方法;

2.如果存在属性完全相同的内部类,但是不是同一个内部类,即分别属于各自的内部类,则spring会认为属性不同,不会copy;

3.泛型只在编译期起作用,不能依靠泛型来做运行期的限制;

4.最后,spring和apache的copy属性的方法源和目的参数的位置正好相反,所以导包和调用的时候都要注意一下。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Nacos启动出现failed to req API:/nacos/v1/ns/instance after all servers问题

    Nacos启动出现failed to req API:/nacos/v1/ns/insta

    这篇文章主要介绍了Nacos启动出现failed to req API:/nacos/v1/ns/instance after all servers问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-08-08
  • Java遍历并删除Map的四种方法对比

    Java遍历并删除Map的四种方法对比

    在Java中,遍历并删除 Map 中的元素有四种常见的方法,每种方法都有其适用场景和优缺点,下面小编就来和大家详细介绍一下这几种方法的具体实现吧
    2024-10-10
  • Android Java判断密码强度方法实例(强度显示)

    Android Java判断密码强度方法实例(强度显示)

    在现代社会中,密码是确保我们个人信息和账户安全的重要手段之一,为了提高密码的安全性,我们可以使用正则表达式来判断密码的强度,这篇文章主要给大家介绍了关于Android Java判断密码强度(强度显示)的相关资料,需要的朋友可以参考下
    2024-03-03
  • SpringBoot中使用@ControllerAdvice注解详解

    SpringBoot中使用@ControllerAdvice注解详解

    这篇文章主要介绍了SpringBoot中使用@ControllerAdvice注解详解,@ControllerAdvice,是Spring3.2提供的新注解,它是一个Controller增强器,可对controller中被 @RequestMapping注解的方法加一些逻辑处理,需要的朋友可以参考下
    2023-10-10
  • 获取Spring当前配置的两种方式

    获取Spring当前配置的两种方式

    这篇文章主要给大家介绍了获取Spring当前配置的,两种方式文中通过代码示例给大家介绍的非常详细,对大家的学习或工作有一定的帮助,需要的朋友可以参考下
    2024-01-01
  • net.sf.json.JSONObject 为null 的判断方法

    net.sf.json.JSONObject 为null 的判断方法

    下面小编就为大家带来一篇net.sf.json.JSONObject 为null 的判断方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-02-02
  • 详解Java如何实现图像灰度化

    详解Java如何实现图像灰度化

    这篇文章主要介绍了灰度化的几种方法,以及如何使用Java实现灰度化。同时分析了网上一种常见却并不妥当的Java灰度化实现,以及证明了opencv的灰度化是使用“加权灰度化”法,下面一起来看看。
    2016-08-08
  • Spring boot 在idea中添加热部署插件的图文教程

    Spring boot 在idea中添加热部署插件的图文教程

    这篇文章主要介绍了Spring boot 在idea中添加热部署插件的图文教程,本文通过图文并茂的形式给大家展示具体步骤,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-10-10
  • 解决springboot项目打成jar包后运行时碰到的小坑

    解决springboot项目打成jar包后运行时碰到的小坑

    这篇文章主要介绍了解决springboot项目打成jar包后运行时碰到的小坑,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-02-02
  • Spring Boot 分库分表策略示例展示

    Spring Boot 分库分表策略示例展示

    分库分表是为了应对大规模数据和高并发请求,提高系统的性能和可扩展性,以下是如何在 Spring Boot 中实现分库分表的详细策略,感兴趣的朋友一起看看吧
    2024-08-08

最新评论