使用Hibernate更新具有瞬态对象的持久对象

每天,数据通过Web服务导入。 我创建了一个pojo的新(瞬态)实例,我通过JPA注释在hibernate中映射。 我将Web服务中的数据填充到瞬态实例中 我从我想要使用瞬态实例中的数据更新的数据库中加载持久对象。 我以某种方式将这个瞬态实例与持久实例合并。如果持久对象在其中一个字段上具有非null值,则它不会被瞬态对象上的潜在空值覆盖。基本上我不想丢失任何数据,只是更新事情发生了变化。 我知道我可以浏览pojos上的所有字段,并检查空值,并在适当的地方更新,但我更愿意让hibernate执行此操作,如果它能够,因为它会减少我添加字段和忘记将其添加到此手动合并过程中。 休眠可以执行上面的第4步吗?     
已邀请:
不,Hibernate(或JPA)不提供开箱即用的功能,但使用JavaBeans机制(或许多提供抽象层的库之一)并不难实现。 使用Plain Javabeans Introspection 这里有一个方法,如果它们在
beanB
中为空,则使用标准的JavaBeans
Introspector
机制将所有属性从
beanA
复制到
beanB
public static void copyBeanProperties(
    final Object beanA, final Object beanB){

    if(beanA.getClass() != beanB.getClass()){
        // actually, this may be a problem, because beanB may be a
        // cglib-created subclass
        throw new IllegalArgumentException();
    }
    try{
        for( final PropertyDescriptor pd :
            Introspector
              .getBeanInfo(beanB.getClass(), Object.class)
              .getPropertyDescriptors()){
            if(pd.getReadMethod().invoke(beanB)==null){
                pd.getWriteMethod().invoke(beanB,
                    pd.getReadMethod().invoke(beanA)
                );
            }
        }
    } catch(IntrospectionException e){
        throw new IllegalStateException(e);
    } catch(IllegalAccessException e){
        throw new IllegalStateException(e);
    } catch(InvocationTargetException e){
        throw new IllegalStateException(e);
    }
}
当然这只是一个快速而肮脏的实现,但它应该让你开始。 使用Apache Commons / BeanUtils 这是一个使用Commons / BeanUtils的稍微优雅的版本。它会隐藏您的反射并提供基于地图的属性访问:
public static void copyBeanProperties(final Object beanA, final Object beanB){
    try{
        @SuppressWarnings("unchecked") // this should be safe
        final Map<String, Object> beanAProps = PropertyUtils.describe(beanA);
        @SuppressWarnings("unchecked") // this should be safe
        final Map<String, Object> beanBProps = PropertyUtils.describe(beanB);

        if(!beanAProps.keySet().containsAll(beanBProps.keySet())){
            throw new IllegalArgumentException("Incompatible types: "
                + beanA + ", " + beanB);
        }
        for(final Entry<String, Object> entryA : beanAProps.entrySet()){
            if(beanBProps.get(entryA.getKey()) == null){
                PropertyUtils.setMappedProperty(beanB, entryA.getKey(),
                    entryA.getValue());
            }
        }
    } catch(final IllegalAccessException e){
        throw new IllegalStateException(e);
    } catch(final InvocationTargetException e){
        throw new IllegalStateException(e);
    } catch(final NoSuchMethodException e){
        throw new IllegalStateException(e);
    }
}
使用Spring的BeanWrapper 这是使用Spring的
BeanWrapper
接口的另一个版本(它是最简洁的,因为Spring提供了一个抽象,超过了所有的反射,并且它自己进行了异常处理),但不幸的是,
BeanWrapper
技术仅适用于Spring IOC容器(当然只有不幸的是,如果你不使用容器):
public static void copyBeanProperties(final Object beanA, final Object beanB){

    final BeanWrapper wrapperA = new BeanWrapperImpl(beanA);
    final BeanWrapper wrapperB = new BeanWrapperImpl(beanB);

    try{
        for(final PropertyDescriptor descriptor : wrapperB
            .getPropertyDescriptors()){

            final String propertyName = descriptor.getName();
            if(wrapperB.getPropertyValue(propertyName) == null){
                wrapperB.setPropertyValue(propertyName,
                    wrapperA.getPropertyValue(propertyName));
            }
        }
    } catch(final /* unchecked */ InvalidPropertyException e){
        throw new IllegalArgumentException("Incompatible types: " + beanA
            + ", " + beanB, e);
    }
}
    
Hibernate不会为你做这件事。您可以更改
setter
s以获得此功能。     

要回复问题请先登录注册