Skip to content

Commit 9fb5aad

Browse files
schaudergregturn
authored andcommitted
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.
1 parent 73da720 commit 9fb5aad

File tree

2 files changed

+59
-23
lines changed

2 files changed

+59
-23
lines changed

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

+34-20
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,16 @@ public EntityRowMapper(RelationalPersistentEntity<T> entity, RelationalMappingCo
6767
@Override
6868
public T mapRow(ResultSet resultSet, int rowNumber) {
6969

70-
T result = createInstance(entity, resultSet, "");
70+
String prefix = "";
71+
72+
RelationalPersistentProperty idProperty = entity.getIdProperty();
73+
74+
Object idValue = null;
75+
if (idProperty != null) {
76+
idValue = readFrom(resultSet, idProperty, prefix);
77+
}
78+
79+
T result = createInstance(entity, resultSet, idValue);
7180

7281
return entity.requiresPropertyPopulation() //
7382
? populateProperties(result, resultSet) //
@@ -88,26 +97,24 @@ private T populateProperties(T result, ResultSet resultSet) {
8897
continue;
8998
}
9099

91-
if (property.isCollectionLike() && id != null) {
92-
93-
propertyAccessor.setProperty(property, accessStrategy.findAllByProperty(id, property));
94-
95-
} else if (property.isMap() && id != null) {
96-
97-
Iterable<Object> allByProperty = accessStrategy.findAllByProperty(id, property);
98-
propertyAccessor.setProperty(property, ITERABLE_OF_ENTRY_TO_MAP_CONVERTER.convert(allByProperty));
99-
100-
} else {
101-
102-
final Object value = readFrom(resultSet, property, "");
103-
104-
propertyAccessor.setProperty(property, value);
105-
}
100+
propertyAccessor.setProperty(property, readOrLoadProperty(resultSet, id, property));
106101
}
107102

108103
return propertyAccessor.getBean();
109104
}
110105

106+
@Nullable
107+
private Object readOrLoadProperty(ResultSet resultSet, @Nullable Object id, RelationalPersistentProperty property) {
108+
109+
if (property.isCollectionLike() && id != null) {
110+
return accessStrategy.findAllByProperty(id, property);
111+
} else if (property.isMap() && id != null) {
112+
return ITERABLE_OF_ENTRY_TO_MAP_CONVERTER.convert(accessStrategy.findAllByProperty(id, property));
113+
} else {
114+
return readFrom(resultSet, property, "");
115+
}
116+
}
117+
111118
/**
112119
* Read a single value or a complete Entity from the {@link ResultSet} passed as an argument.
113120
*
@@ -139,14 +146,20 @@ private <S> S readEntityFrom(ResultSet rs, RelationalPersistentProperty property
139146

140147
RelationalPersistentProperty idProperty = entity.getIdProperty();
141148

149+
Object idValue = null;
150+
151+
if (idProperty != null) {
152+
idValue = readFrom(rs, idProperty, prefix);
153+
}
154+
142155
if ((idProperty != null //
143-
? readFrom(rs, idProperty, prefix) //
156+
? idValue //
144157
: getObjectFromResultSet(rs, prefix + property.getReverseColumnName()) //
145158
) == null) {
146159
return null;
147160
}
148161

149-
S instance = createInstance(entity, rs, prefix);
162+
S instance = createInstance(entity, rs, idValue);
150163

151164
PersistentPropertyAccessor<S> accessor = converter.getPropertyAccessor(entity, instance);
152165

@@ -167,7 +180,7 @@ private Object getObjectFromResultSet(ResultSet rs, String backreferenceName) {
167180
}
168181
}
169182

170-
private <S> S createInstance(RelationalPersistentEntity<S> entity, ResultSet rs, String prefix) {
183+
private <S> S createInstance(RelationalPersistentEntity<S> entity, ResultSet rs, @Nullable Object idValue) {
171184

172185
return converter.createInstance(entity, parameter -> {
173186

@@ -176,7 +189,8 @@ private <S> S createInstance(RelationalPersistentEntity<S> entity, ResultSet rs,
176189
Assert.notNull(parameterName, "A constructor parameter name must not be null to be used with Spring Data JDBC");
177190

178191
RelationalPersistentProperty property = entity.getRequiredPersistentProperty(parameterName);
179-
return readFrom(rs, property, prefix);
192+
193+
return readOrLoadProperty(rs, idValue, property);
180194
});
181195
}
182196
}

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

+25-3
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import static org.mockito.ArgumentMatchers.eq;
2222
import static org.mockito.Mockito.*;
2323

24+
import lombok.AllArgsConstructor;
2425
import lombok.RequiredArgsConstructor;
2526
import lombok.experimental.Wither;
2627

@@ -202,6 +203,18 @@ public void handlesMixedProperties() throws SQLException {
202203
.isEqualTo(new String[] { "111", "222", "333" });
203204
}
204205

206+
@Test // DATAJDBC-273
207+
public void handlesNonSimplePropertyInConstructor() throws SQLException {
208+
209+
ResultSet rs = mockResultSet(asList("id"), //
210+
ID_FOR_ENTITY_REFERENCING_LIST);
211+
rs.next();
212+
213+
EntityWithListInConstructor extracted = createRowMapper(EntityWithListInConstructor.class).mapRow(rs, 1);
214+
215+
assertThat(extracted.content).hasSize(2);
216+
}
217+
205218
private <T> EntityRowMapper<T> createRowMapper(Class<T> type) {
206219
return createRowMapper(type, NamingStrategy.INSTANCE);
207220
}
@@ -319,12 +332,13 @@ private boolean isBeforeFirst() {
319332
return index < 0 && !values.isEmpty();
320333
}
321334

322-
private Object getObject(String column) {
335+
private Object getObject(String column) throws SQLException {
323336

324337
Map<String, Object> rowMap = values.get(index);
325338

326-
Assert.isTrue(rowMap.containsKey(column),
327-
String.format("Trying to access a column (%s) that does not exist", column));
339+
if (!rowMap.containsKey(column)) {
340+
throw new SQLException(String.format("Trying to access a column (%s) that does not exist", column));
341+
}
328342

329343
return rowMap.get(column);
330344
}
@@ -409,4 +423,12 @@ MixedProperties withThree(String three) {
409423
return new MixedProperties(one, two, three);
410424
}
411425
}
426+
427+
@AllArgsConstructor
428+
static class EntityWithListInConstructor {
429+
430+
@Id final Long id;
431+
432+
final List<Trivial> content;
433+
}
412434
}

0 commit comments

Comments
 (0)