From 9fb5aadd896691b945964c98a5fa28e317d92a34 Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Mon, 15 Oct 2018 16:12:21 +0200 Subject: [PATCH] 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. --- .../data/jdbc/core/EntityRowMapper.java | 54 ++++++++++++------- .../jdbc/core/EntityRowMapperUnitTests.java | 28 ++++++++-- 2 files changed, 59 insertions(+), 23 deletions(-) diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/EntityRowMapper.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/EntityRowMapper.java index f6fefd54..2ea9327d 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/EntityRowMapper.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/EntityRowMapper.java @@ -67,7 +67,16 @@ public class EntityRowMapper implements RowMapper { @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 implements RowMapper { continue; } - if (property.isCollectionLike() && id != null) { - - propertyAccessor.setProperty(property, accessStrategy.findAllByProperty(id, property)); - - } else if (property.isMap() && id != null) { - - Iterable 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 implements RowMapper { 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 accessor = converter.getPropertyAccessor(entity, instance); @@ -167,7 +180,7 @@ public class EntityRowMapper implements RowMapper { } } - private S createInstance(RelationalPersistentEntity entity, ResultSet rs, String prefix) { + private S createInstance(RelationalPersistentEntity entity, ResultSet rs, @Nullable Object idValue) { return converter.createInstance(entity, parameter -> { @@ -176,7 +189,8 @@ public class EntityRowMapper implements RowMapper { 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); }); } } diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/EntityRowMapperUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/EntityRowMapperUnitTests.java index e728f4bd..4aee627a 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/EntityRowMapperUnitTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/EntityRowMapperUnitTests.java @@ -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 EntityRowMapper createRowMapper(Class 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 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 content; + } }