Skip to content

Commit 2a55aeb

Browse files
schauderodrotbohm
authored andcommitted
DATAJDBC-252 - Optimize population of (immutable) entities to be created.
The entity creation not skips the property population entirely if the metamodel indicates that the instantiation already creates a complete entity. If there's need to actively populate properties, we now correctly skip the ones already consumed by the constructor. Original pull request: #86.
1 parent 63b9754 commit 2a55aeb

File tree

3 files changed

+101
-4
lines changed

3 files changed

+101
-4
lines changed

src/main/java/org/springframework/data/jdbc/core/EntityRowMapper.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.springframework.data.mapping.MappingException;
2424
import org.springframework.data.mapping.PersistentProperty;
2525
import org.springframework.data.mapping.PersistentPropertyAccessor;
26+
import org.springframework.data.mapping.PreferredConstructor;
2627
import org.springframework.data.relational.core.conversion.RelationalConverter;
2728
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
2829
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
@@ -69,12 +70,27 @@ public T mapRow(ResultSet resultSet, int rowNumber) {
6970

7071
T result = createInstance(entity, resultSet, "");
7172

73+
if (entity.requiresPropertyPopulation()) {
74+
return populateProperties(result, resultSet);
75+
}
76+
77+
return result;
78+
}
79+
80+
private T populateProperties(T result, ResultSet resultSet) {
81+
7282
PersistentPropertyAccessor<T> propertyAccessor = converter.getPropertyAccessor(entity, result);
7383

7484
Object id = idProperty == null ? null : readFrom(resultSet, idProperty, "");
7585

86+
PreferredConstructor<T, RelationalPersistentProperty> persistenceConstructor = entity.getPersistenceConstructor();
87+
7688
for (RelationalPersistentProperty property : entity) {
7789

90+
if (persistenceConstructor != null && persistenceConstructor.isConstructorParameter(property)) {
91+
continue;
92+
}
93+
7894
if (property.isCollectionLike() && id != null) {
7995
propertyAccessor.setProperty(property, accessStrategy.findAllByProperty(id, property));
8096
} else if (property.isMap() && id != null) {
@@ -86,7 +102,7 @@ public T mapRow(ResultSet resultSet, int rowNumber) {
86102
}
87103
}
88104

89-
return result;
105+
return propertyAccessor.getBean();
90106
}
91107

92108
/**

src/test/java/org/springframework/data/jdbc/core/EntityRowMapperUnitTests.java

Lines changed: 66 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import static org.mockito.Mockito.*;
2323

2424
import lombok.RequiredArgsConstructor;
25+
import lombok.experimental.Wither;
2526

2627
import java.sql.ResultSet;
2728
import java.sql.SQLException;
@@ -35,18 +36,19 @@
3536

3637
import javax.naming.OperationNotSupportedException;
3738

38-
import lombok.experimental.Wither;
3939
import org.junit.Test;
4040
import org.mockito.invocation.InvocationOnMock;
4141
import org.mockito.stubbing.Answer;
4242
import org.springframework.data.annotation.Id;
43+
import org.springframework.data.annotation.PersistenceConstructor;
4344
import org.springframework.data.jdbc.core.convert.JdbcCustomConversions;
4445
import org.springframework.data.relational.core.conversion.BasicRelationalConverter;
4546
import org.springframework.data.relational.core.conversion.RelationalConverter;
4647
import org.springframework.data.relational.core.mapping.NamingStrategy;
4748
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
4849
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
4950
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
51+
import org.springframework.data.repository.query.Param;
5052
import org.springframework.util.Assert;
5153

5254
/**
@@ -172,6 +174,33 @@ public void listReferenceGetsLoadedWithAdditionalSelect() throws SQLException {
172174
.containsExactly(ID_FOR_ENTITY_REFERENCING_LIST, "alpha", 2);
173175
}
174176

177+
@Test // DATAJDBC-252
178+
public void doesNotTryToSetPropertiesThatAreSetViaConstructor() throws SQLException {
179+
180+
ResultSet rs = mockResultSet(asList("value"), //
181+
"value-from-resultSet");
182+
rs.next();
183+
184+
DontUseSetter extracted = createRowMapper(DontUseSetter.class).mapRow(rs, 1);
185+
186+
assertThat(extracted.value) //
187+
.isEqualTo("setThroughConstructor:value-from-resultSet");
188+
}
189+
190+
@Test // DATAJDBC-252
191+
public void handlesMixedProperties() throws SQLException {
192+
193+
ResultSet rs = mockResultSet(asList("one", "two", "three"), //
194+
"111", "222", "333");
195+
rs.next();
196+
197+
MixedProperties extracted = createRowMapper(MixedProperties.class).mapRow(rs, 1);
198+
199+
assertThat(extracted) //
200+
.extracting(e -> e.one, e -> e.two, e -> e.three) //
201+
.isEqualTo(new String[] { "111", "222", "333" });
202+
}
203+
175204
private <T> EntityRowMapper<T> createRowMapper(Class<T> type) {
176205
return createRowMapper(type, NamingStrategy.INSTANCE);
177206
}
@@ -189,12 +218,14 @@ private <T> EntityRowMapper<T> createRowMapper(Class<T> type, NamingStrategy nam
189218
doReturn(new HashSet<>(asList( //
190219
new SimpleEntry<>("one", new Trivial()), //
191220
new SimpleEntry<>("two", new Trivial()) //
192-
))).when(accessStrategy).findAllByProperty(eq(ID_FOR_ENTITY_REFERENCING_MAP), any(RelationalPersistentProperty.class));
221+
))).when(accessStrategy).findAllByProperty(eq(ID_FOR_ENTITY_REFERENCING_MAP),
222+
any(RelationalPersistentProperty.class));
193223

194224
doReturn(new HashSet<>(asList( //
195225
new SimpleEntry<>(1, new Trivial()), //
196226
new SimpleEntry<>(2, new Trivial()) //
197-
))).when(accessStrategy).findAllByProperty(eq(ID_FOR_ENTITY_REFERENCING_LIST), any(RelationalPersistentProperty.class));
227+
))).when(accessStrategy).findAllByProperty(eq(ID_FOR_ENTITY_REFERENCING_LIST),
228+
any(RelationalPersistentProperty.class));
198229

199230
RelationalConverter converter = new BasicRelationalConverter(context, new JdbcCustomConversions());
200231

@@ -345,4 +376,36 @@ static class OneToList {
345376
String name;
346377
List<Trivial> children;
347378
}
379+
380+
private static class DontUseSetter {
381+
String value;
382+
383+
DontUseSetter(@Param("value") String value) {
384+
this.value = "setThroughConstructor:" + value;
385+
}
386+
}
387+
388+
static class MixedProperties {
389+
390+
final String one;
391+
String two;
392+
final String three;
393+
394+
@PersistenceConstructor
395+
MixedProperties(String one) {
396+
this.one = one;
397+
this.three = "unset";
398+
}
399+
400+
private MixedProperties(String one, String two, String three) {
401+
402+
this.one = one;
403+
this.two = two;
404+
this.three = three;
405+
}
406+
407+
MixedProperties withThree(String three) {
408+
return new MixedProperties(one, two, three);
409+
}
410+
}
348411
}

src/test/java/org/springframework/data/jdbc/repository/query/QueryAnnotationHsqlIntegrationTests.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import java.util.Optional;
2525
import java.util.stream.Stream;
2626

27+
import lombok.Value;
2728
import org.junit.ClassRule;
2829
import org.junit.Rule;
2930
import org.junit.Test;
@@ -251,6 +252,12 @@ public void executeCustomModifyingQueryWithReturnTypeVoid() {
251252
assertThat(repository.findByNameAsEntity("Spring Data JDBC")).isNotNull();
252253
}
253254

255+
@Test // DATAJDBC-175
256+
public void executeCustomQueryWithImmutableResultType() {
257+
258+
assertThat(repository.immutableTuple()).isEqualTo(new DummyEntityRepository.ImmutableTuple("one", "two", 3));
259+
}
260+
254261
private DummyEntity dummyEntity(String name) {
255262

256263
DummyEntity entity = new DummyEntity();
@@ -329,5 +336,16 @@ private interface DummyEntityRepository extends CrudRepository<DummyEntity, Long
329336
@Query("INSERT INTO DUMMY_ENTITY (name) VALUES(:name)")
330337
void insert(@Param("name") String name);
331338

339+
// DATAJDBC-252
340+
@Query("SELECT 'one' one, 'two' two, 3 three FROM (VALUES (0))")
341+
ImmutableTuple immutableTuple();
342+
343+
344+
@Value
345+
class ImmutableTuple {
346+
String one;
347+
String two;
348+
int three;
349+
}
332350
}
333351
}

0 commit comments

Comments
 (0)