Skip to content

Commit 5854922

Browse files
schauderchristophstrobl
authored andcommitted
DATAJDBC-370 - Fixed handling of entities with no withers.
We tried to set all the properties, even when they were already set via constructor. Fixed it by unifying the three instances where we created and populated instances. Original Pull Request: #151
1 parent 060e404 commit 5854922

File tree

2 files changed

+140
-163
lines changed

2 files changed

+140
-163
lines changed

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

Lines changed: 14 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
import org.springframework.data.jdbc.core.mapping.AggregateReference;
3131
import org.springframework.data.jdbc.support.JdbcUtil;
3232
import org.springframework.data.mapping.MappingException;
33-
import org.springframework.data.mapping.PersistentEntity;
3433
import org.springframework.data.mapping.PersistentPropertyAccessor;
3534
import org.springframework.data.mapping.PreferredConstructor;
3635
import org.springframework.data.mapping.context.MappingContext;
@@ -285,30 +284,22 @@ public ReadingContext(RelationalPersistentEntity<T> entity, DataAccessStrategy a
285284
}
286285

287286
private ReadingContext<?> extendBy(RelationalPersistentProperty property) {
288-
return new ReadingContext<>(entity, accessStrategy, resultSet, path.extendBy(property));
287+
return new ReadingContext(getMappingContext().getRequiredPersistentEntity(property.getActualType()),
288+
accessStrategy, resultSet, path.extendBy(property));
289289
}
290290

291291
T mapRow() {
292292

293293
RelationalPersistentProperty idProperty = entity.getIdProperty();
294294

295-
Object idValue = null;
296-
if (idProperty != null) {
297-
idValue = readFrom(idProperty);
298-
}
299-
300-
T result = createInstanceInternal(entity, idValue);
295+
Object idValue = idProperty == null ? null : readFrom(idProperty);
301296

302-
return entity.requiresPropertyPopulation() //
303-
? populateProperties(result) //
304-
: result;
297+
return createInstanceInternal(idValue);
305298
}
306299

