DATAREST-957 - Improved PUT handling for transient properties not backed by a field.

When copying the transient properties of an aggregate, we now try field based access first and fall back to accessor based copying in case both a setter and getter are exposed on the type.

Previously we always expected a field to be present which doesn't necessarily has to be the case.

Related ticket: DATAREST-986.
This commit is contained in:
Oliver Gierke
2017-01-24 13:21:47 +01:00
parent 08f012d4aa
commit fe1c4b1e1c
2 changed files with 36 additions and 3 deletions

View File

@@ -204,11 +204,23 @@ public class DomainObjectReader {
*/
private static void copyRemainingProperties(MappedProperties properties, Object source, Object target) {
PropertyAccessor sourceAccessor = PropertyAccessorFactory.forDirectFieldAccess(source);
PropertyAccessor targetAccessor = PropertyAccessorFactory.forDirectFieldAccess(target);
PropertyAccessor sourceFieldAccessor = PropertyAccessorFactory.forDirectFieldAccess(source);
PropertyAccessor sourcePropertyAccessor = PropertyAccessorFactory.forBeanPropertyAccess(source);
PropertyAccessor targetFieldAccessor = PropertyAccessorFactory.forDirectFieldAccess(target);
PropertyAccessor targetPropertyAccessor = PropertyAccessorFactory.forBeanPropertyAccess(target);
for (String property : properties.getSpringDataUnmappedProperties()) {
targetAccessor.setPropertyValue(property, sourceAccessor.getPropertyValue(property));
// If there's a field we can just copy it.
if (targetFieldAccessor.isWritableProperty(property)) {
targetFieldAccessor.setPropertyValue(property, sourceFieldAccessor.getPropertyValue(property));
continue;
}
// Otherwise only copy if there's both a getter and setter.
if (targetPropertyAccessor.isWritableProperty(property) && sourcePropertyAccessor.isReadableProperty(property)) {
targetPropertyAccessor.setPropertyValue(property, sourcePropertyAccessor.getPropertyValue(property));
}
}
}

View File

@@ -92,6 +92,7 @@ public class DomainObjectReaderUnitTests {
mappingContext.getPersistentEntity(Outer.class);
mappingContext.getPersistentEntity(Parent.class);
mappingContext.getPersistentEntity(Product.class);
mappingContext.getPersistentEntity(TransientReadOnlyProperty.class);
mappingContext.afterPropertiesSet();
PersistentEntities entities = new PersistentEntities(Collections.singleton(mappingContext));
@@ -441,6 +442,15 @@ public class DomainObjectReaderUnitTests {
assertThat(result.map.get(Locale.GERMAN), is(new LocalizedValue("schlussendlich")));
}
@Test // DATAREST-987
public void handlesTransientPropertyWithoutFieldProperly() throws Exception {
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.readTree("{ \"name\" : \"Foo\" }");
reader.readPut((ObjectNode) node, new TransientReadOnlyProperty(), mapper);
}
@SuppressWarnings("unchecked")
private static <T> T as(Object source, Class<T> type) {
@@ -564,4 +574,15 @@ public class DomainObjectReaderUnitTests {
static class LocalizedValue {
String value;
}
@JsonAutoDetect(getterVisibility = Visibility.ANY)
static class TransientReadOnlyProperty {
@Transient
public String getName() {
return null;
}
public void setName(String name) {}
}
}