You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

升级Spring5.3与Hibernate5.6后BeanUtils.copyProperties报错求助

解决Spring 5.x + Hibernate 5.x下BeanUtils.copyProperties()传入null给PersistentSet的异常

核心原因

Spring 5.x的BeanUtils.copyProperties()相比4.x会更严格地拷贝所有匹配属性,包括null值;而Hibernate 5.x的PersistentSet作为懒加载集合实现,不允许通过setter传入null(会破坏Hibernate对集合的状态跟踪与生命周期管理),两者结合就触发了FatalBeanException。

解决方案

1. 自定义拷贝方法,跳过null值的集合属性

重写拷贝逻辑,过滤掉源对象中为null的集合类型属性,避免传递null给目标对象的PersistentSet:

import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import java.beans.PropertyDescriptor;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

public class CustomBeanUtils {
    public static void copyNonNullCollectionProperties(Object source, Object target) {
        BeanUtils.copyProperties(source, target, getNullCollectionPropertyNames(source));
    }

    private static String[] getNullCollectionPropertyNames(Object source) {
        BeanWrapper srcWrapper = new BeanWrapperImpl(source);
        PropertyDescriptor[] descriptors = srcWrapper.getPropertyDescriptors();
        Set<String> nullCollectionProps = new HashSet<>();

        for (PropertyDescriptor pd : descriptors) {
            Object value = srcWrapper.getPropertyValue(pd.getName());
            // 仅跳过null的集合类型属性
            if (value == null && Collection.class.isAssignableFrom(pd.getPropertyType())) {
                nullCollectionProps.add(pd.getName());
            }
        }
        return nullCollectionProps.toArray(new String[0]);
    }
}

之后替换原有的BeanUtils.copyProperties()调用为CustomBeanUtils.copyNonNullCollectionProperties()即可。

2. 修改实体类的集合setter,兼容null值

在实体类的集合setter中,将null转换为空集合,避免直接传递null给PersistentSet:

import java.util.HashSet;
import java.util.Set;
import javax.persistence.OneToMany;

public class User {
    @OneToMany(mappedBy = "user")
    private Set<Order> orders = new HashSet<>(); // 初始化为空集合而非null

    public void setOrders(Set<Order> orders) {
        // 若传入null则转为空HashSet,避免破坏PersistentSet状态
        this.orders = orders == null ? new HashSet<>() : orders;
    }
}

同时建议在字段声明时直接初始化集合为空集合,从根源上避免null集合的出现。

3. 用BeanWrapper手动控制拷贝逻辑

如果需要更细粒度的属性控制,直接使用Spring的BeanWrapper逐个处理属性:

import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import java.beans.PropertyDescriptor;
import java.util.Collection;

public void manualCopyProperties(Object source, Object target) {
    BeanWrapper srcWrapper = new BeanWrapperImpl(source);
    BeanWrapper targetWrapper = new BeanWrapperImpl(target);

    for (PropertyDescriptor pd : srcWrapper.getPropertyDescriptors()) {
        String propName = pd.getName();
        if ("class".equals(propName)) continue; // 跳过class属性

        Object srcValue = srcWrapper.getPropertyValue(propName);
        // 源值为null且是集合类型时,跳过拷贝
        if (srcValue == null && Collection.class.isAssignableFrom(pd.getPropertyType())) {
            continue;
        }
        targetWrapper.setPropertyValue(propName, srcValue);
    }
}

内容的提问来源于stack exchange,提问作者d00d

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 06:50:27