如何使用springframework BeanUtils copyProperties忽略空值?

Posted

技术标签:

【中文标题】如何使用springframework BeanUtils copyProperties忽略空值?【英文标题】:How to ignore null values using springframework BeanUtils copyProperties? 【发布时间】:2013-11-13 07:05:00 【问题描述】:

我想知道如何使用 Spring Framework 将属性从 Object Source 复制到 Object Dest 并忽略空值。

我实际上是用 Apache beanutils 和这段代码

    beanUtils.setExcludeNulls(true);
    beanUtils.copyProperties(dest, source);

去做。但现在我需要使用 Spring。

有什么帮助吗?

非常感谢

【问题讨论】:

你不能把 BeanUtils 作为你的 Spring 项目类路径的一部分吗?我不认为 Spring 的 BeanUtils 以这种方式工作。 【参考方案1】:

您可以创建自己的方法来复制属性,同时忽略空值。

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);


// then use Spring BeanUtils to copy and ignore null using our function
public static void myCopyProperties(Object src, Object target) 
    BeanUtils.copyProperties(src, target, getNullPropertyNames(src));

【讨论】:

这是一个非常好的解决方案。我希望框架已经安排了一种方法来解决这个问题,但这会完美地工作。谢谢 我也是这样做的。我将这些方法包含在一个带有其他一些自定义实现的新类中。我的新类扩展了 BeanUtils 类,以便我可以添加其他特性和功能。 myCopyProps 方法的 call sig 中有错字... Object 后面多了一个逗号【参考方案2】:

来自 alfredx 的 post 的 Java 8 版本的 getNullPropertyNames 方法:

public static String[] getNullPropertyNames(Object source) 
    final BeanWrapper wrappedSource = new BeanWrapperImpl(source);
    return Stream.of(wrappedSource.getPropertyDescriptors())
            .map(FeatureDescriptor::getName)
            .filter(propertyName -> wrappedSource.getPropertyValue(propertyName) == null)
            .toArray(String[]::new);

【讨论】:

【参考方案3】:

SpringBeans.xml

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

    <bean id="source" class="com.core.HelloWorld">
        <property name="name" value="Source" />
        <property name="gender" value="Male" />
    </bean>

    <bean id="target" class="com.core.HelloWorld">
        <property name="name" value="Target" />
    </bean>

</beans>

    创建一个java Bean,

    public class HelloWorld 
        private String name;
        private String gender;
    
        public void printHello() 
            System.out.println("Spring 3 : Hello ! " + name + "    -> gender      -> " + gender);
        
    
        //Getters and Setters
    
    

    创建主类进行测试

    public class App 
        public static void main(String[] args) 
            ApplicationContext context = new ClassPathXmlApplicationContext("SpringBeans.xml");
    
            HelloWorld source = (HelloWorld) context.getBean("source");
            HelloWorld target = (HelloWorld) context.getBean("target");
    
            String[] nullPropertyNames = getNullPropertyNames(target);
            BeanUtils.copyProperties(target,source,nullPropertyNames);
            source.printHello();
        
    
        public static String[] getNullPropertyNames(Object source) 
            final BeanWrapper wrappedSource = new BeanWrapperImpl(source);
            return Stream.of(wrappedSource.getPropertyDescriptors())
                .map(FeatureDescriptor::getName)
                .filter(propertyName -> wrappedSource.getPropertyValue(propertyName) == null)
                .toArray(String[]::new);
        
    
    

【讨论】:

【参考方案4】:

我建议你使用 ModelMapper。

这是一个解决你疑惑的示例代码。

      ModelMapper modelMapper = new ModelMapper();
modelMapper.getConfiguration().setSkipNullEnabled(true).setMatchingStrategy(MatchingStrategies.STRICT);

      Company a = new Company();
      a.setId(123l);
      Company b = new Company();
      b.setId(456l);
      b.setName("ABC");

      modelMapper.map(a, b);

      System.out.println("->" + b.getName());

它应该打印 B 值。但是如果你设置了“A”这个名字,结果就是一个“A”值的打印。

秘诀是将 SkipNullEnabled 的值更改为 true。

ModelMapper

ModelMapper MVN

【讨论】:

它不起作用,我试过了。如果发现它会替换值,否则它会创建新值。【参考方案5】:
public static List<String> getNullProperties(Object source) 
    final BeanWrapper wrappedSource = new BeanWrapperImpl(source);
    return Stream.of(wrappedSource.getPropertyDescriptors())
        .map(FeatureDescriptor::getName)
        .filter(propertyName -> Objects.isNull(wrappedSource.getPropertyValue(propertyName)))
        .collect(Collectors.toList());

【讨论】:

最好在您的答案中添加某种形式的解释;这将提高其质量并使其更有可能获得支持:) ...特别是因为它与上面发布的答案(4 年前)有 99% 相似:***.com/a/32066155【参考方案6】:

基于 Pawel Kaczorowski 的回答的更好解决方案:

public static String[] getNullPropertyNames(Object source) 
    final BeanWrapper wrappedSource = new BeanWrapperImpl(source);
    return Stream.of(wrappedSource.getPropertyDescriptors())
        .map(FeatureDescriptor::getName)
        .filter(propertyName -> 
            try 
               return wrappedSource.getPropertyValue(propertyName) == null
             catch (Exception e) 
               return false
                            
        )
        .toArray(String[]::new);

例如,如果我们有一个 DTO:

class FooDTO 
    private String a;
    public String getA()  ... ;
    public String getB();

其他答案将在这种特殊情况下引发异常。

【讨论】:

以上是关于如何使用springframework BeanUtils copyProperties忽略空值?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 org.springframework.jdbc.object 实现可重用的 ddl 语句?

如何解决 org.springframework.web.util.NestedServletException:请求处理失败;使用 SAML

如何在 org.springframework.jdbc.CannotGetJdbcConnectionException 上记录 JDBC 连接信息

干掉 BeanUtils!试试这款 Bean 自动映射工具,真心强大!

map和bean转换

如何在 Grails 3 中捕获“org.springframework.web.multipart.MultipartException”