升级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
相关产品推荐
相关产品推荐

