使用BeanUtils或类似方法将非null属性从一个对象复制到另一个对象

kie*_*tos 7 java spring javabeans

我的目标是将一个对象的字段复制到另一个对象中,但只复制那些非空的对象.我不想明确指定它.更通用的解决方案非常有用且易于维护,即在REST API中实现PATCH,您只允许提供特定字段.

我看到了这个类似的线程,我正在尝试从这里实现一些想法:Helper,以便将非null属性从对象复制到另一个?(JAVA)

但是程序执行后对象不会以任何方式改变.

所以这是我创建的示例类,例如:

class Person {
    String name;
    int age;
    Pet friend;

    public Person() {
    }

    public Person(String name, int age, Pet friend) {
        this.name = name;
        this.age = age;
        this.friend = friend;
    }

    // getters and setters here
}

class Pet {
    String name;
    int age;

    public Pet(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // getters and setters here
}
Run Code Online (Sandbox Code Playgroud)

这是我重写的copyProperty方法:

import org.apache.commons.beanutils.BeanUtilsBean;
import java.lang.reflect.InvocationTargetException;

public class MyBeansUtil extends BeanUtilsBean {

@Override
public void copyProperty(Object dest, String name, Object value)
        throws IllegalAccessException, InvocationTargetException {
    if(value == null) return;
    super.copyProperty(dest, name, value);
}
}
Run Code Online (Sandbox Code Playgroud)

......这是我试图在一些例子上测试它的地方:

public class SandBox {
    public static void main(String[] args) {
        Person db = new Person("John", 36, new Pet("Lucy", 3));
        Person db2 = new Person("John", 36, new Pet("Lucy", 2));
        Person db3 = new Person("John", 36, new Pet("Lucy", 4));

        Person in = new Person();
        in.age = 17;
        in.name = "Paul";
        in.friend = new Pet(null, 35);

        Person in2 = new Person();
        in2.name = "Damian";

        Person in3 = new Person();
        in3.friend = new Pet("Lup", 25);

        try {
            BeanUtilsBean notNull  =new MyBeansUtil();
            notNull.copyProperties(db, in);
            notNull.copyProperties(db2, in2);
            notNull.copyProperties(db3, in3);

        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,原始对象db,db1,db2保持原样.我在这里做错了吗?

kie*_*tos 9

我最终使用了Spring BeanUtils库.这是我的工作方法:

import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;

import java.lang.reflect.Field;
import java.util.Collection;

public class MyBeansUtil<T> {
    public T copyNonNullProperties(T target, T in) {
        if (in == null || target == null || target.getClass() != in.getClass()) return null;

        final BeanWrapper src = new BeanWrapperImpl(in);
        final BeanWrapper trg = new BeanWrapperImpl(target);

        for (final Field property : target.getClass().getDeclaredFields()) {
            Object providedObject = src.getPropertyValue(property.getName());
            if (providedObject != null && !(providedObject instanceof Collection<?>)) {
                trg.setPropertyValue(
                        property.getName(),
                        providedObject);
            }
        }
        return target;
    }
}
Run Code Online (Sandbox Code Playgroud)

它工作正常,但请注意它忽略了集合的字段.这是故意的,我分开处理它们.

  • BeanWrapperImpl 被记录为仅供内部使用,因此不应使用它。我认为这里基于 commons-beanutils 提供的答案更好:/sf/ask/91118821/爪哇 (2认同)

Sud*_*kar 6

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

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
public static void myCopyProperties(Object src, Object target) {
    BeanUtils.copyProperties(src, target, getNullPropertyNames(src))
}
Run Code Online (Sandbox Code Playgroud)


Abu*_*iqi 6

使用 BeanUtils 和 java8 我们可以实现这一点:

BeanUtils.copyProperties(Object_source, Object_target, getNullPropertyNames(Object_source));

private 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);
    }
Run Code Online (Sandbox Code Playgroud)