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:
committed by
Greg Turnquist
parent
73da720258
commit
9fb5aadd89
@@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user