From f9474718c57196ccce34a4cf4667b540bd451d44 Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Mon, 4 Dec 2023 13:55:14 +0100 Subject: [PATCH 1/3] npe - Prepare branch --- pom.xml | 2 +- spring-data-jdbc-distribution/pom.xml | 2 +- spring-data-jdbc/pom.xml | 4 ++-- spring-data-r2dbc/pom.xml | 4 ++-- spring-data-relational/pom.xml | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index 02461b8e40..7de5eb753d 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-relational-parent - 3.3.0-SNAPSHOT + 3.3.0-npe-SNAPSHOT pom Spring Data Relational Parent diff --git a/spring-data-jdbc-distribution/pom.xml b/spring-data-jdbc-distribution/pom.xml index 8d987fb028..09ffb728cc 100644 --- a/spring-data-jdbc-distribution/pom.xml +++ b/spring-data-jdbc-distribution/pom.xml @@ -14,7 +14,7 @@ org.springframework.data spring-data-relational-parent - 3.3.0-SNAPSHOT + 3.3.0-npe-SNAPSHOT ../pom.xml diff --git a/spring-data-jdbc/pom.xml b/spring-data-jdbc/pom.xml index fddbaab696..d775a343dc 100644 --- a/spring-data-jdbc/pom.xml +++ b/spring-data-jdbc/pom.xml @@ -6,7 +6,7 @@ 4.0.0 spring-data-jdbc - 3.3.0-SNAPSHOT + 3.3.0-npe-SNAPSHOT Spring Data JDBC Spring Data module for JDBC repositories. @@ -15,7 +15,7 @@ org.springframework.data spring-data-relational-parent - 3.3.0-SNAPSHOT + 3.3.0-npe-SNAPSHOT diff --git a/spring-data-r2dbc/pom.xml b/spring-data-r2dbc/pom.xml index b6ff5c9a15..3c7e256029 100644 --- a/spring-data-r2dbc/pom.xml +++ b/spring-data-r2dbc/pom.xml @@ -6,7 +6,7 @@ 4.0.0 spring-data-r2dbc - 3.3.0-SNAPSHOT + 3.3.0-npe-SNAPSHOT Spring Data R2DBC Spring Data module for R2DBC @@ -15,7 +15,7 @@ org.springframework.data spring-data-relational-parent - 3.3.0-SNAPSHOT + 3.3.0-npe-SNAPSHOT diff --git a/spring-data-relational/pom.xml b/spring-data-relational/pom.xml index 671e71d242..4a31904b2d 100644 --- a/spring-data-relational/pom.xml +++ b/spring-data-relational/pom.xml @@ -6,7 +6,7 @@ 4.0.0 spring-data-relational - 3.3.0-SNAPSHOT + 3.3.0-npe-SNAPSHOT Spring Data Relational Spring Data Relational support @@ -14,7 +14,7 @@ org.springframework.data spring-data-relational-parent - 3.3.0-SNAPSHOT + 3.3.0-npe-SNAPSHOT From 0c70f154c855d38a15f9f213f10e87b6cd955f89 Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Mon, 4 Dec 2023 14:47:57 +0100 Subject: [PATCH 2/3] Fix loading of nested embedded entities. Closes #1676 --- ...dbcRepositoryEmbeddedIntegrationTests.java | 15 +++++++ .../MappingRelationalConverter.java | 45 ++++++++++--------- 2 files changed, 38 insertions(+), 22 deletions(-) diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryEmbeddedIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryEmbeddedIntegrationTests.java index d3c12ea86e..8a7b07c7d4 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryEmbeddedIntegrationTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryEmbeddedIntegrationTests.java @@ -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 all = repository.findAll(); + + assertThat(all)// + .extracting(DummyEntity::getId)// + .containsExactlyInAnyOrder(entity.getId(), other.getId()); + } + @Test // DATAJDBC-111 public void findByIdReturnsEmptyWhenNoneFound() { diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/MappingRelationalConverter.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/MappingRelationalConverter.java index 91da5513f9..b587d3187e 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/MappingRelationalConverter.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/MappingRelationalConverter.java @@ -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; @@ -307,7 +298,7 @@ protected S read(TypeInformation type, RowDocument source) { * @return the converted object, will never be {@literal null}. */ protected S readAggregate(ConversionContext context, RowDocument document, - TypeInformation typeHint) { + TypeInformation typeHint) { return readAggregate(context, new RowDocumentAccessor(document), typeHint); } @@ -321,7 +312,7 @@ protected S readAggregate(ConversionContext context, RowDocument document, */ @SuppressWarnings("unchecked") protected S readAggregate(ConversionContext context, RowDocumentAccessor documentAccessor, - TypeInformation typeHint) { + TypeInformation typeHint) { Class rawType = typeHint.getType(); @@ -430,8 +421,7 @@ private T doConvert(Object value, Class target) { } @SuppressWarnings("ConstantConditions") - private T doConvert(Object value, Class target, - @Nullable Class fallback) { + private T doConvert(Object value, Class target, @Nullable Class fallback) { if (getConversionService().canConvert(value.getClass(), target) || fallback == null) { return getConversionService().convert(value, target); @@ -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 populateProperties(ConversionContext context, RelationalPersistentEntity entity, @@ -562,7 +552,7 @@ 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); } @@ -570,7 +560,8 @@ private Object readEmbedded(ConversionContext conversionContext, RelationalPrope } private boolean shouldReadEmbeddable(ConversionContext context, RelationalPersistentProperty property, - RelationalPersistentEntity unwrappedEntity, RelationalPropertyValueProvider propertyValueProvider) { + RelationalPersistentEntity unwrappedEntity, RelationalPropertyValueProvider propertyValueProvider, + RowDocumentAccessor source) { OnEmpty onEmpty = property.getRequiredAnnotation(Embedded.class).onEmpty(); @@ -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; @@ -787,8 +789,7 @@ protected DefaultConversionContext(RelationalConverter sourceConverter, @SuppressWarnings("unchecked") @Override - public S convert(Object source, TypeInformation typeHint, - ConversionContext context) { + public S convert(Object source, TypeInformation typeHint, ConversionContext context) { Assert.notNull(source, "Source must not be null"); Assert.notNull(typeHint, "TypeInformation must not be null"); @@ -1196,7 +1197,7 @@ protected T potentiallyConvertSpelValue(Object object, Parameter (PersistentPropertyAccessor delegate, + private record PropertyTranslatingPropertyAccessor(PersistentPropertyAccessor delegate, PersistentPropertyTranslator propertyTranslator) implements PersistentPropertyAccessor { static PersistentPropertyAccessor create(PersistentPropertyAccessor delegate, From 13190883d986e8ece461fc9904655a10813d0758 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 5 Dec 2023 10:57:29 +0100 Subject: [PATCH 3/3] Recursively check embedded property loading. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also, avoid falling back into hasValue(…) code path. --- .../conversion/MappingRelationalConverter.java | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/MappingRelationalConverter.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/MappingRelationalConverter.java index b587d3187e..55e03c3121 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/MappingRelationalConverter.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/MappingRelationalConverter.java @@ -552,7 +552,7 @@ private Object readEmbedded(ConversionContext conversionContext, RelationalPrope RowDocumentAccessor source, RelationalPersistentProperty property, RelationalPersistentEntity persistentEntity) { - if (shouldReadEmbeddable(conversionContext, property, persistentEntity, provider, source)) { + if (shouldReadEmbeddable(conversionContext, property, persistentEntity, provider)) { return read(conversionContext, persistentEntity, source); } @@ -560,8 +560,7 @@ private Object readEmbedded(ConversionContext conversionContext, RelationalPrope } private boolean shouldReadEmbeddable(ConversionContext context, RelationalPersistentProperty property, - RelationalPersistentEntity unwrappedEntity, RelationalPropertyValueProvider propertyValueProvider, - RowDocumentAccessor source) { + RelationalPersistentEntity unwrappedEntity, RelationalPropertyValueProvider propertyValueProvider) { OnEmpty onEmpty = property.getRequiredAnnotation(Embedded.class).onEmpty(); @@ -576,16 +575,14 @@ private boolean shouldReadEmbeddable(ConversionContext context, RelationalPersis if (persistentProperty.isEmbedded()) { - TypeInformation typeInformation = persistentProperty.getTypeInformation(); + RelationalPersistentEntity nestedEntity = getMappingContext() + .getRequiredPersistentEntity(persistentProperty); - RelationalPersistentEntity nestedEntity = getMappingContext().getPersistentEntity(typeInformation); - - if (readEmbedded(nestedContext, contextual, source, persistentProperty, nestedEntity) != null) { + if (shouldReadEmbeddable(nestedContext, persistentProperty, nestedEntity, contextual)) { return true; } - } - if (contextual.hasValue(persistentProperty)) { + } else if (contextual.hasValue(persistentProperty)) { return true; } }