Skip to content

Commit aec5722

Browse files
committed
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 baeb8d8 commit aec5722

File tree

2 files changed

+60
-24
lines changed

2 files changed

+60
-24
lines changed

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
}

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

+26-4
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

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

205+
@Test // DATAJDBC-273
206+
public void handlesNonSimplePropertyInConstructor() throws SQLException {
207+
208+
ResultSet rs = mockResultSet(asList("id"), //
209+
ID_FOR_ENTITY_REFERENCING_LIST);
210+
rs.next();
211+
212+
EntityWithListInConstructor extracted = createRowMapper(EntityWithListInConstructor.class).mapRow(rs, 1);
213+
214+
assertThat(extracted.content).hasSize(2);
215+
}
216+
204217
private <T> EntityRowMapper<T> createRowMapper(Class<T> type) {
205218
return createRowMapper(type, NamingStrategy.INSTANCE);
206219
}
@@ -246,7 +259,7 @@ private static ResultSet mockResultSet(List<String> columns, Object... values) {
246259
"Number of values [%d] must be a multiple of the number of columns [%d]", //
247260
values.length, //
248261
columns.size() //
249-
) //
262+
) //
250263
);
251264

252265
List<Map<String, Object>> result = convertValues(columns, values);
@@ -318,12 +331,13 @@ private boolean isBeforeFirst() {
318331
return index < 0 && !values.isEmpty();
319332
}
320333

321-
private Object getObject(String column) {
334+
private Object getObject(String column) throws SQLException {
322335

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

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

328342
return rowMap.get(column);
329343
}
@@ -408,4 +422,12 @@ MixedProperties withThree(String three) {
408422
return new MixedProperties(one, two, three);
409423
}
410424
}
425+
426+
@AllArgsConstructor
427+
static class EntityWithListInConstructor {
428+
429+
@Id final Long id;
430+
431+
final List<Trivial> content;
432+
}
411433
}

0 commit comments

Comments
 (0)