DATAJDBC-273 - Added support for collections and similar in constructors of entities.

Instance creation now uses the same code for materializing property values as the part setting properties.
This commit is contained in:
Jens Schauder
2018-10-15 16:12:21 +02:00
committed by Greg Turnquist
parent 73da720258
commit 9fb5aadd89
2 changed files with 59 additions and 23 deletions

View File

@@ -67,7 +67,16 @@ public class EntityRowMapper<T> implements RowMapper<T> {
@Override
public T mapRow(ResultSet resultSet, int rowNumber) {
T result = createInstance(entity, resultSet, "");
String prefix = "";
RelationalPersistentProperty idProperty = entity.getIdProperty();
Object idValue = null;
if (idProperty != null) {
idValue = readFrom(resultSet, idProperty, prefix);
}
T result = createInstance(entity, resultSet, idValue);
return entity.requiresPropertyPopulation() //
? populateProperties(result, resultSet) //
@@ -88,26 +97,24 @@ public class EntityRowMapper<T> implements RowMapper<T> {
continue;
}
if (property.isCollectionLike() && id != null) {
propertyAccessor.setProperty(property, accessStrategy.findAllByProperty(id, property));
} else if (property.isMap() && id != null) {
Iterable<Object> allByProperty = accessStrategy.findAllByProperty(id, property);
propertyAccessor.setProperty(property, ITERABLE_OF_ENTRY_TO_MAP_CONVERTER.convert(allByProperty));
} else {
final Object value = readFrom(resultSet, property, "");
propertyAccessor.setProperty(property, value);
}
propertyAccessor.setProperty(property, readOrLoadProperty(resultSet, id, property));
}
return propertyAccessor.getBean();
}
@Nullable
private Object readOrLoadProperty(ResultSet resultSet, @Nullable Object id, RelationalPersistentProperty property) {
if (property.isCollectionLike() && id != null) {
return accessStrategy.findAllByProperty(id, property);
} else if (property.isMap() && id != null) {
return ITERABLE_OF_ENTRY_TO_MAP_CONVERTER.convert(accessStrategy.findAllByProperty(id, property));
} else {
return readFrom(resultSet, property, "");
}
}
/**
* Read a single value or a complete Entity from the {@link ResultSet} passed as an argument.
*
@@ -139,14 +146,20 @@ public class EntityRowMapper<T> implements RowMapper<T> {
RelationalPersistentProperty idProperty = entity.getIdProperty();
Object idValue = null;
if (idProperty != null) {
idValue = readFrom(rs, idProperty, prefix);
}
if ((idProperty != null //
? readFrom(rs, idProperty, prefix) //
? idValue //
: getObjectFromResultSet(rs, prefix + property.getReverseColumnName()) //
) == null) {
return null;
}
S instance = createInstance(entity, rs, prefix);
S instance = createInstance(entity, rs, idValue);
PersistentPropertyAccessor<S> accessor = converter.getPropertyAccessor(entity, instance);
@@ -167,7 +180,7 @@ public class EntityRowMapper<T> implements RowMapper<T> {
}
}
private <S> S createInstance(RelationalPersistentEntity<S> entity, ResultSet rs, String prefix) {
private <S> S createInstance(RelationalPersistentEntity<S> entity, ResultSet rs, @Nullable Object idValue) {
return converter.createInstance(entity, parameter -> {
@@ -176,7 +189,8 @@ public class EntityRowMapper<T> implements RowMapper<T> {
Assert.notNull(parameterName, "A constructor parameter name must not be null to be used with Spring Data JDBC");
RelationalPersistentProperty property = entity.getRequiredPersistentProperty(parameterName);
return readFrom(rs, property, prefix);
return readOrLoadProperty(rs, idValue, property);
});
}
}

View File

@@ -21,6 +21,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;
import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.experimental.Wither;
@@ -202,6 +203,18 @@ public class EntityRowMapperUnitTests {
.isEqualTo(new String[] { "111", "222", "333" });
}
@Test // DATAJDBC-273
public void handlesNonSimplePropertyInConstructor() throws SQLException {
ResultSet rs = mockResultSet(asList("id"), //
ID_FOR_ENTITY_REFERENCING_LIST);
rs.next();
EntityWithListInConstructor extracted = createRowMapper(EntityWithListInConstructor.class).mapRow(rs, 1);
assertThat(extracted.content).hasSize(2);
}
private <T> EntityRowMapper<T> createRowMapper(Class<T> type) {
return createRowMapper(type, NamingStrategy.INSTANCE);
}
@@ -319,12 +332,13 @@ public class EntityRowMapperUnitTests {
return index < 0 && !values.isEmpty();
}
private Object getObject(String column) {
private Object getObject(String column) throws SQLException {
Map<String, Object> rowMap = values.get(index);
Assert.isTrue(rowMap.containsKey(column),
String.format("Trying to access a column (%s) that does not exist", column));
if (!rowMap.containsKey(column)) {
throw new SQLException(String.format("Trying to access a column (%s) that does not exist", column));
}
return rowMap.get(column);
}
@@ -409,4 +423,12 @@ public class EntityRowMapperUnitTests {
return new MixedProperties(one, two, three);
}
}
@AllArgsConstructor
static class EntityWithListInConstructor {
@Id final Long id;
final List<Trivial> content;
}
}