307-
private T populateProperties(T result) {
300+
private T populateProperties(T instance, @Nullable Object idValue) {
308301

309-
PersistentPropertyAccessor<T> propertyAccessor = getPropertyAccessor(entity, result);
310-
311-
Object id = idProperty == null ? null : readFrom(idProperty);
302+
PersistentPropertyAccessor<T> propertyAccessor = getPropertyAccessor(entity, instance);
312303

313304
PreferredConstructor<T, RelationalPersistentProperty> persistenceConstructor = entity.getPersistenceConstructor();
314305

@@ -318,7 +309,7 @@ private T populateProperties(T result) {
318309
continue;
319310
}
320311

321-
propertyAccessor.setProperty(property, readOrLoadProperty(id, property));
312+
propertyAccessor.setProperty(property, readOrLoadProperty(idValue, property));
322313
}
323314

324315
return propertyAccessor.getBean();
@@ -358,27 +349,17 @@ private Object readFrom(RelationalPersistentProperty property) {
358349
}
359350

360351
@SuppressWarnings("unchecked")
361-
private Object readEmbeddedEntityFrom(@Nullable Object id, RelationalPersistentProperty property) {
352+
private Object readEmbeddedEntityFrom(@Nullable Object idValue, RelationalPersistentProperty property) {
362353

363354
ReadingContext newContext = extendBy(property);
364355

365-
RelationalPersistentEntity<?> entity = getMappingContext().getRequiredPersistentEntity(property.getActualType());
366-
367-
Object instance = newContext.createInstanceInternal(entity, null);
368-
369-
PersistentPropertyAccessor<?> accessor = getPropertyAccessor((PersistentEntity<Object, ?>) entity, instance);
370-
371-
for (RelationalPersistentProperty p : entity) {
372-
accessor.setProperty(p, newContext.readOrLoadProperty(id, p));
373-
}
374-
375-
return instance;
356+
return newContext.createInstanceInternal(idValue);
376357
}
377358

378359
@Nullable
379360
private <S> S readEntityFrom(RelationalPersistentProperty property, PersistentPropertyPathExtension path) {
380361

381-
ReadingContext<?> newContext = extendBy(property);
362+
ReadingContext<S> newContext = (ReadingContext<S>) extendBy(property);
382363

383364
RelationalPersistentEntity<S> entity = (RelationalPersistentEntity<S>) getMappingContext()
384365
.getRequiredPersistentEntity(property.getActualType());
@@ -398,15 +379,7 @@ private <S> S readEntityFrom(RelationalPersistentProperty property, PersistentPr
398379
return null;
399380
}
400381

401-
S instance = newContext.createInstanceInternal(entity, idValue);
402-
403-
PersistentPropertyAccessor<S> accessor = getPropertyAccessor(entity, instance);
404-
405-
for (RelationalPersistentProperty p : entity) {
406-
accessor.setProperty(p, newContext.readOrLoadProperty(idValue, p));
407-
}
408-
409-
return instance;
382+
return newContext.createInstanceInternal(idValue);
410383
}
411384

412385
@Nullable
@@ -419,9 +392,9 @@ private Object getObjectFromResultSet(String backreferenceName) {
419392
}
420393
}
421394

422-
private <S> S createInstanceInternal(RelationalPersistentEntity<S> entity, @Nullable Object idValue) {
395+
private T createInstanceInternal(@Nullable Object idValue) {
423396

424-
return createInstance(entity, parameter -> {
397+
T instance = createInstance(entity, parameter -> {
425398

426399
String parameterName = parameter.getName();
427400

@@ -431,6 +404,7 @@ private <S> S createInstanceInternal(RelationalPersistentEntity<S> entity, @Null
431404

432405
return readOrLoadProperty(idValue, property);
433406
});
407+
return populateProperties(instance, idValue);
434408
}
435409

436410
}

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

Lines changed: 126 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,131 @@ public void chainedEntitiesWithoutId() throws SQLException {
284284
fixture.assertOn(extracted);
285285
}
286286

287+
// Model classes to be used in tests
288+
289+
@RequiredArgsConstructor
290+
static class TrivialImmutable {
291+
292+
@Id private final Long id;
293+
private final String name;
294+
}
295+
296+
static class Trivial {
297+
298+
@Id Long id;
299+
String name;
300+
}
301+
302+
static class OneToOne {
303+
304+
@Id Long id;
305+
String name;
306+
Trivial child;
307+
}
308+
309+
@RequiredArgsConstructor
310+
static class OneToOneImmutable {
311+
312+
private final @Id Long id;
313+
private final String name;
314+
private final TrivialImmutable child;
315+
}
316+
317+
static class OneToSet {
318+
319+
@Id Long id;
320+
String name;
321+
Set<Trivial> children;
322+
}
323+
324+
static class OneToMap {
325+
326+
@Id Long id;
327+
String name;
328+
Map<String, Trivial> children;
329+
}
330+
331+
static class OneToList {
332+
333+
@Id Long id;
334+
String name;
335+
List<Trivial> children;
336+
}
337+
338+
static class EmbeddedEntity {
339+
340+
@Id Long id;
341+
String name;
342+
@Embedded("prefix_") Trivial children;
343+
}
344+
345+
private static class DontUseSetter {
346+
String value;
347+
348+
DontUseSetter(@Param("value") String value) {
349+
this.value = "setThroughConstructor:" + value;
350+
}
351+
}
352+
353+
static class MixedProperties {
354+
355+
final String one;
356+
String two;
357+
final String three;
358+
359+
@PersistenceConstructor
360+
MixedProperties(String one) {
361+
this.one = one;
362+
this.three = "unset";
363+
}
364+
365+
private MixedProperties(String one, String two, String three) {
366+
367+
this.one = one;
368+
this.two = two;
369+
this.three = three;
370+
}
371+
372+
MixedProperties withThree(String three) {
373+
return new MixedProperties(one, two, three);
374+
}
375+
}
376+
377+
@AllArgsConstructor
378+
static class EntityWithListInConstructor {
379+
380+
@Id final Long id;
381+
382+
final List<Trivial> content;
383+
}
384+
385+
static class NoIdChain0 {
386+
String zeroValue;
387+
}
388+
389+
static class NoIdChain1 {
390+
String oneValue;
391+
NoIdChain0 chain0;
392+
}
393+
394+
static class NoIdChain2 {
395+
String twoValue;
396+
NoIdChain1 chain1;
397+
}
398+
399+
static class NoIdChain3 {
400+
String threeValue;
401+
NoIdChain2 chain2;
402+
}
403+
404+
static class NoIdChain4 {
405+
@Id Long four;
406+
String fourValue;
407+
NoIdChain3 chain3;
408+
}
409+
410+
// Infrastructure for assertions and constructing mocks
411+
287412
private <T> FixtureBuilder<T> buildFixture() {
288413
return new FixtureBuilder<>();
289414
}
@@ -418,129 +543,6 @@ private boolean next() {
418543
}
419544
}
420545

421-
@RequiredArgsConstructor
422-
@Wither
423-
static class TrivialImmutable {
424-
425-
@Id private final Long id;
426-
private final String name;
427-
}
428-
429-
static class Trivial {
430-
431-
@Id Long id;
432-
String name;
433-
}
434-
435-
static class OneToOne {
436-
437-
@Id Long id;
438-
String name;
439-
Trivial child;
440-
}
441-
442-
@RequiredArgsConstructor
443-
@Wither
444-
static class OneToOneImmutable {
445-
446-
private final @Id Long id;
447-
private final String name;
448-
private final TrivialImmutable child;
449-
}
450-
451-
static class OneToSet {
452-
453-
@Id Long id;
454-
String name;
455-
Set<Trivial> children;
456-
}
457-
458-
static class OneToMap {
459-
460-
@Id Long id;
461-
String name;
462-
Map<String, Trivial> children;
463-
}
464-
465-
static class OneToList {
466-
467-
@Id Long id;
468-
String name;
469-
List<Trivial> children;
470-
}
471-
472-
static class EmbeddedEntity {
473-
474-
@Id Long id;
475-
String name;
476-
@Embedded("prefix_") Trivial children;
477-
}
478-
479-
private static class DontUseSetter {
480-
String value;
481-
482-
DontUseSetter(@Param("value") String value) {
483-
this.value = "setThroughConstructor:" + value;
484-
}
485-
}
486-
487-
static class MixedProperties {
488-
489-
final String one;
490-
String two;
491-
final String three;
492-
493-
@PersistenceConstructor
494-
MixedProperties(String one) {
495-
this.one = one;
496-
this.three = "unset";
497-
}
498-
499-
private MixedProperties(String one, String two, String three) {
500-
501-
this.one = one;
502-
this.two = two;
503-
this.three = three;
504-
}
505-
506-
MixedProperties withThree(String three) {
507-
return new MixedProperties(one, two, three);
508-
}
509-
}
510-
511-
@AllArgsConstructor
512-
static class EntityWithListInConstructor {
513-
514-
@Id final Long id;
515-
516-
final List<Trivial> content;
517-
}
518-
519-
static class NoIdChain0 {
520-
String zeroValue;
521-
}
522-
523-
static class NoIdChain1 {
524-
String oneValue;
525-
NoIdChain0 chain0;
526-
}
527-
528-
static class NoIdChain2 {
529-
String twoValue;
530-
NoIdChain1 chain1;
531-
}
532-
533-
static class NoIdChain3 {
534-
String threeValue;
535-
NoIdChain2 chain2;
536-
}
537-
538-
static class NoIdChain4 {
539-
@Id Long four;
540-
String fourValue;
541-
NoIdChain3 chain3;
542-
}
543-
544546
private interface SetValue<T> {
545547
SetColumns<T> value(Object value);
546548

@@ -609,6 +611,7 @@ public SetValue<T> endUpIn(Function<T, Object> extractor) {
609611

610612
@AllArgsConstructor
611613
private static class Fixture<T> {
614+
612615
final ResultSet resultSet;
613616
final List<Expectation<T>> expectations;
614617

0 commit comments

Comments
 (0)