From 37c0c20c10132fafebf772bbe63d225a0d2b1fe2 Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Fri, 7 Jun 2024 16:25:24 +0200 Subject: [PATCH 1/2] 1737-nullable-embedded-with-collection - 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 6050cb016e..04a90d5855 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-relational-parent - 3.4.0-SNAPSHOT + 3.4.0-1737-nullable-embedded-with-collection-SNAPSHOT pom Spring Data Relational Parent diff --git a/spring-data-jdbc-distribution/pom.xml b/spring-data-jdbc-distribution/pom.xml index 4626db4364..583df255fa 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.4.0-SNAPSHOT + 3.4.0-1737-nullable-embedded-with-collection-SNAPSHOT ../pom.xml diff --git a/spring-data-jdbc/pom.xml b/spring-data-jdbc/pom.xml index aa44f71832..dc1217579b 100644 --- a/spring-data-jdbc/pom.xml +++ b/spring-data-jdbc/pom.xml @@ -6,7 +6,7 @@ 4.0.0 spring-data-jdbc - 3.4.0-SNAPSHOT + 3.4.0-1737-nullable-embedded-with-collection-SNAPSHOT Spring Data JDBC Spring Data module for JDBC repositories. @@ -15,7 +15,7 @@ org.springframework.data spring-data-relational-parent - 3.4.0-SNAPSHOT + 3.4.0-1737-nullable-embedded-with-collection-SNAPSHOT diff --git a/spring-data-r2dbc/pom.xml b/spring-data-r2dbc/pom.xml index 3168f9d4f6..979b7a28a1 100644 --- a/spring-data-r2dbc/pom.xml +++ b/spring-data-r2dbc/pom.xml @@ -6,7 +6,7 @@ 4.0.0 spring-data-r2dbc - 3.4.0-SNAPSHOT + 3.4.0-1737-nullable-embedded-with-collection-SNAPSHOT Spring Data R2DBC Spring Data module for R2DBC @@ -15,7 +15,7 @@ org.springframework.data spring-data-relational-parent - 3.4.0-SNAPSHOT + 3.4.0-1737-nullable-embedded-with-collection-SNAPSHOT diff --git a/spring-data-relational/pom.xml b/spring-data-relational/pom.xml index 3ba39b861c..f10edf138a 100644 --- a/spring-data-relational/pom.xml +++ b/spring-data-relational/pom.xml @@ -6,7 +6,7 @@ 4.0.0 spring-data-relational - 3.4.0-SNAPSHOT + 3.4.0-1737-nullable-embedded-with-collection-SNAPSHOT Spring Data Relational Spring Data Relational support @@ -14,7 +14,7 @@ org.springframework.data spring-data-relational-parent - 3.4.0-SNAPSHOT + 3.4.0-1737-nullable-embedded-with-collection-SNAPSHOT From 46bc937b631c15425247d46576f5f87321ce79d1 Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Sun, 9 Jun 2024 09:05:04 +0200 Subject: [PATCH 2/2] Consider embedded entities with empty non entity collections empty. Embedded entities which contain a empty collection of values that aren't entities are now considered empty, if this collection is empty. Closes #1737 --- .../core/convert/MappingJdbcConverter.java | 2 +- ...JdbcAggregateTemplateIntegrationTests.java | 62 +++++++++++++++---- .../MappingRelationalConverter.java | 49 +++++++++++---- 3 files changed, 88 insertions(+), 25 deletions(-) diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/MappingJdbcConverter.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/MappingJdbcConverter.java index ebb4d58717..97db411446 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/MappingJdbcConverter.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/MappingJdbcConverter.java @@ -421,7 +421,7 @@ public T getPropertyValue(RelationalPersistentProperty property) { @Override public boolean hasValue(RelationalPersistentProperty property) { - if (property.isCollectionLike() || property.isMap()) { + if ((property.isCollectionLike() && property.isEntity())|| property.isMap()) { // attempt relation fetch return true; } diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/AbstractJdbcAggregateTemplateIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/AbstractJdbcAggregateTemplateIntegrationTests.java index c7ac9275ab..a4ad36e04f 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/AbstractJdbcAggregateTemplateIntegrationTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/AbstractJdbcAggregateTemplateIntegrationTests.java @@ -22,16 +22,8 @@ import static org.springframework.data.jdbc.testing.TestDatabaseFeatures.Feature.*; import java.time.LocalDateTime; +import java.util.*; import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; import java.util.function.Function; import java.util.stream.IntStream; @@ -61,6 +53,7 @@ import org.springframework.data.mapping.context.InvalidPersistentPropertyPath; import org.springframework.data.relational.core.conversion.DbActionExecutionException; import org.springframework.data.relational.core.mapping.Column; +import org.springframework.data.relational.core.mapping.Embedded; import org.springframework.data.relational.core.mapping.InsertOnlyProperty; import org.springframework.data.relational.core.mapping.MappedCollection; import org.springframework.data.relational.core.mapping.RelationalMappingContext; @@ -778,6 +771,36 @@ void saveAndLoadAnEntityWithSet() { assertThat(reloaded.digits).isEqualTo(new HashSet<>(asList("one", "two", "three"))); } + @Test //GH-1737 + @EnabledOnFeature(SUPPORTS_ARRAYS) + void saveAndLoadEmbeddedArray() { + + EmbeddedStringListOwner embeddedStringListOwner = new EmbeddedStringListOwner(); + embeddedStringListOwner.embeddedStringList = new EmbeddedStringList(); + embeddedStringListOwner.embeddedStringList.digits = List.of("one", "two", "three"); + + EmbeddedStringListOwner saved = template.save(embeddedStringListOwner); + + EmbeddedStringListOwner reloaded = template.findById(saved.id, EmbeddedStringListOwner.class); + + assertThat(reloaded.embeddedStringList.digits).containsExactly("one", "two", "three"); + } + + @Test //GH-1737 + @EnabledOnFeature(SUPPORTS_ARRAYS) + void saveAndLoadEmptyEmbeddedArray() { + + EmbeddedStringListOwner embeddedStringListOwner = new EmbeddedStringListOwner(); + embeddedStringListOwner.embeddedStringList = new EmbeddedStringList(); + embeddedStringListOwner.embeddedStringList.digits = emptyList(); + + EmbeddedStringListOwner saved = template.save(embeddedStringListOwner); + + EmbeddedStringListOwner reloaded = template.findById(saved.id, EmbeddedStringListOwner.class); + + assertThat(reloaded.embeddedStringList).isNull(); + } + @Test // DATAJDBC-327 void saveAndLoadAnEntityWithByteArray() { @@ -919,7 +942,7 @@ void readOnlyGetsLoadedButNotWritten() { assertThat( jdbcTemplate.queryForObject("SELECT read_only FROM with_read_only", Collections.emptyMap(), String.class)) - .isEqualTo("from-db"); + .isEqualTo("from-db"); } @Test @@ -1258,7 +1281,8 @@ void recordOfSet() { @Test // GH-1656 void mapWithEnumKey() { - EnumMapOwner enumMapOwner = template.save(new EnumMapOwner(null, "OwnerName", Map.of(Color.BLUE, new MapElement("Element")))); + EnumMapOwner enumMapOwner = template + .save(new EnumMapOwner(null, "OwnerName", Map.of(Color.BLUE, new MapElement("Element")))); Iterable enumMapOwners = template.findAll(EnumMapOwner.class); @@ -1266,7 +1290,7 @@ void mapWithEnumKey() { } @Test // GH-1684 - void oneToOneWithIdenticalIdColumnName(){ + void oneToOneWithIdenticalIdColumnName() { WithOneToOne saved = template.insert(new WithOneToOne("one", new Referenced(23L))); @@ -1369,6 +1393,17 @@ private static class FloatListOwner { List digits = new ArrayList<>(); } + @Table("ARRAY_OWNER") + private static class EmbeddedStringListOwner { + @Id Long id; + + @Embedded(onEmpty = Embedded.OnEmpty.USE_NULL, prefix = "") EmbeddedStringList embeddedStringList; + } + + private static class EmbeddedStringList { + List digits = new ArrayList<>(); + } + static class LegoSet { @Column("id1") @@ -2096,7 +2131,8 @@ record Book(String name) { record EnumMapOwner(@Id Long id, String name, Map map) { } - record WithOneToOne(@Id String id,@MappedCollection(idColumn = "renamed") Referenced referenced){} + record WithOneToOne(@Id String id, @MappedCollection(idColumn = "renamed") Referenced referenced) { + } record Referenced(@Id Long id) { } 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 3469c8b9fd..3233d1a270 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 @@ -44,16 +44,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.CachingValueExpressionEvaluatorFactory; -import org.springframework.data.mapping.model.ConvertingPropertyAccessor; -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.ValueExpressionEvaluator; -import org.springframework.data.mapping.model.ValueExpressionParameterValueProvider; +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; @@ -1168,7 +1159,43 @@ public Object getValue(AggregatePath path) { @Override public boolean hasValue(AggregatePath path) { - return document.get(path.getColumnInfo().alias().getReference()) != null; + Object value = document.get(path.getColumnInfo().alias().getReference()); + + if (value == null) { + return false; + } + if (!path.isCollectionLike()) { + return true; + } + + if (value instanceof char[] ar) { + return ar.length != 0; + } + if (value instanceof byte[] ar) { + return ar.length != 0; + } + if (value instanceof short[] ar) { + return ar.length != 0; + } + if (value instanceof int[] ar) { + return ar.length != 0; + } + if (value instanceof long[] ar) { + return ar.length != 0; + } + if (value instanceof float[] ar) { + return ar.length != 0; + } + if (value instanceof double[] ar) { + return ar.length != 0; + } + if (value instanceof Object[] ar) { + return ar.length != 0; + } + if (value instanceof Collection col) { + return !col.isEmpty(); + } + return true; } @Override