On a daily basis, data is imported via a webservice.
I know I could go through all the fields on the pojos, and check for null values, and update where appropriate, but I would prefer to have hibernate do this if it able to, as it would decrease the chances of me adding a field and forgetting to add it to this manual merge process.
Can hibernate perform step #4 above?
No, Hibernate (or JPA) does not provide that functionality out of the box, but it's not hard to achieve using the JavaBeans mechanism (or one of many libraries that provide an abstraction layer over that).
Here's a method that copies all properties from beanA
to beanB
if they are null in beanB
, using the standard JavaBeans Introspector
mechanism:
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);
}
}
Of course this is just a quick-and-dirty implementation, but it should get you started.
Here is a slightly more elegant version using Commons / BeanUtils. It hides the reflection from you and provides map-based property access:
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);
}
}
And here's another version using Spring's BeanWrapper
interface (it's the least verbose, because Spring provides an abstraction above all the reflection and does it's own Exception handling), but unfortunately the BeanWrapper
technology is only available with the Spring IOC container (which is of course only unfortunate if you don't use the container):
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);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With