diff --git a/pom.xml b/pom.xml index 9b63bcc527..50c6c83b4f 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-jdbc - 1.0.0.BUILD-SNAPSHOT + 1.0.0.DATAJDBC-252-SNAPSHOT Spring Data JDBC Spring Data module for JDBC repositories. diff --git a/src/main/java/org/springframework/data/jdbc/core/EntityRowMapper.java b/src/main/java/org/springframework/data/jdbc/core/EntityRowMapper.java index 1da0c3ba74..9ff594c416 100644 --- a/src/main/java/org/springframework/data/jdbc/core/EntityRowMapper.java +++ b/src/main/java/org/springframework/data/jdbc/core/EntityRowMapper.java @@ -23,6 +23,7 @@ import org.springframework.data.mapping.MappingException; import org.springframework.data.mapping.PersistentProperty; import org.springframework.data.mapping.PersistentPropertyAccessor; +import org.springframework.data.mapping.PreferredConstructor; import org.springframework.data.relational.core.conversion.RelationalConverter; import org.springframework.data.relational.core.mapping.RelationalMappingContext; import org.springframework.data.relational.core.mapping.RelationalPersistentEntity; @@ -69,12 +70,27 @@ public T mapRow(ResultSet resultSet, int rowNumber) { T result = createInstance(entity, resultSet, ""); + if (entity.requiresPropertyPopulation()) { + return populateProperties(result, resultSet); + } + + return result; + } + + private T populateProperties(T result, ResultSet resultSet) { + PersistentPropertyAccessor propertyAccessor = converter.getPropertyAccessor(entity, result); Object id = idProperty == null ? null : readFrom(resultSet, idProperty, ""); + PreferredConstructor persistenceConstructor = entity.getPersistenceConstructor(); + for (RelationalPersistentProperty property : entity) { + if (persistenceConstructor != null && persistenceConstructor.isConstructorParameter(property)) { + continue; + } + if (property.isCollectionLike() && id != null) { propertyAccessor.setProperty(property, accessStrategy.findAllByProperty(id, property)); } else if (property.isMap() && id != null) { @@ -86,7 +102,7 @@ public T mapRow(ResultSet resultSet, int rowNumber) { } } - return result; + return propertyAccessor.getBean(); } /** diff --git a/src/test/java/org/springframework/data/jdbc/core/EntityRowMapperUnitTests.java b/src/test/java/org/springframework/data/jdbc/core/EntityRowMapperUnitTests.java index 47d5bbb4aa..b1bfb3efd2 100644 --- a/src/test/java/org/springframework/data/jdbc/core/EntityRowMapperUnitTests.java +++ b/src/test/java/org/springframework/data/jdbc/core/EntityRowMapperUnitTests.java @@ -22,6 +22,7 @@ import static org.mockito.Mockito.*; import lombok.RequiredArgsConstructor; +import lombok.experimental.Wither; import java.sql.ResultSet; import java.sql.SQLException; @@ -35,11 +36,11 @@ import javax.naming.OperationNotSupportedException; -import lombok.experimental.Wither; import org.junit.Test; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.springframework.data.annotation.Id; +import org.springframework.data.annotation.PersistenceConstructor; import org.springframework.data.jdbc.core.convert.JdbcCustomConversions; import org.springframework.data.relational.core.conversion.BasicRelationalConverter; import org.springframework.data.relational.core.conversion.RelationalConverter; @@ -47,6 +48,7 @@ import org.springframework.data.relational.core.mapping.RelationalMappingContext; import org.springframework.data.relational.core.mapping.RelationalPersistentEntity; import org.springframework.data.relational.core.mapping.RelationalPersistentProperty; +import org.springframework.data.repository.query.Param; import org.springframework.util.Assert; /** @@ -172,6 +174,33 @@ public void listReferenceGetsLoadedWithAdditionalSelect() throws SQLException { .containsExactly(ID_FOR_ENTITY_REFERENCING_LIST, "alpha", 2); } + @Test // DATAJDBC-252 + public void doesNotTryToSetPropertiesThatAreSetViaConstructor() throws SQLException { + + ResultSet rs = mockResultSet(asList("value"), // + "value-from-resultSet"); + rs.next(); + + DontUseSetter extracted = createRowMapper(DontUseSetter.class).mapRow(rs, 1); + + assertThat(extracted.value) // + .isEqualTo("setThroughConstructor:value-from-resultSet"); + } + + @Test // DATAJDBC-252 + public void handlesMixedProperties() throws SQLException { + + ResultSet rs = mockResultSet(asList("one", "two", "three"), // + "111", "222", "333"); + rs.next(); + + MixedProperties extracted = createRowMapper(MixedProperties.class).mapRow(rs, 1); + + assertThat(extracted) // + .extracting(e -> e.one, e -> e.two, e -> e.three) // + .isEqualTo(new String[] { "111", "222", "333" }); + } + private EntityRowMapper createRowMapper(Class type) { return createRowMapper(type, NamingStrategy.INSTANCE); } @@ -189,12 +218,14 @@ private EntityRowMapper createRowMapper(Class type, NamingStrategy nam doReturn(new HashSet<>(asList( // new SimpleEntry<>("one", new Trivial()), // new SimpleEntry<>("two", new Trivial()) // - ))).when(accessStrategy).findAllByProperty(eq(ID_FOR_ENTITY_REFERENCING_MAP), any(RelationalPersistentProperty.class)); + ))).when(accessStrategy).findAllByProperty(eq(ID_FOR_ENTITY_REFERENCING_MAP), + any(RelationalPersistentProperty.class)); doReturn(new HashSet<>(asList( // new SimpleEntry<>(1, new Trivial()), // new SimpleEntry<>(2, new Trivial()) // - ))).when(accessStrategy).findAllByProperty(eq(ID_FOR_ENTITY_REFERENCING_LIST), any(RelationalPersistentProperty.class)); + ))).when(accessStrategy).findAllByProperty(eq(ID_FOR_ENTITY_REFERENCING_LIST), + any(RelationalPersistentProperty.class)); RelationalConverter converter = new BasicRelationalConverter(context, new JdbcCustomConversions()); @@ -345,4 +376,36 @@ static class OneToList { String name; List children; } + + private static class DontUseSetter { + String value; + + DontUseSetter(@Param("value") String value) { + this.value = "setThroughConstructor:" + value; + } + } + + static class MixedProperties { + + final String one; + String two; + final String three; + + @PersistenceConstructor + MixedProperties(String one) { + this.one = one; + this.three = "unset"; + } + + private MixedProperties(String one, String two, String three) { + + this.one = one; + this.two = two; + this.three = three; + } + + MixedProperties withThree(String three) { + return new MixedProperties(one, two, three); + } + } } diff --git a/src/test/java/org/springframework/data/jdbc/repository/query/QueryAnnotationHsqlIntegrationTests.java b/src/test/java/org/springframework/data/jdbc/repository/query/QueryAnnotationHsqlIntegrationTests.java index 2c5df7db48..bec15dd5bf 100644 --- a/src/test/java/org/springframework/data/jdbc/repository/query/QueryAnnotationHsqlIntegrationTests.java +++ b/src/test/java/org/springframework/data/jdbc/repository/query/QueryAnnotationHsqlIntegrationTests.java @@ -24,6 +24,7 @@ import java.util.Optional; import java.util.stream.Stream; +import lombok.Value; import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; @@ -251,6 +252,12 @@ public void executeCustomModifyingQueryWithReturnTypeVoid() { assertThat(repository.findByNameAsEntity("Spring Data JDBC")).isNotNull(); } + @Test // DATAJDBC-175 + public void executeCustomQueryWithImmutableResultType() { + + assertThat(repository.immutableTuple()).isEqualTo(new DummyEntityRepository.ImmutableTuple("one", "two", 3)); + } + private DummyEntity dummyEntity(String name) { DummyEntity entity = new DummyEntity(); @@ -329,5 +336,16 @@ private interface DummyEntityRepository extends CrudRepository