Skip to content

Commit dc0ffe9

Browse files
committed
Fix loading of nested embedded entities.
Closes #1684
1 parent f947471 commit dc0ffe9

File tree

2 files changed

+38
-22
lines changed

2 files changed

+38
-22
lines changed

spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryEmbeddedIntegrationTests.java

+15
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,21 @@ public void findAllFindsAllEntities() {
114114
.containsExactlyInAnyOrder(entity.getId(), other.getId());
115115
}
116116

117+
@Test // GH-1676
118+
public void findAllFindsAllEntitiesWithOnlyReferenceNotNull() {
119+
120+
DummyEntity entity = createDummyEntity();
121+
entity.prefixedEmbeddable.test = null;
122+
entity = repository.save(entity);
123+
DummyEntity other = repository.save(createDummyEntity());
124+
125+
Iterable<DummyEntity> all = repository.findAll();
126+
127+
assertThat(all)//
128+
.extracting(DummyEntity::getId)//
129+
.containsExactlyInAnyOrder(entity.getId(), other.getId());
130+
}
131+
117132
@Test // DATAJDBC-111
118133
public void findByIdReturnsEmptyWhenNoneFound() {
119134

spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/MappingRelationalConverter.java

+23-22
Original file line numberDiff line numberDiff line change
@@ -41,16 +41,7 @@
4141
import org.springframework.data.mapping.PersistentPropertyAccessor;
4242
import org.springframework.data.mapping.PersistentPropertyPathAccessor;
4343
import org.springframework.data.mapping.context.MappingContext;
44-
import org.springframework.data.mapping.model.ConvertingPropertyAccessor;
45-
import org.springframework.data.mapping.model.DefaultSpELExpressionEvaluator;
46-
import org.springframework.data.mapping.model.EntityInstantiator;
47-
import org.springframework.data.mapping.model.ParameterValueProvider;
48-
import org.springframework.data.mapping.model.PersistentEntityParameterValueProvider;
49-
import org.springframework.data.mapping.model.PropertyValueProvider;
50-
import org.springframework.data.mapping.model.SimpleTypeHolder;
51-
import org.springframework.data.mapping.model.SpELContext;
52-
import org.springframework.data.mapping.model.SpELExpressionEvaluator;
53-
import org.springframework.data.mapping.model.SpELExpressionParameterValueProvider;
44+
import org.springframework.data.mapping.model.*;
5445
import org.springframework.data.projection.EntityProjection;
5546
import org.springframework.data.projection.EntityProjectionIntrospector;
5647
import org.springframework.data.projection.EntityProjectionIntrospector.ProjectionPredicate;
@@ -307,7 +298,7 @@ protected <S> S read(TypeInformation<S> type, RowDocument source) {
307298
* @return the converted object, will never be {@literal null}.
308299
*/
309300
protected <S> S readAggregate(ConversionContext context, RowDocument document,
310-
TypeInformation<? extends S> typeHint) {
301+
TypeInformation<? extends S> typeHint) {
311302
return readAggregate(context, new RowDocumentAccessor(document), typeHint);
312303
}
313304

@@ -321,7 +312,7 @@ protected <S> S readAggregate(ConversionContext context, RowDocument document,
321312
*/
322313
@SuppressWarnings("unchecked")
323314
protected <S> S readAggregate(ConversionContext context, RowDocumentAccessor documentAccessor,
324-
TypeInformation<? extends S> typeHint) {
315+
TypeInformation<? extends S> typeHint) {
325316

326317
Class<? extends S> rawType = typeHint.getType();
327318

@@ -430,8 +421,7 @@ private <T> T doConvert(Object value, Class<? extends T> target) {
430421
}
431422

432423
@SuppressWarnings("ConstantConditions")
433-
private <T> T doConvert(Object value, Class<? extends T> target,
434-
@Nullable Class<? extends T> fallback) {
424+
private <T> T doConvert(Object value, Class<? extends T> target, @Nullable Class<? extends T> fallback) {
435425

436426
if (getConversionService().canConvert(value.getClass(), target) || fallback == null) {
437427
return getConversionService().convert(value, target);
@@ -504,7 +494,7 @@ public RelationalPropertyValueProvider withContext(ConversionContext context) {
504494
entity, contextualizing, context.getPath().getCurrentObject());
505495

506496
return new ConverterAwareSpELExpressionParameterValueProvider(context, evaluator, getConversionService(),
507-
new ConvertingParameterValueProvider<>( parameterProvider::getParameterValue));
497+
new ConvertingParameterValueProvider<>(parameterProvider::getParameterValue));
508498
}
509499

510500
private <S> S populateProperties(ConversionContext context, RelationalPersistentEntity<S> entity,
@@ -562,15 +552,16 @@ private Object readEmbedded(ConversionContext conversionContext, RelationalPrope
562552
RowDocumentAccessor source, RelationalPersistentProperty property,
563553
RelationalPersistentEntity<?> persistentEntity) {
564554

565-
if (shouldReadEmbeddable(conversionContext, property, persistentEntity, provider)) {
555+
if (shouldReadEmbeddable(conversionContext, property, persistentEntity, provider, source)) {
566556
return read(conversionContext, persistentEntity, source);
567557
}
568558

569559
return null;
570560
}
571561

572562
private boolean shouldReadEmbeddable(ConversionContext context, RelationalPersistentProperty property,
573-
RelationalPersistentEntity<?> unwrappedEntity, RelationalPropertyValueProvider propertyValueProvider) {
563+
RelationalPersistentEntity<?> unwrappedEntity, RelationalPropertyValueProvider propertyValueProvider,
564+
RowDocumentAccessor source) {
574565

575566
OnEmpty onEmpty = property.getRequiredAnnotation(Embedded.class).onEmpty();
576567

@@ -580,8 +571,19 @@ private boolean shouldReadEmbeddable(ConversionContext context, RelationalPersis
580571

581572
for (RelationalPersistentProperty persistentProperty : unwrappedEntity) {
582573

583-
RelationalPropertyValueProvider contextual = propertyValueProvider
584-
.withContext(context.forProperty(persistentProperty));
574+
ConversionContext nestedContext = context.forProperty(persistentProperty);
575+
RelationalPropertyValueProvider contextual = propertyValueProvider.withContext(nestedContext);
576+
577+
if (persistentProperty.isEmbedded()) {
578+
579+
TypeInformation<?> typeInformation = persistentProperty.getTypeInformation();
580+
581+
RelationalPersistentEntity<?> nestedEntity = getMappingContext().getPersistentEntity(typeInformation);
582+
583+
if (readEmbedded(nestedContext, contextual, source, persistentProperty, nestedEntity) != null) {
584+
return true;
585+
}
586+
}
585587

586588
if (contextual.hasValue(persistentProperty)) {
587589
return true;
@@ -787,8 +789,7 @@ protected DefaultConversionContext(RelationalConverter sourceConverter,
787789

788790
@SuppressWarnings("unchecked")
789791
@Override
790-
public <S> S convert(Object source, TypeInformation<? extends S> typeHint,
791-
ConversionContext context) {
792+
public <S> S convert(Object source, TypeInformation<? extends S> typeHint, ConversionContext context) {
792793

793794
Assert.notNull(source, "Source must not be null");
794795
Assert.notNull(typeHint, "TypeInformation must not be null");
@@ -1196,7 +1197,7 @@ protected <T> T potentiallyConvertSpelValue(Object object, Parameter<T, Relation
11961197
}
11971198
}
11981199

1199-
private record PropertyTranslatingPropertyAccessor<T> (PersistentPropertyAccessor<T> delegate,
1200+
private record PropertyTranslatingPropertyAccessor<T>(PersistentPropertyAccessor<T> delegate,
12001201
PersistentPropertyTranslator propertyTranslator) implements PersistentPropertyAccessor<T> {
12011202

12021203
static <T> PersistentPropertyAccessor<T> create(PersistentPropertyAccessor<T> delegate,

0 commit comments

Comments
 (0)