Skip to content

Commit

Permalink
Fix loading of nested embedded entities.
Browse files Browse the repository at this point in the history
Closes #1676
  • Loading branch information
schauder committed Dec 5, 2023
1 parent f947471 commit 0c70f15
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,21 @@ public void findAllFindsAllEntities() {
.containsExactlyInAnyOrder(entity.getId(), other.getId());
}

@Test // GH-1676
public void findAllFindsAllEntitiesWithOnlyReferenceNotNull() {

DummyEntity entity = createDummyEntity();
entity.prefixedEmbeddable.test = null;
entity = repository.save(entity);
DummyEntity other = repository.save(createDummyEntity());

Iterable<DummyEntity> all = repository.findAll();

assertThat(all)//
.extracting(DummyEntity::getId)//
.containsExactlyInAnyOrder(entity.getId(), other.getId());
}

@Test // DATAJDBC-111
public void findByIdReturnsEmptyWhenNoneFound() {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,7 @@
import org.springframework.data.mapping.PersistentPropertyAccessor;
import org.springframework.data.mapping.PersistentPropertyPathAccessor;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.model.ConvertingPropertyAccessor;
import org.springframework.data.mapping.model.DefaultSpELExpressionEvaluator;
import org.springframework.data.mapping.model.EntityInstantiator;
import org.springframework.data.mapping.model.ParameterValueProvider;
import org.springframework.data.mapping.model.PersistentEntityParameterValueProvider;
import org.springframework.data.mapping.model.PropertyValueProvider;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.data.mapping.model.SpELContext;
import org.springframework.data.mapping.model.SpELExpressionEvaluator;
import org.springframework.data.mapping.model.SpELExpressionParameterValueProvider;
import org.springframework.data.mapping.model.*;
import org.springframework.data.projection.EntityProjection;
import org.springframework.data.projection.EntityProjectionIntrospector;
import org.springframework.data.projection.EntityProjectionIntrospector.ProjectionPredicate;
Expand Down Expand Up @@ -307,7 +298,7 @@ protected <S> S read(TypeInformation<S> type, RowDocument source) {
* @return the converted object, will never be {@literal null}.
*/
protected <S> S readAggregate(ConversionContext context, RowDocument document,
TypeInformation<? extends S> typeHint) {
TypeInformation<? extends S> typeHint) {
return readAggregate(context, new RowDocumentAccessor(document), typeHint);
}

Expand All @@ -321,7 +312,7 @@ protected <S> S readAggregate(ConversionContext context, RowDocument document,
*/
@SuppressWarnings("unchecked")
protected <S> S readAggregate(ConversionContext context, RowDocumentAccessor documentAccessor,
TypeInformation<? extends S> typeHint) {
TypeInformation<? extends S> typeHint) {

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

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

@SuppressWarnings("ConstantConditions")
private <T> T doConvert(Object value, Class<? extends T> target,
@Nullable Class<? extends T> fallback) {
private <T> T doConvert(Object value, Class<? extends T> target, @Nullable Class<? extends T> fallback) {

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

return new ConverterAwareSpELExpressionParameterValueProvider(context, evaluator, getConversionService(),
new ConvertingParameterValueProvider<>( parameterProvider::getParameterValue));
new ConvertingParameterValueProvider<>(parameterProvider::getParameterValue));
}

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

if (shouldReadEmbeddable(conversionContext, property, persistentEntity, provider)) {
if (shouldReadEmbeddable(conversionContext, property, persistentEntity, provider, source)) {
return read(conversionContext, persistentEntity, source);
}

return null;
}

private boolean shouldReadEmbeddable(ConversionContext context, RelationalPersistentProperty property,
RelationalPersistentEntity<?> unwrappedEntity, RelationalPropertyValueProvider propertyValueProvider) {
RelationalPersistentEntity<?> unwrappedEntity, RelationalPropertyValueProvider propertyValueProvider,
RowDocumentAccessor source) {

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

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

for (RelationalPersistentProperty persistentProperty : unwrappedEntity) {

RelationalPropertyValueProvider contextual = propertyValueProvider
.withContext(context.forProperty(persistentProperty));
ConversionContext nestedContext = context.forProperty(persistentProperty);
RelationalPropertyValueProvider contextual = propertyValueProvider.withContext(nestedContext);

if (persistentProperty.isEmbedded()) {

TypeInformation<?> typeInformation = persistentProperty.getTypeInformation();

RelationalPersistentEntity<?> nestedEntity = getMappingContext().getPersistentEntity(typeInformation);

if (readEmbedded(nestedContext, contextual, source, persistentProperty, nestedEntity) != null) {
return true;
}
}

if (contextual.hasValue(persistentProperty)) {
return true;
Expand Down Expand Up @@ -787,8 +789,7 @@ protected DefaultConversionContext(RelationalConverter sourceConverter,

@SuppressWarnings("unchecked")
@Override
public <S> S convert(Object source, TypeInformation<? extends S> typeHint,
ConversionContext context) {
public <S> S convert(Object source, TypeInformation<? extends S> typeHint, ConversionContext context) {

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

private record PropertyTranslatingPropertyAccessor<T> (PersistentPropertyAccessor<T> delegate,
private record PropertyTranslatingPropertyAccessor<T>(PersistentPropertyAccessor<T> delegate,
PersistentPropertyTranslator propertyTranslator) implements PersistentPropertyAccessor<T> {

static <T> PersistentPropertyAccessor<T> create(PersistentPropertyAccessor<T> delegate,
Expand Down

0 comments on commit 0c70f15

Please sign in to comment.