diff --git a/core/src/main/java/net/lecousin/reactive/data/relational/enhance/EntityState.java b/core/src/main/java/net/lecousin/reactive/data/relational/enhance/EntityState.java index 55a04fc..f7cce16 100644 --- a/core/src/main/java/net/lecousin/reactive/data/relational/enhance/EntityState.java +++ b/core/src/main/java/net/lecousin/reactive/data/relational/enhance/EntityState.java @@ -13,22 +13,6 @@ */ package net.lecousin.reactive.data.relational.enhance; -import java.lang.reflect.Array; -import java.lang.reflect.Field; -import java.util.Collection; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.function.Function; -import java.util.function.Supplier; - -import org.apache.commons.lang3.mutable.MutableObject; -import org.springframework.core.CollectionFactory; -import org.springframework.data.mapping.MappingException; -import org.springframework.lang.NonNull; -import org.springframework.lang.Nullable; - import net.lecousin.reactive.data.relational.LcReactiveDataRelationalClient; import net.lecousin.reactive.data.relational.model.ModelAccessException; import net.lecousin.reactive.data.relational.model.ModelUtils; @@ -38,14 +22,28 @@ import net.lecousin.reactive.data.relational.model.metadata.PropertyStaticMetadata; import net.lecousin.reactive.data.relational.query.SelectQuery; import net.lecousin.reactive.data.relational.query.criteria.Criteria; +import org.apache.commons.lang3.mutable.MutableObject; +import org.springframework.core.CollectionFactory; +import org.springframework.data.mapping.MappingException; +import org.springframework.lang.NonNull; +import org.springframework.lang.Nullable; import reactor.core.CorePublisher; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; +import java.util.function.Supplier; + /** * Internal state of an entity, allowing to implement features such as lazy loading, updated attributes detection... * * @author Guillaume Le Cousin + * @author Sebastian Nawrocki * */ public class EntityState { @@ -154,9 +152,10 @@ private void updatePersistedValues(Object entity) { private void savePersistedValue(Field field, Object value) { if (value != null && ModelUtils.isCollection(field)) { - List list = new LinkedList<>(); - list.addAll(ModelUtils.getAsCollection(value)); - persistedValues.put(field.getName(), list); + Collection valueAsCollection = ModelUtils.getAsCollection(value); + Collection collection = CollectionFactory.createApproximateCollection(valueAsCollection, 10); + collection.addAll(valueAsCollection); + persistedValues.put(field.getName(), collection); } else { persistedValues.put(field.getName(), value); } diff --git a/core/src/main/java/net/lecousin/reactive/data/relational/model/ModelUtils.java b/core/src/main/java/net/lecousin/reactive/data/relational/model/ModelUtils.java index 53fa780..4c72649 100644 --- a/core/src/main/java/net/lecousin/reactive/data/relational/model/ModelUtils.java +++ b/core/src/main/java/net/lecousin/reactive/data/relational/model/ModelUtils.java @@ -13,6 +13,20 @@ */ package net.lecousin.reactive.data.relational.model; +import net.lecousin.reactive.data.relational.annotations.CompositeId; +import net.lecousin.reactive.data.relational.enhance.EntityState; +import net.lecousin.reactive.data.relational.model.metadata.EntityMetadata; +import net.lecousin.reactive.data.relational.model.metadata.EntityStaticMetadata; +import net.lecousin.reactive.data.relational.model.metadata.PropertyMetadata; +import net.lecousin.reactive.data.relational.model.metadata.PropertyStaticMetadata; +import org.apache.commons.lang3.ArrayUtils; +import org.springframework.core.CollectionFactory; +import org.springframework.data.mapping.MappingException; +import org.springframework.data.relational.core.mapping.RelationalPersistentEntity; +import org.springframework.data.relational.core.mapping.RelationalPersistentProperty; +import org.springframework.lang.NonNull; +import org.springframework.lang.Nullable; + import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.ParameterizedType; @@ -25,21 +39,6 @@ import java.util.List; import java.util.function.Function; -import org.apache.commons.lang3.ArrayUtils; -import org.springframework.core.CollectionFactory; -import org.springframework.data.mapping.MappingException; -import org.springframework.data.relational.core.mapping.RelationalPersistentEntity; -import org.springframework.data.relational.core.mapping.RelationalPersistentProperty; -import org.springframework.lang.NonNull; -import org.springframework.lang.Nullable; - -import net.lecousin.reactive.data.relational.annotations.CompositeId; -import net.lecousin.reactive.data.relational.enhance.EntityState; -import net.lecousin.reactive.data.relational.model.metadata.EntityMetadata; -import net.lecousin.reactive.data.relational.model.metadata.EntityStaticMetadata; -import net.lecousin.reactive.data.relational.model.metadata.PropertyMetadata; -import net.lecousin.reactive.data.relational.model.metadata.PropertyStaticMetadata; - /** * Utility methods. * @@ -120,9 +119,7 @@ public static boolean isCollection(Field field) { public static boolean isCollectionType(Class type) { if (type.isArray()) return !char[].class.equals(type); - if (Collection.class.isAssignableFrom(type)) - return true; - return false; + return Collection.class.isAssignableFrom(type); } /** Return the given object as a collection. @@ -164,11 +161,9 @@ public static Class getCollectionType(Field field) { * @return type of elements */ public static Class getRequiredCollectionType(Field field) { - if (field.getType().isArray()) - return field.getType().getComponentType(); - Type genType = field.getGenericType(); - if (genType instanceof ParameterizedType) - return (Class) ((ParameterizedType)genType).getActualTypeArguments()[0]; + Class collectionType = getCollectionType(field); + if (collectionType != null) + return collectionType; throw new MappingException("Field is not a collection: " + field.getDeclaringClass().getName() + "." + field.getName()); } diff --git a/core/src/test/java/net/lecousin/reactive/data/relational/test/AbstractLcReactiveDataRelationalTest.java b/core/src/test/java/net/lecousin/reactive/data/relational/test/AbstractLcReactiveDataRelationalTest.java index ca6b5cd..cbcc8cd 100644 --- a/core/src/test/java/net/lecousin/reactive/data/relational/test/AbstractLcReactiveDataRelationalTest.java +++ b/core/src/test/java/net/lecousin/reactive/data/relational/test/AbstractLcReactiveDataRelationalTest.java @@ -1,17 +1,13 @@ package net.lecousin.reactive.data.relational.test; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Objects; -import java.util.function.Function; - -import javax.annotation.PostConstruct; - +import net.lecousin.reactive.data.relational.LcReactiveDataRelationalClient; +import net.lecousin.reactive.data.relational.model.metadata.EntityStaticMetadata; +import net.lecousin.reactive.data.relational.query.SelectQuery; +import net.lecousin.reactive.data.relational.repository.LcR2dbcEntityTemplate; +import net.lecousin.reactive.data.relational.schema.RelationalDatabaseSchema; +import net.lecousin.reactive.data.relational.test.arraycolumns.EntityWithArrays; +import net.lecousin.reactive.data.relational.test.arraycolumns.UpdateableCollectionProperties; +import net.lecousin.reactive.data.relational.test.simplemodel.DateTypesWithTimeZone; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; @@ -25,13 +21,17 @@ import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.DirtiesContext.ClassMode; -import net.lecousin.reactive.data.relational.LcReactiveDataRelationalClient; -import net.lecousin.reactive.data.relational.model.metadata.EntityStaticMetadata; -import net.lecousin.reactive.data.relational.query.SelectQuery; -import net.lecousin.reactive.data.relational.repository.LcR2dbcEntityTemplate; -import net.lecousin.reactive.data.relational.schema.RelationalDatabaseSchema; -import net.lecousin.reactive.data.relational.test.arraycolumns.EntityWithArrays; -import net.lecousin.reactive.data.relational.test.simplemodel.DateTypesWithTimeZone; +import javax.annotation.PostConstruct; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.function.Function; @DataR2dbcTest @EnableAutoConfiguration @@ -77,9 +77,9 @@ public void initDatabase() { protected Collection> getAllCompatibleEntities() { Collection> entities = new LinkedList<>(EntityStaticMetadata.getClasses()); if (!lcClient.getSchemaDialect().isTimeZoneSupported()) - entities.remove(DateTypesWithTimeZone.class); + entities.removeAll(Set.of(DateTypesWithTimeZone.class)); if (!lcClient.getSchemaDialect().isArrayColumnSupported()) - entities.remove(EntityWithArrays.class); + entities.removeAll(Set.of(EntityWithArrays.class, UpdateableCollectionProperties.class)); return entities; } diff --git a/core/src/test/java/net/lecousin/reactive/data/relational/test/arraycolumns/AbstractTestArrayColumns.java b/core/src/test/java/net/lecousin/reactive/data/relational/test/arraycolumns/AbstractTestArrayColumns.java index ef8e310..d310bde 100644 --- a/core/src/test/java/net/lecousin/reactive/data/relational/test/arraycolumns/AbstractTestArrayColumns.java +++ b/core/src/test/java/net/lecousin/reactive/data/relational/test/arraycolumns/AbstractTestArrayColumns.java @@ -1,18 +1,18 @@ package net.lecousin.reactive.data.relational.test.arraycolumns; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; - +import net.lecousin.reactive.data.relational.query.SelectQuery; +import net.lecousin.reactive.data.relational.query.criteria.Criteria; +import net.lecousin.reactive.data.relational.repository.LcR2dbcRepositoryFactoryBean; +import net.lecousin.reactive.data.relational.test.AbstractLcReactiveDataRelationalTest; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories; -import net.lecousin.reactive.data.relational.query.SelectQuery; -import net.lecousin.reactive.data.relational.query.criteria.Criteria; -import net.lecousin.reactive.data.relational.repository.LcR2dbcRepositoryFactoryBean; -import net.lecousin.reactive.data.relational.test.AbstractLcReactiveDataRelationalTest; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Set; @EnableR2dbcRepositories(repositoryFactoryBeanClass = LcR2dbcRepositoryFactoryBean.class) public abstract class AbstractTestArrayColumns extends AbstractLcReactiveDataRelationalTest { @@ -22,7 +22,7 @@ public abstract class AbstractTestArrayColumns extends AbstractLcReactiveDataRel @Override protected Collection> usedEntities() { - return Arrays.asList(EntityWithArrays.class); + return Arrays.asList(EntityWithArrays.class, UpdateableCollectionProperties.class); } @Test @@ -33,6 +33,7 @@ public void testEmptyArrays() { entity = repo1.findAll().blockFirst(); Assertions.assertTrue(entity.getIntegers() == null || entity.getIntegers().length == 0); Assertions.assertTrue(entity.getPrimitiveIntegers() == null || entity.getPrimitiveIntegers().length == 0); + Assertions.assertTrue(entity.getIntegerList() == null || entity.getIntegerList().size() == 0); } @Test @@ -58,6 +59,7 @@ public void testArraysWithOneElement() { entity.setDoubleList(Arrays.asList(4.5d)); entity.setStrings(new String[] { "test1" }); entity.setStringList(Arrays.asList("test2")); + entity.setStringSet(Set.of("test3")); repo1.save(entity).block(); entity = repo1.findAll().blockFirst(); @@ -141,6 +143,10 @@ public void testArraysWithOneElement() { Assertions.assertNotNull(entity.getStringList()); Assertions.assertEquals(1, entity.getStringList().size()); Assertions.assertEquals("test2", entity.getStringList().get(0)); + + Assertions.assertNotNull(entity.getStringSet()); + Assertions.assertEquals(1, entity.getStringSet().size()); + Assertions.assertEquals("test3", entity.getStringSet().iterator().next()); // update an element in the array entity.getIntegers()[0] = 12345; @@ -197,4 +203,39 @@ public void testSearchEntityHavingAnArrayContaining() { .collectList().block(); Assertions.assertEquals(2, list.size()); } + + @Test + public void testArraysWithUpdateableCollectionProperties() { + UpdateableCollectionProperties entity = new UpdateableCollectionProperties(); + entity.setStrings1(List.of("1.1")); + entity.setStrings2(Set.of("2.1")); + entity.setStrings3(List.of("3.1")); + entity.setStrings4(Set.of("4.1")); + + entity = lcClient.save(entity).block(); + Assertions.assertEquals(List.of("1.1"), entity.getStrings1()); + Assertions.assertEquals(Set.of("2.1"), entity.getStrings2()); + Assertions.assertEquals(List.of("3.1"), entity.getStrings3()); + Assertions.assertEquals(Set.of("4.1"), entity.getStrings4()); + long id = entity.getId(); + + entity.setStrings1(List.of("1.2")); + entity.setStrings2(Set.of("2.2")); + entity.setStrings3(List.of("3.2")); + entity.setStrings4(Set.of("4.2")); + + entity = lcClient.save(entity).block(); + Assertions.assertEquals(id, entity.getId()); + Assertions.assertEquals(List.of("1.2"), entity.getStrings1()); + Assertions.assertEquals(Set.of("2.2"), entity.getStrings2()); + Assertions.assertEquals(List.of("3.1"), entity.getStrings3()); + Assertions.assertEquals(Set.of("4.1"), entity.getStrings4()); + + entity = SelectQuery.from(UpdateableCollectionProperties.class, "entity").execute(lcClient).blockFirst(); + Assertions.assertEquals(id, entity.getId()); + Assertions.assertEquals(List.of("1.2"), entity.getStrings1()); + Assertions.assertEquals(Set.of("2.2"), entity.getStrings2()); + Assertions.assertEquals(List.of("3.1"), entity.getStrings3()); + Assertions.assertEquals(Set.of("4.1"), entity.getStrings4()); + } } diff --git a/core/src/test/java/net/lecousin/reactive/data/relational/test/arraycolumns/EntityWithArrays.java b/core/src/test/java/net/lecousin/reactive/data/relational/test/arraycolumns/EntityWithArrays.java index 0fbd412..db09a7e 100644 --- a/core/src/test/java/net/lecousin/reactive/data/relational/test/arraycolumns/EntityWithArrays.java +++ b/core/src/test/java/net/lecousin/reactive/data/relational/test/arraycolumns/EntityWithArrays.java @@ -1,12 +1,12 @@ package net.lecousin.reactive.data.relational.test.arraycolumns; -import java.util.List; - +import net.lecousin.reactive.data.relational.annotations.GeneratedValue; import org.springframework.data.annotation.Id; import org.springframework.data.relational.core.mapping.Column; import org.springframework.data.relational.core.mapping.Table; -import net.lecousin.reactive.data.relational.annotations.GeneratedValue; +import java.util.List; +import java.util.Set; @Table public class EntityWithArrays { @@ -74,6 +74,9 @@ public class EntityWithArrays { @Column private List stringList; + @Column + private Set stringSet; + public Long getId() { return id; } @@ -241,5 +244,12 @@ public List getStringList() { public void setStringList(List stringList) { this.stringList = stringList; } - + + public Set getStringSet() { + return stringSet; + } + + public void setStringSet(Set stringSet) { + this.stringSet = stringSet; + } } diff --git a/core/src/test/java/net/lecousin/reactive/data/relational/test/arraycolumns/UpdateableCollectionProperties.java b/core/src/test/java/net/lecousin/reactive/data/relational/test/arraycolumns/UpdateableCollectionProperties.java new file mode 100644 index 0000000..9996acd --- /dev/null +++ b/core/src/test/java/net/lecousin/reactive/data/relational/test/arraycolumns/UpdateableCollectionProperties.java @@ -0,0 +1,68 @@ +package net.lecousin.reactive.data.relational.test.arraycolumns; + +import net.lecousin.reactive.data.relational.annotations.ColumnDefinition; +import net.lecousin.reactive.data.relational.annotations.GeneratedValue; +import org.springframework.data.annotation.Id; +import org.springframework.data.relational.core.mapping.Table; + +import java.util.List; +import java.util.Set; + +@Table +public class UpdateableCollectionProperties { + + @Id @GeneratedValue + private Long id; + + @ColumnDefinition(updatable = true) + private List strings1; + + @ColumnDefinition(updatable = true) + private Set strings2; + + @ColumnDefinition(updatable = false) + private List strings3; + + @ColumnDefinition(updatable = false) + private Set strings4; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public List getStrings1() { + return strings1; + } + + public void setStrings1(List strings1) { + this.strings1 = strings1; + } + + public Set getStrings2() { + return strings2; + } + + public void setStrings2(Set strings2) { + this.strings2 = strings2; + } + + public List getStrings3() { + return strings3; + } + + public void setStrings3(List strings3) { + this.strings3 = strings3; + } + + public Set getStrings4() { + return strings4; + } + + public void setStrings4(Set strings4) { + this.strings4 = strings4; + } +} diff --git a/core/src/test/resources/lc-reactive-data-relational.yaml b/core/src/test/resources/lc-reactive-data-relational.yaml index 365d94d..bcd377c 100644 --- a/core/src/test/resources/lc-reactive-data-relational.yaml +++ b/core/src/test/resources/lc-reactive-data-relational.yaml @@ -65,4 +65,5 @@ entities: - MultipleDbEntity2 - arraycolumns: - EntityWithArrays + - UpdateableCollectionProperties \ No newline at end of file