diff --git a/.gitignore b/.gitignore index 3a2e1e20..3370dfc0 100644 --- a/.gitignore +++ b/.gitignore @@ -62,11 +62,12 @@ hs_err_pid* .tern-project # EclipseStore -storage -storage-person -storage-invoice -storage-complex -storage-eclipsestore +spring-data-eclipse-store/storage +spring-data-eclipse-store-demo/storage +spring-data-eclipse-store-demo/storage-person +spring-data-eclipse-store-demo/storage-invoice +spring-data-eclipse-store-demo/storage-complex +spring-data-eclipse-store-jpa/storage-eclipsestore spring-data-eclipse-store-jpa/storage-h2.mv.db spring-data-eclipse-store-jpa/storage-h2.trace.db diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c93ca89..a1d2dc7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# 2.1.0 + +* Implemented auto-id-generation for UUIDs. +* Implemented composite primary keys. +* Keyword "ignoreCase" now available for queries. +* Implemented ``@Query`` annotation with simple SQL-Selects + # 2.0.1 * Fix for Issue [#131](https://github.com/xdev-software/spring-data-eclipse-store/issues/131) diff --git a/docs/antora.yml b/docs/antora.yml index f0e594e0..fc9ac74e 100644 --- a/docs/antora.yml +++ b/docs/antora.yml @@ -1,14 +1,14 @@ name: ROOT title: Spring-Data-Eclipse-Store version: master -display_version: '2.0.0' +display_version: '2.1.0' start_page: index.adoc nav: - modules/ROOT/nav.adoc asciidoc: attributes: product-name: 'Spring-Data-Eclipse-Store' - display-version: '2.0.0' - maven-version: '2.0.0' + display-version: '2.1.0' + maven-version: '2.1.0' page-editable: false page-out-of-support: false diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index 49a238b7..7f76b1f6 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -3,8 +3,10 @@ * xref:configuration.adoc[Configuration] * xref:working-copies.adoc[Working Copies] * xref:features/features.adoc[Features] +** xref:features/ids.adoc[IDs] ** xref:features/lazies.adoc[Lazy References] +** xref:features/queries.adoc[Queries] ** xref:features/transactions.adoc[Transactions] ** xref:features/versions.adoc[Versions] -* xref:migration.adoc[Migration] +* xref:migration.adoc[Migration from JPA] * xref:known-issues.adoc[Known issues] diff --git a/docs/modules/ROOT/pages/features/features.adoc b/docs/modules/ROOT/pages/features/features.adoc index 8f85b440..6c511a04 100644 --- a/docs/modules/ROOT/pages/features/features.adoc +++ b/docs/modules/ROOT/pages/features/features.adoc @@ -1,5 +1,7 @@ = Features +* xref:features/ids.adoc[IDs] * xref:features/lazies.adoc[Lazy References] +* xref:features/queries.adoc[Queries] * xref:features/transactions.adoc[Transactions] * xref:features/versions.adoc[Versions] diff --git a/docs/modules/ROOT/pages/features/ids.adoc b/docs/modules/ROOT/pages/features/ids.adoc new file mode 100644 index 00000000..8d9cdb35 --- /dev/null +++ b/docs/modules/ROOT/pages/features/ids.adoc @@ -0,0 +1,19 @@ += IDs + +{product-name} supports the following types with auto generating (``GenerationType.AUTO``) values: + +* ``int`` / ``Integer`` +* ``long`` / ``Long`` +* ``String`` +* ``UUID`` + +Other generation types are currently not supported. + +== Composite keys + +It is possible to use **any class as https://jakarta.ee/specifications/persistence/3.2/apidocs/jakarta.persistence/jakarta/persistence/id[``@Id``]** but without any auto generation. +Most importantly the used class **must have a valid ``hashCode``** since a ``HashMap`` is used to store and manage entities. + +{product-name} can also handle https://jakarta.ee/specifications/persistence/3.2/apidocs/jakarta.persistence/jakarta/persistence/embeddedid[``@EmbeddedId``] which results in the same behavior as ``@Id`` but the id-class must then implement ``Serializable``. + +Multiple Ids for a single entity and https://jakarta.ee/specifications/persistence/3.2/apidocs/jakarta.persistence/jakarta/persistence/idclass[``@IdClass``] are **not** supported. diff --git a/docs/modules/ROOT/pages/features/queries.adoc b/docs/modules/ROOT/pages/features/queries.adoc new file mode 100644 index 00000000..e956f710 --- /dev/null +++ b/docs/modules/ROOT/pages/features/queries.adoc @@ -0,0 +1,73 @@ += Queries + +== Keywords + +It is possible to use **most of the standard query keywords** for repositories defined in Spring Data JPA: https://docs.spring.io/spring-data/jpa/reference/repositories/query-keywords-reference.html[Spring Data JPA - Repository query keywords]. + +Here are a few examples: + +[source,java] +---- +@Repository +public interface UserRepository extends EclipseStoreRepository +{ + List findByFirstName(String firstName, String lastName); + List findByFirstNameAndLastName(String firstName, String lastName); + List findByDateOfBirthBefore(LocalDate date); + List findByAgeIn(List ages); + List findByIsActiveFalse(); +} +---- + +More examples are in the https://github.com/xdev-software/spring-data-eclipse-store/blob/develop/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/by/string/UserRepository.java[test-cases]. + +== Query by Example + +Developers can also use https://docs.spring.io/spring-data/jpa/reference/repositories/query-by-example.html[Query by Example] if preferred. + +An example: + +[source,java] +---- +public List findAllUsersNamedMick() +{ + final User probe = new User(1, "Mick", BigDecimal.TEN); + return userRepository.findAll(Example.of(probe)); +} +---- + +More examples are in the https://github.com/xdev-software/spring-data-eclipse-store/blob/develop/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/by/example/QueryByExampleTest.java[test-cases]. + +== @Query annotation + +The support for a ``@Query``-Annotation is currently quite limited, but useful nonetheless. + +To keep parse and execute SQL-Queries we use the https://github.com/npgall/cqengine[cqengine] by https://github.com/npgall[Niall Gallagher]. +It offers rudimentary support of some SQL-Queries, but not all. + +[NOTE] +==== +https://github.com/npgall/cqengine[cqengine] parses the SQL String as a SQLite-SQL-String and is therefore different from the https://docs.spring.io/spring-data/jpa/reference/jpa/query-methods.html#jpa.query-methods.at-query[HQL or JPQL] of Spring Data JPA. +==== + +Here are some working examples: + +[source,java] +---- +public interface MyEntityRepository extends ListCrudRepository +{ + @Query("SELECT * FROM MyEntity WHERE name = '?1'") + List findByName(String name); + + @Query("SELECT * FROM MyEntity WHERE (name = '?1' AND age > ?2)") + List findByNameAndAgeGreaterThan(String name, int age); + + @Query("SELECT * FROM MyEntity WHERE 'name' LIKE '%?1%'") + List findByNameContaining(String keyword); + + @Query("SELECT * FROM MyEntity WHERE otherEntity IS NOT NULL") + List findWhereOtherEntityIsNotNull(); +} +---- + +More examples are in the https://github.com/xdev-software/spring-data-eclipse-store/blob/develop/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/hsql/MyEntityRepository.java[test-cases]. diff --git a/docs/modules/ROOT/pages/known-issues.adoc b/docs/modules/ROOT/pages/known-issues.adoc index 8cbdc247..f6ad7ee1 100644 --- a/docs/modules/ROOT/pages/known-issues.adoc +++ b/docs/modules/ROOT/pages/known-issues.adoc @@ -1,17 +1,5 @@ = Known issues -== Query annotations - -In Spring-Data-JPA you can write a Query over methods of repositories like this: - -[source,java,title="From https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/owner/OwnerRepository.java[spring-petclinic]"] ----- -@Query("SELECT ptype FROM PetType ptype ORDER BY ptype.name") -List findPetTypes(); ----- - -We created https://github.com/xdev-software/spring-data-eclipse-store/issues/32[an issue] for that but right now we *do not support Query annotations*. - == Data changes There are two basic ways to keep your data up to date. diff --git a/docs/modules/ROOT/pages/migration.adoc b/docs/modules/ROOT/pages/migration.adoc index 24148348..833cf77e 100644 --- a/docs/modules/ROOT/pages/migration.adoc +++ b/docs/modules/ROOT/pages/migration.adoc @@ -1,4 +1,4 @@ -= Migration += Migration from JPA Migrating from Spring Data JPA is very easy. We implemented a https://github.com/xdev-software/spring-data-eclipse-store-migration[OpenRewrite recipe] for that. diff --git a/pom.xml b/pom.xml index d2133d1e..5add980c 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ software.xdev spring-data-eclipse-store-root - 2.0.2-SNAPSHOT + 2.2.0-SNAPSHOT pom diff --git a/spring-data-eclipse-store-benchmark/pom.xml b/spring-data-eclipse-store-benchmark/pom.xml index 37281087..722b57f9 100644 --- a/spring-data-eclipse-store-benchmark/pom.xml +++ b/spring-data-eclipse-store-benchmark/pom.xml @@ -5,11 +5,11 @@ software.xdev spring-data-eclipse-store-root - 2.0.2-SNAPSHOT + 2.2.0-SNAPSHOT spring-data-eclipse-store-benchmark - 2.0.2-SNAPSHOT + 2.1.0-SNAPSHOT jar 2023 diff --git a/spring-data-eclipse-store-demo/pom.xml b/spring-data-eclipse-store-demo/pom.xml index b05209a0..9249afbc 100644 --- a/spring-data-eclipse-store-demo/pom.xml +++ b/spring-data-eclipse-store-demo/pom.xml @@ -7,11 +7,11 @@ software.xdev spring-data-eclipse-store-root - 2.0.2-SNAPSHOT + 2.2.0-SNAPSHOT spring-data-eclipse-store-demo - 2.0.2-SNAPSHOT + 2.1.0-SNAPSHOT jar diff --git a/spring-data-eclipse-store-demo/src/test/java/software/xdev/spring/data/eclipse/store/demo/dual/storage/DualStorageDemoApplicationTest.java b/spring-data-eclipse-store-demo/src/test/java/software/xdev/spring/data/eclipse/store/demo/dual/storage/DualStorageDemoApplicationTest.java new file mode 100644 index 00000000..d70af695 --- /dev/null +++ b/spring-data-eclipse-store-demo/src/test/java/software/xdev/spring/data/eclipse/store/demo/dual/storage/DualStorageDemoApplicationTest.java @@ -0,0 +1,44 @@ +package software.xdev.spring.data.eclipse.store.demo.dual.storage; + +import java.io.File; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import software.xdev.spring.data.eclipse.store.demo.TestUtil; +import software.xdev.spring.data.eclipse.store.demo.dual.storage.invoice.PersistenceInvoiceConfiguration; +import software.xdev.spring.data.eclipse.store.demo.dual.storage.person.PersistencePersonConfiguration; + + +@SpringBootTest(classes = DualStorageDemoApplication.class) +class DualStorageDemoApplicationTest +{ + private final PersistenceInvoiceConfiguration invoiceConfiguration; + private final PersistencePersonConfiguration personConfiguration; + + @Autowired + public DualStorageDemoApplicationTest( + final PersistenceInvoiceConfiguration invoiceConfiguration, + final PersistencePersonConfiguration personConfiguration) + { + this.invoiceConfiguration = invoiceConfiguration; + this.personConfiguration = personConfiguration; + } + + @BeforeAll + static void clearPreviousData() + { + TestUtil.deleteDirectory(new File("./" + PersistenceInvoiceConfiguration.STORAGE_PATH)); + TestUtil.deleteDirectory(new File("./" + PersistencePersonConfiguration.STORAGE_PATH)); + } + + @Test + void checkPossibilityToSimplyStartAndRestartApplication() + { + this.invoiceConfiguration.getStorageInstance().stop(); + this.personConfiguration.getStorageInstance().stop(); + DualStorageDemoApplication.main(new String[]{}); + } +} diff --git a/spring-data-eclipse-store-jpa/pom.xml b/spring-data-eclipse-store-jpa/pom.xml index 7dc07535..79006829 100644 --- a/spring-data-eclipse-store-jpa/pom.xml +++ b/spring-data-eclipse-store-jpa/pom.xml @@ -7,11 +7,11 @@ software.xdev spring-data-eclipse-store-root - 2.0.2-SNAPSHOT + 2.2.0-SNAPSHOT spring-data-eclipse-store-jpa - 2.0.2-SNAPSHOT + 2.1.0-SNAPSHOT jar 2023 diff --git a/spring-data-eclipse-store/pom.xml b/spring-data-eclipse-store/pom.xml index cf40fb0e..26676304 100644 --- a/spring-data-eclipse-store/pom.xml +++ b/spring-data-eclipse-store/pom.xml @@ -6,7 +6,7 @@ software.xdev spring-data-eclipse-store - 2.0.2-SNAPSHOT + 2.1.0-SNAPSHOT jar spring-data-eclipse-store @@ -47,6 +47,7 @@ ${javaVersion} UTF-8 + UTF-8 @@ -163,6 +164,26 @@ + + + com.googlecode.cqengine + cqengine + 3.6.0 + + + kryo + com.esotericsoftware + + + kryo-serializers + de.javakaffee + + + sqlite-jdbc + org.xerial + + + org.junit.jupiter diff --git a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/exceptions/IdFieldFinalException.java b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/exceptions/IdFieldException.java similarity index 86% rename from spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/exceptions/IdFieldFinalException.java rename to spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/exceptions/IdFieldException.java index 144e2c55..4e4fd186 100644 --- a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/exceptions/IdFieldFinalException.java +++ b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/exceptions/IdFieldException.java @@ -15,9 +15,9 @@ */ package software.xdev.spring.data.eclipse.store.exceptions; -public class IdFieldFinalException extends RuntimeException +public class IdFieldException extends RuntimeException { - public IdFieldFinalException(final String message) + public IdFieldException(final String message) { super(message); } diff --git a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/Query.java b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/Query.java index 9099f492..d1e18c7b 100644 --- a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/Query.java +++ b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/Query.java @@ -40,5 +40,5 @@ @QueryAnnotation public @interface Query { - @SuppressWarnings("unused") String value() default ""; + String value() default ""; } diff --git a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/query/EclipseStoreQueryCreator.java b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/query/EclipseStoreQueryCreator.java index 8f1ade91..1a40b12c 100644 --- a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/query/EclipseStoreQueryCreator.java +++ b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/query/EclipseStoreQueryCreator.java @@ -126,6 +126,9 @@ private AbstractCriteriaNode from( { Objects.requireNonNull(criteria); final Part.Type type = Objects.requireNonNull(part).getType(); + final Part.IgnoreCaseType ignoreCaseType = part.shouldIgnoreCase(); + final boolean doIgnoreCase = + ignoreCaseType == Part.IgnoreCaseType.ALWAYS || ignoreCaseType == Part.IgnoreCaseType.WHEN_POSSIBLE; switch(type) { @@ -169,27 +172,27 @@ private AbstractCriteriaNode from( } case LIKE -> { - return criteria.like((String)Objects.requireNonNull(parameters).next()); + return criteria.like((String)Objects.requireNonNull(parameters).next(), doIgnoreCase); } case STARTING_WITH -> { - return criteria.startWith((String)Objects.requireNonNull(parameters).next()); + return criteria.startWith((String)Objects.requireNonNull(parameters).next(), doIgnoreCase); } case ENDING_WITH -> { - return criteria.endWith((String)Objects.requireNonNull(parameters).next()); + return criteria.endWith((String)Objects.requireNonNull(parameters).next(), doIgnoreCase); } case CONTAINING -> { - return criteria.containing((String)Objects.requireNonNull(parameters).next()); + return criteria.containing((String)Objects.requireNonNull(parameters).next(), doIgnoreCase); } case NOT_LIKE -> { - return criteria.notLike((String)Objects.requireNonNull(parameters).next()); + return criteria.notLike((String)Objects.requireNonNull(parameters).next(), doIgnoreCase); } case NOT_CONTAINING -> { - return criteria.notContaining((String)Objects.requireNonNull(parameters).next()); + return criteria.notContaining((String)Objects.requireNonNull(parameters).next(), doIgnoreCase); } case EXISTS -> { diff --git a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/query/HSqlQueryProvider.java b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/query/HSqlQueryProvider.java new file mode 100644 index 00000000..32661493 --- /dev/null +++ b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/query/HSqlQueryProvider.java @@ -0,0 +1,62 @@ +/* + * Copyright © 2024 XDEV Software (https://xdev.software) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.xdev.spring.data.eclipse.store.repository.query; + +import java.util.Objects; + +import org.springframework.data.repository.query.QueryMethod; +import org.springframework.data.repository.query.RepositoryQuery; + +import software.xdev.spring.data.eclipse.store.core.EntityListProvider; +import software.xdev.spring.data.eclipse.store.repository.query.antlr.HSqlQueryExecutor; +import software.xdev.spring.data.eclipse.store.repository.support.copier.working.WorkingCopier; + + +public class HSqlQueryProvider implements RepositoryQuery +{ + private final HSqlQueryExecutor executor; + private final String sqlValue; + private final QueryMethod queryMethod; + + public HSqlQueryProvider( + final String sqlValue, + final QueryMethod queryMethod, + final Class domainClass, + final EntityListProvider entityListProvider, + final WorkingCopier copier + ) + { + this.queryMethod = queryMethod; + this.executor = new HSqlQueryExecutor<>( + Objects.requireNonNull(domainClass), + Objects.requireNonNull(entityListProvider), + copier + ); + this.sqlValue = sqlValue; + } + + @Override + public Object execute(final Object[] parameters) + { + return this.executor.execute(this.sqlValue, parameters); + } + + @Override + public QueryMethod getQueryMethod() + { + return this.queryMethod; + } +} diff --git a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/query/antlr/HSqlQueryExecutor.java b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/query/antlr/HSqlQueryExecutor.java new file mode 100644 index 00000000..cff88bb1 --- /dev/null +++ b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/query/antlr/HSqlQueryExecutor.java @@ -0,0 +1,105 @@ +/* + * Copyright © 2024 XDEV Software (https://xdev.software) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.xdev.spring.data.eclipse.store.repository.query.antlr; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import java.util.stream.Collectors; + +import com.googlecode.cqengine.ConcurrentIndexedCollection; +import com.googlecode.cqengine.IndexedCollection; +import com.googlecode.cqengine.attribute.Attribute; +import com.googlecode.cqengine.attribute.ReflectiveAttribute; +import com.googlecode.cqengine.query.parser.sql.SQLParser; +import com.googlecode.cqengine.resultset.ResultSet; + +import software.xdev.spring.data.eclipse.store.core.EntityListProvider; +import software.xdev.spring.data.eclipse.store.repository.access.AccessHelper; +import software.xdev.spring.data.eclipse.store.repository.support.copier.working.WorkingCopier; + + +public class HSqlQueryExecutor +{ + private final SQLParser parser; + private final EntityListProvider entityListProvider; + private final Class domainClass; + private final WorkingCopier copier; + + public HSqlQueryExecutor( + final Class domainClass, + final EntityListProvider entityListProvider, + final WorkingCopier copier) + { + this.domainClass = domainClass; + this.parser = SQLParser.forPojoWithAttributes(domainClass, this.createAttributes(domainClass)); + this.entityListProvider = entityListProvider; + this.copier = copier; + } + + public List execute(final String sqlValue, final Object[] parameters) + { + final IndexedCollection entities = new ConcurrentIndexedCollection<>(); + entities.addAll(this.entityListProvider.getEntityProvider(this.domainClass).toCollection()); + final String sqlStringWithReplacedValues = this.replacePlaceholders(sqlValue, parameters); + final ResultSet retrieve = this.parser.retrieve(entities, sqlStringWithReplacedValues); + final List results = retrieve.stream().toList(); + return this.copier.copy(results); + } + + private String replacePlaceholders(final String sqlValue, final Object[] parameters) + { + String stringWithReplacedValues = sqlValue; + // Replace positional placeholders with actual parameter values + for(int i = 0; i < parameters.length; i++) + { + final String placeholder = "\\?" + (i + 1); + String value = parameters[i].toString(); + if(parameters[i] instanceof final Collection collection) + { + value = + collection.stream() + .map(o -> "'" + o.toString() + "'") + .collect(Collectors.joining(", ", "(", ")")); + } + if(parameters[i] instanceof final LocalDate localDate) + { + value = localDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")); + } + stringWithReplacedValues = stringWithReplacedValues.replaceAll(placeholder, value); + } + return stringWithReplacedValues; + } + + private Map> createAttributes(final Class domainClass) + { + final Map> attributes = new TreeMap<>(); + AccessHelper.getInheritedPrivateFieldsByName(domainClass).forEach( + (fieldName, field) -> attributes.put( + fieldName, + new ReflectiveAttribute<>( + domainClass, + field.getType(), + fieldName + ) + ) + ); + return attributes; + } +} diff --git a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/query/criteria/AbstractCriteriaNode.java b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/query/criteria/AbstractCriteriaNode.java index fc62538a..56de748d 100644 --- a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/query/criteria/AbstractCriteriaNode.java +++ b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/query/criteria/AbstractCriteriaNode.java @@ -147,51 +147,59 @@ public AbstractCriteriaNode exists(final boolean value) return this; } - public AbstractCriteriaNode like(final String like) + public AbstractCriteriaNode like(final String like, final boolean doIgnoreCase) { - final String completeRegex = sqlLikeStringToRegex(like); + final String completeRegex = sqlLikeStringToRegex(like, doIgnoreCase); this.predicates.add(entity -> { final String fieldValue = (String)Objects.requireNonNull(this.field).readValue(entity); - return fieldValue != null && fieldValue.toUpperCase().matches(completeRegex); + if(fieldValue == null) + { + return false; + } + return (doIgnoreCase ? fieldValue.toUpperCase() : fieldValue).matches(completeRegex); }); return this; } - private static String sqlLikeStringToRegex(final String like) + private static String sqlLikeStringToRegex(final String like, final boolean doIgnoreCase) { - String regex = like.toUpperCase(); + String regex = doIgnoreCase ? like.toUpperCase() : like; regex = regex.replace(".", "\\."); regex = regex.replace("_", "."); return regex.replace("%", ".*"); } - public AbstractCriteriaNode startWith(final String startString) + public AbstractCriteriaNode startWith(final String startString, final boolean doIgnoreCase) { - return this.like(startString + "%"); + return this.like(startString + "%", doIgnoreCase); } - public AbstractCriteriaNode endWith(final String endString) + public AbstractCriteriaNode endWith(final String endString, final boolean doIgnoreCase) { - return this.like("%" + endString); + return this.like("%" + endString, doIgnoreCase); } - public AbstractCriteriaNode containing(final String containedString) + public AbstractCriteriaNode containing(final String containedString, final boolean doIgnoreCase) { - return this.like("%" + containedString + "%"); + return this.like("%" + containedString + "%", doIgnoreCase); } - public AbstractCriteriaNode notLike(final String notLikeString) + public AbstractCriteriaNode notLike(final String notLikeString, final boolean doIgnoreCase) { - final String completeRegex = sqlLikeStringToRegex(notLikeString); + final String completeRegex = sqlLikeStringToRegex(notLikeString, doIgnoreCase); this.predicates.add(entity -> { final String fieldValue = (String)Objects.requireNonNull(this.field).readValue(entity); - return fieldValue != null && !fieldValue.toUpperCase().matches(completeRegex); + if(fieldValue == null) + { + return false; + } + return !(doIgnoreCase ? fieldValue.toUpperCase() : fieldValue).matches(completeRegex); }); return this; } - public AbstractCriteriaNode notContaining(final String containedString) + public AbstractCriteriaNode notContaining(final String containedString, final boolean doIgnoreCase) { - return this.notLike("%" + containedString + "%"); + return this.notLike("%" + containedString + "%", doIgnoreCase); } } diff --git a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/AnnotatedFieldFinder.java b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/AnnotatedFieldFinder.java index 3230cbbe..03826ee3 100644 --- a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/AnnotatedFieldFinder.java +++ b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/AnnotatedFieldFinder.java @@ -17,10 +17,17 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Field; +import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Optional; +import jakarta.persistence.EmbeddedId; +import jakarta.persistence.Id; +import jakarta.persistence.Version; + +import software.xdev.spring.data.eclipse.store.exceptions.IdFieldException; +import software.xdev.spring.data.eclipse.store.exceptions.InvalidVersionException; import software.xdev.spring.data.eclipse.store.repository.access.AccessHelper; @@ -31,17 +38,34 @@ private AnnotatedFieldFinder() } /** - * Finds any field in a class with an ID-Annotation ({@link jakarta.persistence.Id} or - * {@link org.springframework.data.annotation.Id}). Finds this field recursively in the Hierarchy-tree. + * Finds any field in a class with an ID-Annotation ({@link jakarta.persistence.Id}, + * {@link org.springframework.data.annotation.Id} or {@link jakarta.persistence.EmbeddedId}). Finds this field + * recursively in the Hierarchy-tree. * * @return field with ID-Annotation. Is {@link Optional#empty()} if no field was found. */ public static Optional findIdField(final Class domainClass) { - return findAnnotatedField( + final List idFields = findAnnotatedFields( domainClass, - List.of(jakarta.persistence.Id.class, org.springframework.data.annotation.Id.class) + List.of( + Id.class, + org.springframework.data.annotation.Id.class, + EmbeddedId.class) ); + + if(idFields.isEmpty()) + { + return Optional.empty(); + } + else + { + if(idFields.size() > 1) + { + throw new IdFieldException("Only one id field is allowed"); + } + return Optional.of(idFields.get(0)); + } } /** @@ -52,21 +76,35 @@ public static Optional findIdField(final Class domainClass) */ public static Optional findVersionField(final Class domainClass) { - return findAnnotatedField( + final List versionFields = findAnnotatedFields( domainClass, - List.of(jakarta.persistence.Version.class, org.springframework.data.annotation.Version.class) + List.of(Version.class, org.springframework.data.annotation.Version.class) ); + + if(versionFields.isEmpty()) + { + return Optional.empty(); + } + else + { + if(versionFields.size() > 1) + { + throw new InvalidVersionException("Only one version field is allowed"); + } + return Optional.of(versionFields.get(0)); + } } /** * Finds any field in a class with specified annotations. Finds this field recursively in the Hierarchy-tree. * - * @return field with annotation. Is {@link Optional#empty()} if no field was found. + * @return fields with annotation. */ - public static Optional findAnnotatedField( + public static List findAnnotatedFields( final Class domainClass, final Collection> annotations) { + final ArrayList foundFields = new ArrayList<>(); final Collection classFields = AccessHelper.getInheritedPrivateFieldsByName(domainClass).values(); for(final Field currentField : classFields) { @@ -74,10 +112,10 @@ public static Optional findAnnotatedField( { if(currentField.getAnnotationsByType(annotation).length > 0) { - return Optional.of(currentField); + foundFields.add(currentField); } } } - return Optional.empty(); + return foundFields; } } diff --git a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/EclipseStoreQueryLookupStrategy.java b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/EclipseStoreQueryLookupStrategy.java index 06ca3f2b..2f132717 100644 --- a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/EclipseStoreQueryLookupStrategy.java +++ b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/EclipseStoreQueryLookupStrategy.java @@ -17,8 +17,8 @@ import java.lang.reflect.Method; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import jakarta.annotation.Nonnull; + import org.springframework.data.projection.ProjectionFactory; import org.springframework.data.repository.core.NamedQueries; import org.springframework.data.repository.core.RepositoryMetadata; @@ -26,17 +26,16 @@ import org.springframework.data.repository.query.QueryMethod; import org.springframework.data.repository.query.RepositoryQuery; -import jakarta.annotation.Nonnull; import software.xdev.spring.data.eclipse.store.repository.EclipseStoreStorage; import software.xdev.spring.data.eclipse.store.repository.Query; import software.xdev.spring.data.eclipse.store.repository.query.FindAllEclipseStoreQueryProvider; +import software.xdev.spring.data.eclipse.store.repository.query.HSqlQueryProvider; import software.xdev.spring.data.eclipse.store.repository.query.StringBasedEclipseStoreQueryProvider; import software.xdev.spring.data.eclipse.store.repository.support.copier.working.WorkingCopierCreator; public class EclipseStoreQueryLookupStrategy implements QueryLookupStrategy { - private static final Logger LOG = LoggerFactory.getLogger(EclipseStoreQueryLookupStrategy.class); private final EclipseStoreStorage storage; private final WorkingCopierCreator workingCopierCreator; @@ -58,11 +57,9 @@ public RepositoryQuery resolveQuery( { final QueryMethod queryMethod = new QueryMethod(method, metadata, factory); - if(method.getAnnotation(Query.class) != null) + final Query queryAnnotation = method.getAnnotation(Query.class); + if(queryAnnotation != null) { - LOG.warn( - "Annotation @Query is used in Repository {}. This is useless for now and should be deleted.", - metadata.getRepositoryInterface().getSimpleName()); if(method.getName().equalsIgnoreCase("findall")) { // Special case for Queries that have findAll and are annotated with Query @@ -72,6 +69,12 @@ public RepositoryQuery resolveQuery( method ); } + + return this.createHSqlQueryProvider( + queryAnnotation.value(), + metadata.getDomainType(), + queryMethod + ); } return this.createStringBasedEclipseStoreQueryProvider( @@ -110,4 +113,19 @@ private RepositoryQuery createStringBasedEclipseStoreQueryProvider( this.workingCopierCreator.createWorkingCopier(domainType, this.storage) ); } + + private RepositoryQuery createHSqlQueryProvider( + final String sqlString, + final Class domainType, + final QueryMethod queryMethod + ) + { + return new HSqlQueryProvider<>( + sqlString, + queryMethod, + domainType, + this.storage, + this.workingCopierCreator.createWorkingCopier(domainType, this.storage) + ); + } } diff --git a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/copier/version/SimpleEntityVersionIncrementer.java b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/copier/version/SimpleEntityVersionIncrementer.java index cb873e85..046d4001 100644 --- a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/copier/version/SimpleEntityVersionIncrementer.java +++ b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/copier/version/SimpleEntityVersionIncrementer.java @@ -19,7 +19,7 @@ import java.lang.reflect.Modifier; import software.xdev.spring.data.eclipse.store.exceptions.FieldAccessReflectionException; -import software.xdev.spring.data.eclipse.store.exceptions.IdFieldFinalException; +import software.xdev.spring.data.eclipse.store.exceptions.IdFieldException; import software.xdev.spring.data.eclipse.store.repository.access.modifier.FieldAccessModifier; import software.xdev.spring.data.eclipse.store.repository.support.copier.version.incrementer.VersionIncrementer; @@ -43,7 +43,7 @@ private void checkIfVersionFieldIsFinal() final int fieldModifiers = this.versionField.getModifiers(); if(Modifier.isFinal(fieldModifiers)) { - throw new IdFieldFinalException(String.format( + throw new IdFieldException(String.format( "Field %s is final and cannot be modified. Version fields must not be final.", this.versionField.getName())); } diff --git a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/id/SimpleIdSetter.java b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/id/SimpleIdSetter.java index 11c97285..08cfb984 100644 --- a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/id/SimpleIdSetter.java +++ b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/id/SimpleIdSetter.java @@ -20,7 +20,7 @@ import java.util.function.Consumer; import software.xdev.spring.data.eclipse.store.exceptions.FieldAccessReflectionException; -import software.xdev.spring.data.eclipse.store.exceptions.IdFieldFinalException; +import software.xdev.spring.data.eclipse.store.exceptions.IdFieldException; import software.xdev.spring.data.eclipse.store.repository.access.modifier.FieldAccessModifier; import software.xdev.spring.data.eclipse.store.repository.support.id.strategy.IdFinder; @@ -45,7 +45,7 @@ private void checkIfIdFieldIsFinal() final int fieldModifiers = this.idField.getModifiers(); if(Modifier.isFinal(fieldModifiers)) { - throw new IdFieldFinalException(String.format( + throw new IdFieldException(String.format( "Field %s is final and cannot be modified. ID fields must not be final.", this.idField.getName())); } diff --git a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/id/strategy/IdFinder.java b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/id/strategy/IdFinder.java index d9119b3f..488eeef8 100644 --- a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/id/strategy/IdFinder.java +++ b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/id/strategy/IdFinder.java @@ -17,6 +17,7 @@ import java.lang.reflect.Field; import java.util.Objects; +import java.util.UUID; import java.util.function.Supplier; import jakarta.persistence.GeneratedValue; @@ -26,6 +27,7 @@ import software.xdev.spring.data.eclipse.store.repository.support.id.strategy.auto.AutoIntegerIdFinder; import software.xdev.spring.data.eclipse.store.repository.support.id.strategy.auto.AutoLongIdFinder; import software.xdev.spring.data.eclipse.store.repository.support.id.strategy.auto.AutoStringIdFinder; +import software.xdev.spring.data.eclipse.store.repository.support.id.strategy.auto.AutoUUIDIdFinder; /** @@ -56,6 +58,10 @@ else if(Long.class.isAssignableFrom(idField.getType()) || long.class.isAssignabl { return (IdFinder)new AutoLongIdFinder(lastIdGetter); } + else if(idField.getType().equals(UUID.class)) + { + return (IdFinder)new AutoUUIDIdFinder(lastIdGetter); + } } throw new IdGeneratorNotSupportedException(String.format( "Id generator with strategy %s for type %s is not supported.", diff --git a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/id/strategy/auto/AutoUUIDIdFinder.java b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/id/strategy/auto/AutoUUIDIdFinder.java new file mode 100644 index 00000000..e8240c6c --- /dev/null +++ b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/id/strategy/auto/AutoUUIDIdFinder.java @@ -0,0 +1,40 @@ +/* + * Copyright © 2024 XDEV Software (https://xdev.software) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.xdev.spring.data.eclipse.store.repository.support.id.strategy.auto; + +import java.util.UUID; +import java.util.function.Supplier; + + +public class AutoUUIDIdFinder extends AbstractAutoIdFinder +{ + public AutoUUIDIdFinder(final Supplier idGetter) + { + super(() -> (UUID)idGetter.get()); + } + + @Override + protected UUID getNext(final UUID oldId) + { + return UUID.randomUUID(); + } + + @Override + public UUID getDefaultValue() + { + return null; + } +} diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/IdTest.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/IdTest.java index ca31f1e0..fd216e61 100644 --- a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/IdTest.java +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/IdTest.java @@ -19,6 +19,7 @@ import java.util.List; import java.util.Optional; +import java.util.UUID; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -40,6 +41,8 @@ import software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.model.CustomerWithIdLongRepository; import software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.model.CustomerWithIdString; import software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.model.CustomerWithIdStringRepository; +import software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.model.CustomerWithIdUuid; +import software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.model.CustomerWithIdUuidRepository; import software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.model.CustomerWithPurchase; import software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.model.CustomerWithPurchaseRepository; import software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.model.Purchase; @@ -60,7 +63,7 @@ public IdTest(final IdTestConfiguration configuration) } @Test - void testCreateSingleWithAutoIdInteger(@Autowired final CustomerWithIdIntegerRepository customerRepository) + void createSingleWithAutoIdInteger(@Autowired final CustomerWithIdIntegerRepository customerRepository) { final CustomerWithIdInteger customer1 = new CustomerWithIdInteger(TestData.FIRST_NAME, TestData.LAST_NAME); customerRepository.save(customer1); @@ -138,7 +141,7 @@ void saveBulkWithAutoIdIntAndHardcodedId(@Autowired final CustomerWithIdIntRepos * no previous method is called before the test. */ @Test - void testSaveSingleWithoutAnyPreviousCall(@Autowired final CustomerWithIdIntegerRepository customerRepository) + void saveSingleWithoutAnyPreviousCall(@Autowired final CustomerWithIdIntegerRepository customerRepository) { restartDatastore(this.configuration); final CustomerWithIdInteger customer1 = new CustomerWithIdInteger(TestData.FIRST_NAME, TestData.LAST_NAME); @@ -155,7 +158,7 @@ void testSaveSingleWithoutAnyPreviousCall(@Autowired final CustomerWithIdInteger } @Test - void testCreateSingleWithAutoIdIntegerWorkingCopyIdSet( + void createSingleWithAutoIdIntegerWorkingCopyIdSet( @Autowired final CustomerWithIdIntegerRepository customerRepository ) { @@ -166,7 +169,7 @@ void testCreateSingleWithAutoIdIntegerWorkingCopyIdSet( } @Test - void testCreateMultipleWithAutoIdInteger(@Autowired final CustomerWithIdIntegerRepository customerRepository) + void createMultipleWithAutoIdInteger(@Autowired final CustomerWithIdIntegerRepository customerRepository) { final CustomerWithIdInteger customer1 = new CustomerWithIdInteger(TestData.FIRST_NAME, TestData.LAST_NAME); customerRepository.save(customer1); @@ -186,7 +189,7 @@ void testCreateMultipleWithAutoIdInteger(@Autowired final CustomerWithIdIntegerR } @Test - void testCreateMultipleWithAutoIdIntegerSingleFinds( + void createMultipleWithAutoIdIntegerSingleFinds( @Autowired final CustomerWithIdIntegerRepository customerRepository ) { @@ -208,7 +211,31 @@ void testCreateMultipleWithAutoIdIntegerSingleFinds( } @Test - void testCreateSingleWithAutoIdInt(@Autowired final CustomerWithIdIntRepository customerRepository) + void createMultipleWithAutoIdUuidSingleFinds( + @Autowired final CustomerWithIdUuidRepository customerRepository + ) + { + final CustomerWithIdUuid customer1 = new CustomerWithIdUuid(TestData.FIRST_NAME, TestData.LAST_NAME); + customerRepository.save(customer1); + final CustomerWithIdUuid customer2 = + new CustomerWithIdUuid(TestData.FIRST_NAME_ALTERNATIVE, TestData.LAST_NAME_ALTERNATIVE); + customerRepository.save(customer2); + + final UUID generatedId1 = customerRepository.findAll().get(0).getId(); + final UUID generatedId2 = customerRepository.findAll().get(1).getId(); + + TestUtil.doBeforeAndAfterRestartOfDatastore( + this.configuration, + () -> { + final Optional loadedCustomer1 = customerRepository.findById(generatedId1); + final Optional loadedCustomer2 = customerRepository.findById(generatedId2); + Assertions.assertNotEquals(loadedCustomer2, loadedCustomer1); + } + ); + } + + @Test + void createSingleWithAutoIdInt(@Autowired final CustomerWithIdIntRepository customerRepository) { final CustomerWithIdInt customer1 = new CustomerWithIdInt(TestData.FIRST_NAME, TestData.LAST_NAME); customerRepository.save(customer1); @@ -224,7 +251,7 @@ void testCreateSingleWithAutoIdInt(@Autowired final CustomerWithIdIntRepository } @Test - void testCreateSingleWithAutoIdString(@Autowired final CustomerWithIdStringRepository customerRepository) + void createSingleWithAutoIdString(@Autowired final CustomerWithIdStringRepository customerRepository) { final CustomerWithIdString customer1 = new CustomerWithIdString(TestData.FIRST_NAME, TestData.LAST_NAME); customerRepository.save(customer1); @@ -240,7 +267,25 @@ void testCreateSingleWithAutoIdString(@Autowired final CustomerWithIdStringRepos } @Test - void testSaveAfterRestartSingleWithAutoIdString(@Autowired final CustomerWithIdStringRepository customerRepository) + void createSingleWithAutoIdUuid(@Autowired final CustomerWithIdUuidRepository customerRepository) + { + final CustomerWithIdUuid customer1 = new CustomerWithIdUuid(TestData.FIRST_NAME, TestData.LAST_NAME); + customerRepository.save(customer1); + + final UUID generatedId = customerRepository.findAll().get(0).getId(); + + TestUtil.doBeforeAndAfterRestartOfDatastore( + this.configuration, + () -> { + final Optional loadedCustomer = customerRepository.findById(generatedId); + Assertions.assertTrue(loadedCustomer.isPresent()); + Assertions.assertEquals(customer1, loadedCustomer.get()); + } + ); + } + + @Test + void saveAfterRestartSingleWithAutoIdString(@Autowired final CustomerWithIdStringRepository customerRepository) { final CustomerWithIdString customer1 = new CustomerWithIdString(TestData.FIRST_NAME, TestData.LAST_NAME); customerRepository.save(customer1); @@ -263,7 +308,7 @@ void testSaveAfterRestartSingleWithAutoIdString(@Autowired final CustomerWithIdS } @Test - void testCreateMultipleWithAutoIdString(@Autowired final CustomerWithIdStringRepository customerRepository) + void createMultipleWithAutoIdString(@Autowired final CustomerWithIdStringRepository customerRepository) { final CustomerWithIdString customer1 = new CustomerWithIdString(TestData.FIRST_NAME, TestData.LAST_NAME); customerRepository.save(customer1); @@ -283,7 +328,7 @@ void testCreateMultipleWithAutoIdString(@Autowired final CustomerWithIdStringRep } @Test - void testCreateSingleWithAutoIdLong(@Autowired final CustomerWithIdLongRepository customerRepository) + void createSingleWithAutoIdLong(@Autowired final CustomerWithIdLongRepository customerRepository) { final CustomerWithIdLong customer1 = new CustomerWithIdLong(TestData.FIRST_NAME, TestData.LAST_NAME); customerRepository.save(customer1); @@ -300,7 +345,7 @@ void testCreateSingleWithAutoIdLong(@Autowired final CustomerWithIdLongRepositor } @Test - void testCreateMultipleWithAutoIdLong(@Autowired final CustomerWithIdLongRepository customerRepository) + void createMultipleWithAutoIdLong(@Autowired final CustomerWithIdLongRepository customerRepository) { final CustomerWithIdLong customer1 = new CustomerWithIdLong(TestData.FIRST_NAME, TestData.LAST_NAME); customerRepository.save(customer1); @@ -323,7 +368,7 @@ void testCreateMultipleWithAutoIdLong(@Autowired final CustomerWithIdLongReposit } @Test - void testCreateSingleWithNoAutoIdInteger( + void createSingleWithNoAutoIdInteger( @Autowired final CustomerWithIdIntegerNoAutoGenerateRepository customerRepository) { final CustomerWithIdIntegerNoAutoGenerate customer1 = @@ -341,7 +386,7 @@ void testCreateSingleWithNoAutoIdInteger( } @Test - void testCreateMultipleWithNoAutoIdInteger( + void createMultipleWithNoAutoIdInteger( @Autowired final CustomerWithIdIntegerNoAutoGenerateRepository customerRepository) { final CustomerWithIdIntegerNoAutoGenerate customer1 = @@ -364,7 +409,7 @@ void testCreateMultipleWithNoAutoIdInteger( } @Test - void testSaveSingleWithAutoIdInteger( + void saveSingleWithAutoIdInteger( @Autowired final CustomerWithIdIntegerRepository customerRepository ) { @@ -385,7 +430,7 @@ void testSaveSingleWithAutoIdInteger( } @Test - void testSaveSingleWithNoAutoIdInteger( + void saveSingleWithNoAutoIdInteger( @Autowired final CustomerWithIdIntegerNoAutoGenerateRepository customerRepository) { final CustomerWithIdIntegerNoAutoGenerate customer1 = @@ -397,7 +442,7 @@ void testSaveSingleWithNoAutoIdInteger( } @Test - void testAutoIdWithSubnodeWithId( + void autoIdWithSubnodeWithId( @Autowired final CustomerWithPurchaseRepository customerRepository) { final String purchaseName = "bag"; @@ -419,7 +464,7 @@ void testAutoIdWithSubnodeWithId( } @Test - void testAutoIdWithTwoSubnodeWithId( + void autoIdWithTwoSubnodeWithId( @Autowired final CustomerWithPurchaseRepository customerRepository) { final String purchaseName = "bag"; @@ -442,7 +487,7 @@ void testAutoIdWithTwoSubnodeWithId( } @Test - void testAutoIdWithTwoSameSubnodesWithSameIdDifferentNod( + void autoIdWithTwoSameSubnodesWithSameIdDifferentNod( @Autowired final CustomerWithPurchaseRepository customerRepository) { final Purchase purchase = new Purchase("bag"); @@ -475,7 +520,7 @@ void testAutoIdWithTwoSameSubnodesWithSameIdDifferentNod( } @Test - void testAutoIdWithTwoSameSubnodesWithSameIdSameNode( + void autoIdWithTwoSameSubnodesWithSameIdSameNode( @Autowired final CustomerWithPurchaseRepository customerRepository) { final Purchase purchase = new Purchase("bag"); @@ -498,7 +543,7 @@ void testAutoIdWithTwoSameSubnodesWithSameIdSameNode( } @Test - void testReplaceWithId(@Autowired final CustomerWithIdIntegerNoAutoGenerateRepository customerRepository) + void replaceWithId(@Autowired final CustomerWithIdIntegerNoAutoGenerateRepository customerRepository) { final CustomerWithIdIntegerNoAutoGenerate existingCustomer = new CustomerWithIdIntegerNoAutoGenerate(1, TestData.FIRST_NAME, TestData.LAST_NAME); @@ -523,7 +568,7 @@ void testReplaceWithId(@Autowired final CustomerWithIdIntegerNoAutoGenerateRepos } @Test - void testReplaceWithAutoId(@Autowired final CustomerWithIdIntegerRepository customerRepository) + void replaceWithAutoId(@Autowired final CustomerWithIdIntegerRepository customerRepository) { final CustomerWithIdInteger existingCustomer = new CustomerWithIdInteger(TestData.FIRST_NAME, TestData.LAST_NAME); @@ -550,7 +595,7 @@ void testReplaceWithAutoId(@Autowired final CustomerWithIdIntegerRepository cust } @Test - void testReplaceWithIdSaveAll(@Autowired final CustomerWithIdIntegerNoAutoGenerateRepository customerRepository) + void replaceWithIdSaveAll(@Autowired final CustomerWithIdIntegerNoAutoGenerateRepository customerRepository) { final CustomerWithIdIntegerNoAutoGenerate existingCustomer = new CustomerWithIdIntegerNoAutoGenerate(1, TestData.FIRST_NAME, TestData.LAST_NAME); @@ -565,7 +610,7 @@ void testReplaceWithIdSaveAll(@Autowired final CustomerWithIdIntegerNoAutoGenera } @Test - void testAddTwoWithId(@Autowired final CustomerWithIdIntegerNoAutoGenerateRepository customerRepository) + void addTwoWithId(@Autowired final CustomerWithIdIntegerNoAutoGenerateRepository customerRepository) { final CustomerWithIdIntegerNoAutoGenerate existingCustomer = new CustomerWithIdIntegerNoAutoGenerate(1, TestData.FIRST_NAME, TestData.LAST_NAME); @@ -588,7 +633,7 @@ void testAddTwoWithId(@Autowired final CustomerWithIdIntegerNoAutoGenerateReposi } @Test - void testIdsInMultipleTransactions( + void idsInMultipleTransactions( @Autowired final CustomerWithIdIntegerNoAutoGenerateRepository customerRepository, @Autowired final PlatformTransactionManager transactionManager ) @@ -626,7 +671,7 @@ void testIdsInMultipleTransactions( } @Test - void testIdsInSingleTransactions( + void idsInSingleTransactions( @Autowired final CustomerWithIdIntegerNoAutoGenerateRepository customerRepository, @Autowired final PlatformTransactionManager transactionManager ) diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/custom/CustomIdTest.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/custom/CustomIdTest.java new file mode 100644 index 00000000..bf5dc52d --- /dev/null +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/custom/CustomIdTest.java @@ -0,0 +1,200 @@ +/* + * Copyright © 2024 XDEV Software (https://xdev.software) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.custom; + +import java.time.LocalDate; +import java.util.List; +import java.util.Optional; +import java.util.stream.Stream; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.test.context.ContextConfiguration; + +import software.xdev.spring.data.eclipse.store.helper.TestData; +import software.xdev.spring.data.eclipse.store.helper.TestUtil; +import software.xdev.spring.data.eclipse.store.integration.isolated.IsolatedTestAnnotations; +import software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.IdTestConfiguration; +import software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.custom.model.CompositeKey; +import software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.custom.model.CompositeKeyAsRecord; +import software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.custom.model.CustomerWithIdCompositeKey; +import software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.custom.model.CustomerWithIdCompositeKeyAsRecord; +import software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.custom.model.CustomerWithIdCompositeKeyAsRecordRepository; +import software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.custom.model.CustomerWithIdCompositeKeyEmbeddedId; +import software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.custom.model.CustomerWithIdCompositeKeyEmbeddedIdRepository; +import software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.custom.model.CustomerWithIdCompositeKeyRepository; +import software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.custom.model.CustomerWithIdLocalDate; +import software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.custom.model.CustomerWithIdLocalDateRepository; +import software.xdev.spring.data.eclipse.store.repository.interfaces.EclipseStoreRepository; + + +@SuppressWarnings("OptionalGetWithoutIsPresent") +@IsolatedTestAnnotations +@ContextConfiguration(classes = {IdTestConfiguration.class}) +class CustomIdTest +{ + public static Stream generateData() + { + return Stream.of( + new SingleTestDataset<>( + id -> new CustomerWithIdCompositeKey(id, TestData.FIRST_NAME), + context -> context.getBean(CustomerWithIdCompositeKeyRepository.class), + () -> new CompositeKey(1, 1), + () -> new CompositeKey(2, 2) + ).toArguments(), + new SingleTestDataset<>( + id -> new CustomerWithIdCompositeKeyEmbeddedId(id, TestData.FIRST_NAME), + context -> context.getBean(CustomerWithIdCompositeKeyEmbeddedIdRepository.class), + () -> new CompositeKey(1, 1), + () -> new CompositeKey(2, 2) + ).toArguments(), + new SingleTestDataset<>( + id -> new CustomerWithIdCompositeKeyAsRecord(id, TestData.FIRST_NAME), + context -> context.getBean(CustomerWithIdCompositeKeyAsRecordRepository.class), + () -> new CompositeKeyAsRecord(1, 1), + () -> new CompositeKeyAsRecord(2, 2) + ).toArguments(), + new SingleTestDataset<>( + id -> new CustomerWithIdLocalDate(id, TestData.FIRST_NAME), + context -> context.getBean(CustomerWithIdLocalDateRepository.class), + () -> LocalDate.of(2024, 1, 1), + () -> LocalDate.of(2024, 1, 2) + ).toArguments() + ); + } + + private final IdTestConfiguration configuration; + + @Autowired + public CustomIdTest(final IdTestConfiguration configuration) + { + this.configuration = configuration; + } + + @ParameterizedTest + @MethodSource("generateData") + void createSingleWithCustomKey( + final SingleTestDataset data, @Autowired final ApplicationContext context) + { + final EclipseStoreRepository repository = data.repositoryGenerator().apply(context); + + final T customer = data.enitityGenerator().apply(data.firstIdSupplier().get()); + repository.save(customer); + + TestUtil.doBeforeAndAfterRestartOfDatastore( + this.configuration, + () -> { + final Optional loadedCustomer = repository.findById(data.firstIdSupplier().get()); + Assertions.assertTrue(loadedCustomer.isPresent()); + Assertions.assertEquals(customer, loadedCustomer.get()); + } + ); + } + + @ParameterizedTest + @MethodSource("generateData") + void createDoubleWithCustomKey( + final SingleTestDataset data, @Autowired final ApplicationContext context) + { + final EclipseStoreRepository repository = data.repositoryGenerator().apply(context); + + final T customer1 = data.enitityGenerator().apply(data.firstIdSupplier().get()); + repository.save(customer1); + + final T customer2 = data.enitityGenerator().apply(data.secondIdSupplier().get()); + repository.save(customer2); + + TestUtil.doBeforeAndAfterRestartOfDatastore( + this.configuration, + () -> { + Assertions.assertEquals(2, repository.findAll().size()); + Assertions.assertEquals( + 2, + repository.findAllById(List.of(data.firstIdSupplier().get(), data.secondIdSupplier().get())) + .size()); + + final Optional loadedCustomer1 = repository.findById(data.firstIdSupplier().get()); + Assertions.assertTrue(loadedCustomer1.isPresent()); + Assertions.assertEquals(customer1, loadedCustomer1.get()); + + final Optional loadedCustomer2 = repository.findById(data.secondIdSupplier().get()); + Assertions.assertTrue(loadedCustomer2.isPresent()); + Assertions.assertEquals(customer2, loadedCustomer2.get()); + } + ); + } + + @ParameterizedTest + @MethodSource("generateData") + void createNullWithCustomKey( + final SingleTestDataset data, @Autowired final ApplicationContext context) + { + final EclipseStoreRepository repository = data.repositoryGenerator().apply(context); + + final T customer = data.enitityGenerator().apply(null); + Assertions.assertThrows(IllegalArgumentException.class, () -> repository.save(customer)); + } + + @ParameterizedTest + @MethodSource("generateData") + void deleteBeforeRestartWithCustomKey( + final SingleTestDataset data, @Autowired final ApplicationContext context) + { + final EclipseStoreRepository repository = data.repositoryGenerator().apply(context); + + final T customer = data.enitityGenerator().apply(data.firstIdSupplier().get()); + repository.save(customer); + + repository.deleteById(data.firstIdSupplier().get()); + + TestUtil.doBeforeAndAfterRestartOfDatastore( + this.configuration, + () -> { + Assertions.assertTrue(repository.findAll().isEmpty()); + final Optional loadedCustomer = repository.findById(data.firstIdSupplier().get()); + Assertions.assertFalse(loadedCustomer.isPresent()); + } + ); + } + + @ParameterizedTest + @MethodSource("generateData") + void deleteAfterRestartWithCustomKey( + final SingleTestDataset data, @Autowired final ApplicationContext context) + { + final EclipseStoreRepository repository = data.repositoryGenerator().apply(context); + + final T customer = data.enitityGenerator().apply(data.firstIdSupplier().get()); + repository.save(customer); + + TestUtil.restartDatastore(this.configuration); + + repository.deleteById(data.firstIdSupplier().get()); + + TestUtil.doBeforeAndAfterRestartOfDatastore( + this.configuration, + () -> { + Assertions.assertTrue(repository.findAll().isEmpty()); + final Optional loadedCustomer = repository.findById(data.firstIdSupplier().get()); + Assertions.assertFalse(loadedCustomer.isPresent()); + } + ); + } +} diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/custom/SingleTestDataset.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/custom/SingleTestDataset.java new file mode 100644 index 00000000..b17085da --- /dev/null +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/custom/SingleTestDataset.java @@ -0,0 +1,38 @@ +/* + * Copyright © 2024 XDEV Software (https://xdev.software) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.custom; + +import java.util.function.Function; +import java.util.function.Supplier; + +import org.junit.jupiter.params.provider.Arguments; +import org.springframework.context.ApplicationContext; + +import software.xdev.spring.data.eclipse.store.repository.interfaces.EclipseStoreRepository; + + +public record SingleTestDataset( + Function enitityGenerator, + Function> repositoryGenerator, + Supplier firstIdSupplier, + Supplier secondIdSupplier +) +{ + public Arguments toArguments() + { + return Arguments.of(this); + } +} diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/custom/model/CompositeKey.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/custom/model/CompositeKey.java new file mode 100644 index 00000000..157b4be8 --- /dev/null +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/custom/model/CompositeKey.java @@ -0,0 +1,53 @@ +/* + * Copyright © 2024 XDEV Software (https://xdev.software) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.custom.model; + +import java.io.Serializable; +import java.util.Objects; + + +public class CompositeKey implements Serializable +{ + private final int idPart1; + private final int idPart2; + + public CompositeKey(final int idPart1, final int idPart2) + { + this.idPart1 = idPart1; + this.idPart2 = idPart2; + } + + @Override + public boolean equals(final Object o) + { + if(this == o) + { + return true; + } + if(o == null || this.getClass() != o.getClass()) + { + return false; + } + final CompositeKey that = (CompositeKey)o; + return this.idPart1 == that.idPart1 && this.idPart2 == that.idPart2; + } + + @Override + public int hashCode() + { + return Objects.hash(this.idPart1, this.idPart2); + } +} diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/custom/model/CompositeKeyAsRecord.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/custom/model/CompositeKeyAsRecord.java new file mode 100644 index 00000000..4ae66c86 --- /dev/null +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/custom/model/CompositeKeyAsRecord.java @@ -0,0 +1,20 @@ +/* + * Copyright © 2024 XDEV Software (https://xdev.software) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.custom.model; + +public record CompositeKeyAsRecord(int keyPart1, int keyPart2) +{ +} diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/custom/model/CustomerWithIdCompositeKey.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/custom/model/CustomerWithIdCompositeKey.java new file mode 100644 index 00000000..dad3fc07 --- /dev/null +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/custom/model/CustomerWithIdCompositeKey.java @@ -0,0 +1,74 @@ +/* + * Copyright © 2024 XDEV Software (https://xdev.software) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.custom.model; + +import java.util.Objects; + +import jakarta.persistence.Id; + + +public class CustomerWithIdCompositeKey +{ + @Id + private CompositeKey id; + + private final String firstName; + + public CustomerWithIdCompositeKey(final CompositeKey id, final String firstName) + { + this.id = id; + this.firstName = firstName; + } + + public String getFirstName() + { + return this.firstName; + } + + @Override + public String toString() + { + return String.format( + "Customer[firstName='%s']", + this.firstName); + } + + public CompositeKey getId() + { + return this.id; + } + + @Override + public boolean equals(final Object o) + { + if(this == o) + { + return true; + } + if(o == null || this.getClass() != o.getClass()) + { + return false; + } + final CustomerWithIdCompositeKey customer = (CustomerWithIdCompositeKey)o; + return Objects.equals(this.firstName, customer.firstName); + } + + @Override + public int hashCode() + { + return Objects.hash(this.firstName); + } +} diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/custom/model/CustomerWithIdCompositeKeyAsRecord.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/custom/model/CustomerWithIdCompositeKeyAsRecord.java new file mode 100644 index 00000000..6d6fa3e7 --- /dev/null +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/custom/model/CustomerWithIdCompositeKeyAsRecord.java @@ -0,0 +1,74 @@ +/* + * Copyright © 2024 XDEV Software (https://xdev.software) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.custom.model; + +import java.util.Objects; + +import jakarta.persistence.Id; + + +public class CustomerWithIdCompositeKeyAsRecord +{ + @Id + private CompositeKeyAsRecord id; + + private final String firstName; + + public CustomerWithIdCompositeKeyAsRecord(final CompositeKeyAsRecord id, final String firstName) + { + this.id = id; + this.firstName = firstName; + } + + public String getFirstName() + { + return this.firstName; + } + + @Override + public String toString() + { + return String.format( + "Customer[firstName='%s']", + this.firstName); + } + + public CompositeKeyAsRecord getId() + { + return this.id; + } + + @Override + public boolean equals(final Object o) + { + if(this == o) + { + return true; + } + if(o == null || this.getClass() != o.getClass()) + { + return false; + } + final CustomerWithIdCompositeKeyAsRecord customer = (CustomerWithIdCompositeKeyAsRecord)o; + return Objects.equals(this.firstName, customer.firstName); + } + + @Override + public int hashCode() + { + return Objects.hash(this.firstName); + } +} diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/custom/model/CustomerWithIdCompositeKeyAsRecordRepository.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/custom/model/CustomerWithIdCompositeKeyAsRecordRepository.java new file mode 100644 index 00000000..26ac79f8 --- /dev/null +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/custom/model/CustomerWithIdCompositeKeyAsRecordRepository.java @@ -0,0 +1,24 @@ +/* + * Copyright © 2024 XDEV Software (https://xdev.software) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.custom.model; + +import software.xdev.spring.data.eclipse.store.repository.interfaces.EclipseStoreRepository; + + +public interface CustomerWithIdCompositeKeyAsRecordRepository + extends EclipseStoreRepository +{ +} diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/custom/model/CustomerWithIdCompositeKeyEmbeddedId.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/custom/model/CustomerWithIdCompositeKeyEmbeddedId.java new file mode 100644 index 00000000..19cef1c5 --- /dev/null +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/custom/model/CustomerWithIdCompositeKeyEmbeddedId.java @@ -0,0 +1,74 @@ +/* + * Copyright © 2024 XDEV Software (https://xdev.software) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.custom.model; + +import java.util.Objects; + +import jakarta.persistence.EmbeddedId; + + +public class CustomerWithIdCompositeKeyEmbeddedId +{ + @EmbeddedId + private final CompositeKey id; + + private final String firstName; + + public CustomerWithIdCompositeKeyEmbeddedId(final CompositeKey id, final String firstName) + { + this.id = id; + this.firstName = firstName; + } + + public String getFirstName() + { + return this.firstName; + } + + @Override + public String toString() + { + return String.format( + "Customer[firstName='%s']", + this.firstName); + } + + public CompositeKey getId() + { + return this.id; + } + + @Override + public boolean equals(final Object o) + { + if(this == o) + { + return true; + } + if(o == null || this.getClass() != o.getClass()) + { + return false; + } + final CustomerWithIdCompositeKeyEmbeddedId customer = (CustomerWithIdCompositeKeyEmbeddedId)o; + return Objects.equals(this.firstName, customer.firstName); + } + + @Override + public int hashCode() + { + return Objects.hash(this.firstName); + } +} diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/custom/model/CustomerWithIdCompositeKeyEmbeddedIdRepository.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/custom/model/CustomerWithIdCompositeKeyEmbeddedIdRepository.java new file mode 100644 index 00000000..98a591a9 --- /dev/null +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/custom/model/CustomerWithIdCompositeKeyEmbeddedIdRepository.java @@ -0,0 +1,24 @@ +/* + * Copyright © 2024 XDEV Software (https://xdev.software) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.custom.model; + +import software.xdev.spring.data.eclipse.store.repository.interfaces.EclipseStoreRepository; + + +public interface CustomerWithIdCompositeKeyEmbeddedIdRepository + extends EclipseStoreRepository +{ +} diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/custom/model/CustomerWithIdCompositeKeyRepository.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/custom/model/CustomerWithIdCompositeKeyRepository.java new file mode 100644 index 00000000..c57cbd00 --- /dev/null +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/custom/model/CustomerWithIdCompositeKeyRepository.java @@ -0,0 +1,24 @@ +/* + * Copyright © 2024 XDEV Software (https://xdev.software) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.custom.model; + +import software.xdev.spring.data.eclipse.store.repository.interfaces.EclipseStoreRepository; + + +public interface CustomerWithIdCompositeKeyRepository + extends EclipseStoreRepository +{ +} diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/custom/model/CustomerWithIdLocalDate.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/custom/model/CustomerWithIdLocalDate.java new file mode 100644 index 00000000..126ffb14 --- /dev/null +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/custom/model/CustomerWithIdLocalDate.java @@ -0,0 +1,75 @@ +/* + * Copyright © 2024 XDEV Software (https://xdev.software) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.custom.model; + +import java.time.LocalDate; +import java.util.Objects; + +import jakarta.persistence.Id; + + +public class CustomerWithIdLocalDate +{ + @Id + private LocalDate id; + + private final String firstName; + + public CustomerWithIdLocalDate(final LocalDate id, final String firstName) + { + this.id = id; + this.firstName = firstName; + } + + public String getFirstName() + { + return this.firstName; + } + + @Override + public String toString() + { + return String.format( + "Customer[firstName='%s']", + this.firstName); + } + + public LocalDate getId() + { + return this.id; + } + + @Override + public boolean equals(final Object o) + { + if(this == o) + { + return true; + } + if(o == null || this.getClass() != o.getClass()) + { + return false; + } + final CustomerWithIdLocalDate customer = (CustomerWithIdLocalDate)o; + return Objects.equals(this.firstName, customer.firstName); + } + + @Override + public int hashCode() + { + return Objects.hash(this.firstName); + } +} diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/custom/model/CustomerWithIdLocalDateRepository.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/custom/model/CustomerWithIdLocalDateRepository.java new file mode 100644 index 00000000..39260256 --- /dev/null +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/custom/model/CustomerWithIdLocalDateRepository.java @@ -0,0 +1,26 @@ +/* + * Copyright © 2024 XDEV Software (https://xdev.software) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.custom.model; + +import java.time.LocalDate; + +import software.xdev.spring.data.eclipse.store.repository.interfaces.EclipseStoreRepository; + + +public interface CustomerWithIdLocalDateRepository + extends EclipseStoreRepository +{ +} diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/model/CustomerWithIdUuid.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/model/CustomerWithIdUuid.java new file mode 100644 index 00000000..d764a86a --- /dev/null +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/model/CustomerWithIdUuid.java @@ -0,0 +1,86 @@ +/* + * Copyright © 2024 XDEV Software (https://xdev.software) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.model; + +import java.util.Objects; +import java.util.UUID; + +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; + + +public class CustomerWithIdUuid +{ + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private UUID id; + + private final String firstName; + private final String lastName; + + public CustomerWithIdUuid(final String firstName, final String lastName) + { + this.firstName = firstName; + this.lastName = lastName; + } + + public String getFirstName() + { + return this.firstName; + } + + public String getLastName() + { + return this.lastName; + } + + @Override + public String toString() + { + return String.format( + "Customer[firstName='%s', lastName='%s']", + this.firstName, this.lastName); + } + + public UUID getId() + { + return this.id; + } + + @Override + public boolean equals(final Object o) + { + if(this == o) + { + return true; + } + if(o == null || this.getClass() != o.getClass()) + { + return false; + } + final CustomerWithIdUuid customer = (CustomerWithIdUuid)o; + return Objects.equals(this.firstName, customer.firstName) && Objects.equals( + this.lastName, + customer.lastName); + } + + @Override + public int hashCode() + { + return Objects.hash(this.firstName, this.lastName); + } +} diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/model/CustomerWithIdUuidRepository.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/model/CustomerWithIdUuidRepository.java new file mode 100644 index 00000000..a3a22ccb --- /dev/null +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/model/CustomerWithIdUuidRepository.java @@ -0,0 +1,25 @@ +/* + * Copyright © 2024 XDEV Software (https://xdev.software) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.model; + +import java.util.UUID; + +import org.springframework.data.repository.ListCrudRepository; + + +public interface CustomerWithIdUuidRepository extends ListCrudRepository +{ +} diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/by/string/Child.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/by/string/Child.java new file mode 100644 index 00000000..ef7da71a --- /dev/null +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/by/string/Child.java @@ -0,0 +1,82 @@ +/* + * Copyright © 2024 XDEV Software (https://xdev.software) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.xdev.spring.data.eclipse.store.integration.isolated.tests.query.by.string; + +import java.util.Objects; + + +public class Child +{ + private String firstName; + private String lastName; + + public Child(final String firstName, final String lastName) + { + this.firstName = firstName; + this.lastName = lastName; + } + + public String getFirstName() + { + return this.firstName; + } + + public void setFirstName(final String firstName) + { + this.firstName = firstName; + } + + public String getLastName() + { + return this.lastName; + } + + public void setLastName(final String lastName) + { + this.lastName = lastName; + } + + @Override + public String toString() + { + return String.format( + "Child[firstName='%s', lastName='%s']", + this.firstName, this.lastName); + } + + @Override + public boolean equals(final Object o) + { + if(this == o) + { + return true; + } + if(o == null || this.getClass() != o.getClass()) + { + return false; + } + final Child customer = (Child)o; + return Objects.equals(this.firstName, customer.firstName) && Objects.equals( + this.lastName, + customer.lastName); + } + + @Override + public int hashCode() + { + return Objects.hash(this.firstName, this.lastName); + } +} diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/by/string/Customer.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/by/string/Customer.java new file mode 100644 index 00000000..98b6daba --- /dev/null +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/by/string/Customer.java @@ -0,0 +1,89 @@ +/* + * Copyright © 2024 XDEV Software (https://xdev.software) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.xdev.spring.data.eclipse.store.integration.isolated.tests.query.by.string; + +import java.util.List; +import java.util.Objects; + + +public class Customer +{ + private String firstName; + private String lastName; + + public Customer(final String firstName, final String lastName) + { + this.firstName = firstName; + this.lastName = lastName; + } + + public String getFirstName() + { + return this.firstName; + } + + public void setFirstName(final String firstName) + { + this.firstName = firstName; + } + + public String getLastName() + { + return this.lastName; + } + + public void setLastName(final String lastName) + { + this.lastName = lastName; + } + + @Override + public String toString() + { + return String.format( + "Customer[firstName='%s', lastName='%s']", + this.firstName, this.lastName); + } + + @Override + public boolean equals(final Object o) + { + if(this == o) + { + return true; + } + if(o == null || this.getClass() != o.getClass()) + { + return false; + } + final Customer customer = (Customer)o; + return Objects.equals(this.firstName, customer.firstName) && Objects.equals( + this.lastName, + customer.lastName); + } + + @Override + public int hashCode() + { + return Objects.hash(this.firstName, this.lastName); + } + + @SuppressWarnings("OptionalGetWithoutIsPresent") + public static Customer getCustomerWithFirstName(final List customers, final String firstName) + { + return customers.stream().filter(customer -> customer.getFirstName().equals(firstName)).findFirst().get(); + } +} diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/by/string/CustomerRepository.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/by/string/CustomerRepository.java new file mode 100644 index 00000000..562f7a60 --- /dev/null +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/by/string/CustomerRepository.java @@ -0,0 +1,45 @@ +/* + * Copyright © 2024 XDEV Software (https://xdev.software) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.xdev.spring.data.eclipse.store.integration.isolated.tests.query.by.string; + +import java.util.List; +import java.util.Optional; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.PagingAndSortingRepository; + + +public interface CustomerRepository + extends CrudRepository, PagingAndSortingRepository +{ + Optional findByFirstName(String firstName); + + Iterable findAllByLastName(String lastName); + + @Override + Page findAll(Pageable pageable); + + Page findAllByLastName(String lastName, Pageable pageable); + + List findByOrderByLastNameAsc(); + + Iterable findAllByLastName(String lastName, Sort sort); + + List findAllByFirstName(String lastName, Pageable pageable); +} diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/by/string/CustomerWithChild.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/by/string/CustomerWithChild.java new file mode 100644 index 00000000..e143dc6c --- /dev/null +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/by/string/CustomerWithChild.java @@ -0,0 +1,104 @@ +/* + * Copyright © 2024 XDEV Software (https://xdev.software) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.xdev.spring.data.eclipse.store.integration.isolated.tests.query.by.string; + +import java.util.List; +import java.util.Objects; + + +public class CustomerWithChild +{ + private String firstName; + private String lastName; + + private Child child; + + public CustomerWithChild(final String firstName, final String lastName, final Child child) + { + this.firstName = firstName; + this.lastName = lastName; + this.child = child; + } + + public String getFirstName() + { + return this.firstName; + } + + public void setFirstName(final String firstName) + { + this.firstName = firstName; + } + + public String getLastName() + { + return this.lastName; + } + + public void setLastName(final String lastName) + { + this.lastName = lastName; + } + + public Child getChild() + { + return this.child; + } + + public void setChild(final Child child) + { + this.child = child; + } + + @Override + public String toString() + { + return String.format( + "Customer[firstName='%s', lastName='%s']", + this.firstName, this.lastName); + } + + @Override + public boolean equals(final Object o) + { + if(this == o) + { + return true; + } + if(o == null || this.getClass() != o.getClass()) + { + return false; + } + final CustomerWithChild customer = (CustomerWithChild)o; + return Objects.equals(this.firstName, customer.firstName) && Objects.equals( + this.lastName, + customer.lastName); + } + + @Override + public int hashCode() + { + return Objects.hash(this.firstName, this.lastName); + } + + @SuppressWarnings("OptionalGetWithoutIsPresent") + public static CustomerWithChild getCustomerWithFirstName( + final List customers, + final String firstName) + { + return customers.stream().filter(customer -> customer.getFirstName().equals(firstName)).findFirst().get(); + } +} diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/repositories/CustomerRepositoryWithQuery.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/by/string/CustomerWithChildRepository.java similarity index 55% rename from spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/repositories/CustomerRepositoryWithQuery.java rename to spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/by/string/CustomerWithChildRepository.java index 86d5b1d2..e708d148 100644 --- a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/repositories/CustomerRepositoryWithQuery.java +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/by/string/CustomerWithChildRepository.java @@ -13,24 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package software.xdev.spring.data.eclipse.store.integration.shared.repositories; +package software.xdev.spring.data.eclipse.store.integration.isolated.tests.query.by.string; -import java.util.List; import java.util.Optional; -import org.springframework.data.repository.Repository; +import org.springframework.data.repository.CrudRepository; -import software.xdev.spring.data.eclipse.store.repository.Query; - -public interface CustomerRepositoryWithQuery extends Repository +public interface CustomerWithChildRepository extends CrudRepository { - List findAll(); - - Optional findByFirstName(String firstName); - - @Query() - Iterable findAllByLastName(String lastName); - - void save(CustomerWithQuery customer); + Optional findByChild(Child child); } diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/by/string/QueryPageTest.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/by/string/QueryPageTest.java new file mode 100644 index 00000000..5a345241 --- /dev/null +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/by/string/QueryPageTest.java @@ -0,0 +1,174 @@ +/* + * Copyright © 2024 XDEV Software (https://xdev.software) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.xdev.spring.data.eclipse.store.integration.isolated.tests.query.by.string; + +import java.util.List; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.test.context.ContextConfiguration; + +import software.xdev.spring.data.eclipse.store.helper.TestData; +import software.xdev.spring.data.eclipse.store.helper.TestUtil; +import software.xdev.spring.data.eclipse.store.integration.isolated.IsolatedTestAnnotations; + + +@IsolatedTestAnnotations +@ContextConfiguration(classes = {QueryTestConfiguration.class}) +class QueryPageTest +{ + @Autowired + private CustomerRepository customerRepository; + @Autowired + private QueryTestConfiguration configuration; + + @Test + void pageableFindAllTwoPages() + { + final Customer customer1 = new Customer(TestData.FIRST_NAME, TestData.LAST_NAME); + this.customerRepository.save(customer1); + final Customer customer2 = new Customer(TestData.FIRST_NAME_ALTERNATIVE, TestData.LAST_NAME_ALTERNATIVE); + this.customerRepository.save(customer2); + + TestUtil.doBeforeAndAfterRestartOfDatastore( + this.configuration, + () -> { + final Pageable pageable = PageRequest.of(0, 1); + final List customersPage1 = + TestUtil.iterableToList(this.customerRepository.findAll(pageable)); + Assertions.assertEquals(1, customersPage1.size()); + + final List customersPage2 = + TestUtil.iterableToList(this.customerRepository.findAll(pageable.next())); + Assertions.assertEquals(1, customersPage2.size()); + + Assertions.assertNotEquals(customersPage1.get(0), customersPage2.get(0)); + } + ); + } + + @Test + void pageableFindAllTwoPagesWithNextPageable() + { + final Customer customer1 = new Customer(TestData.FIRST_NAME, TestData.LAST_NAME); + this.customerRepository.save(customer1); + final Customer customer2 = new Customer(TestData.FIRST_NAME_ALTERNATIVE, TestData.LAST_NAME_ALTERNATIVE); + this.customerRepository.save(customer2); + + TestUtil.doBeforeAndAfterRestartOfDatastore( + this.configuration, + () -> { + final Page customersPage1 = this.customerRepository.findAll(PageRequest.of(0, 1)); + Assertions.assertEquals(1, customersPage1.getContent().size()); + + final List customersPage2 = + TestUtil.iterableToList(this.customerRepository.findAll(customersPage1.nextPageable())); + Assertions.assertEquals(1, customersPage2.size()); + + Assertions.assertNotEquals(customersPage1.getContent().get(0), customersPage2.get(0)); + } + ); + } + + @Test + void pageableFindAllUnpaged() + { + final Customer customer1 = new Customer(TestData.FIRST_NAME, TestData.LAST_NAME); + this.customerRepository.save(customer1); + final Customer customer2 = new Customer(TestData.FIRST_NAME_ALTERNATIVE, TestData.LAST_NAME_ALTERNATIVE); + this.customerRepository.save(customer2); + + TestUtil.doBeforeAndAfterRestartOfDatastore( + this.configuration, + () -> { + final Pageable pageable = Pageable.unpaged(); + final List customersPage = + TestUtil.iterableToList(this.customerRepository.findAll(pageable)); + Assertions.assertEquals(2, customersPage.size()); + Assertions.assertNotEquals(customersPage.get(0), customersPage.get(1)); + } + ); + } + + @Test + void pageableFindAllOnePage() + { + final Customer customer1 = new Customer(TestData.FIRST_NAME, TestData.LAST_NAME); + this.customerRepository.save(customer1); + final Customer customer2 = new Customer(TestData.FIRST_NAME_ALTERNATIVE, TestData.LAST_NAME_ALTERNATIVE); + this.customerRepository.save(customer2); + + TestUtil.doBeforeAndAfterRestartOfDatastore( + this.configuration, + () -> { + final Pageable pageable = PageRequest.of(0, 2); + final List customersPage1 = + TestUtil.iterableToList(this.customerRepository.findAll(pageable)); + Assertions.assertEquals(2, customersPage1.size()); + Assertions.assertEquals( + customer1, + Customer.getCustomerWithFirstName(customersPage1, TestData.FIRST_NAME)); + Assertions.assertEquals( + customer2, + Customer.getCustomerWithFirstName(customersPage1, TestData.FIRST_NAME_ALTERNATIVE)); + } + ); + } + + @Test + void pageableFindByLastName() + { + final Customer customer1 = new Customer(TestData.FIRST_NAME, TestData.LAST_NAME); + this.customerRepository.save(customer1); + final Customer customer2 = new Customer(TestData.FIRST_NAME_ALTERNATIVE, TestData.LAST_NAME_ALTERNATIVE); + this.customerRepository.save(customer2); + + TestUtil.doBeforeAndAfterRestartOfDatastore( + this.configuration, + () -> { + final Pageable pageable = PageRequest.of(0, 10); + final List customersPage = + TestUtil.iterableToList(this.customerRepository.findAllByLastName(TestData.LAST_NAME, pageable)); + Assertions.assertEquals(1, customersPage.size()); + Assertions.assertEquals(customer1, customersPage.get(0)); + } + ); + } + + @Test + void pageableFindByFirstNameWithList() + { + final Customer customer1 = new Customer(TestData.FIRST_NAME, TestData.LAST_NAME); + this.customerRepository.save(customer1); + final Customer customer2 = new Customer(TestData.FIRST_NAME_ALTERNATIVE, TestData.LAST_NAME_ALTERNATIVE); + this.customerRepository.save(customer2); + + TestUtil.doBeforeAndAfterRestartOfDatastore( + this.configuration, + () -> { + final Pageable pageable = PageRequest.of(0, 10); + final List customersPage = + TestUtil.iterableToList(this.customerRepository.findAllByFirstName(TestData.FIRST_NAME, pageable)); + Assertions.assertEquals(1, customersPage.size()); + Assertions.assertEquals(customer1, customersPage.get(0)); + } + ); + } +} diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/by/string/QuerySortTest.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/by/string/QuerySortTest.java new file mode 100644 index 00000000..3eb98691 --- /dev/null +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/by/string/QuerySortTest.java @@ -0,0 +1,123 @@ +/* + * Copyright © 2024 XDEV Software (https://xdev.software) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.xdev.spring.data.eclipse.store.integration.isolated.tests.query.by.string; + +import java.util.List; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Sort; +import org.springframework.test.context.ContextConfiguration; + +import software.xdev.spring.data.eclipse.store.helper.TestData; +import software.xdev.spring.data.eclipse.store.helper.TestUtil; +import software.xdev.spring.data.eclipse.store.integration.isolated.IsolatedTestAnnotations; + + +@IsolatedTestAnnotations +@ContextConfiguration(classes = {QueryTestConfiguration.class}) +class QuerySortTest +{ + @Autowired + private CustomerRepository customerRepository; + @Autowired + private QueryTestConfiguration configuration; + + @Test + void sortFindByLastNameDefault() + { + final Customer customer1 = new Customer("B", TestData.LAST_NAME); + this.customerRepository.save(customer1); + final Customer customer2 = new Customer("A", TestData.LAST_NAME); + this.customerRepository.save(customer2); + + TestUtil.doBeforeAndAfterRestartOfDatastore( + this.configuration, + () -> { + final Sort sort = Sort.by("firstName"); + final List customersPage = + TestUtil.iterableToList(this.customerRepository.findAllByLastName(TestData.LAST_NAME, sort)); + Assertions.assertEquals(2, customersPage.size()); + Assertions.assertEquals(customer2, customersPage.get(0)); + Assertions.assertEquals(customer1, customersPage.get(1)); + } + ); + } + + @Test + void sortFindByFirstNameAscending() + { + final Customer customer1 = new Customer("B", TestData.LAST_NAME); + this.customerRepository.save(customer1); + final Customer customer2 = new Customer("A", TestData.LAST_NAME); + this.customerRepository.save(customer2); + + TestUtil.doBeforeAndAfterRestartOfDatastore( + this.configuration, + () -> { + final Sort sort = Sort.by("firstName").ascending(); + final List customersPage = + TestUtil.iterableToList(this.customerRepository.findAllByLastName(TestData.LAST_NAME, sort)); + Assertions.assertEquals(2, customersPage.size()); + Assertions.assertEquals(customer2, customersPage.get(0)); + Assertions.assertEquals(customer1, customersPage.get(1)); + } + ); + } + + @Test + void sortFindByFirstNameAscendingVariant() + { + final Customer customer1 = new Customer(TestData.FIRST_NAME, "B"); + this.customerRepository.save(customer1); + final Customer customer2 = new Customer(TestData.FIRST_NAME, "A"); + this.customerRepository.save(customer2); + + TestUtil.doBeforeAndAfterRestartOfDatastore( + this.configuration, + () -> { + final Sort sort = Sort.by(Sort.Direction.ASC, "lastName"); + final List customersPage = + TestUtil.iterableToList(this.customerRepository.findAll(sort)); + Assertions.assertEquals(2, customersPage.size()); + Assertions.assertEquals(customer2, customersPage.get(0)); + Assertions.assertEquals(customer1, customersPage.get(1)); + } + ); + } + + @Test + void sortFindByLastNameDescending() + { + final Customer customer1 = new Customer("B", TestData.LAST_NAME); + this.customerRepository.save(customer1); + final Customer customer2 = new Customer("A", TestData.LAST_NAME); + this.customerRepository.save(customer2); + + TestUtil.doBeforeAndAfterRestartOfDatastore( + this.configuration, + () -> { + final Sort sort = Sort.by("firstName").descending(); + final List customersPage = + TestUtil.iterableToList(this.customerRepository.findAllByLastName(TestData.LAST_NAME, sort)); + Assertions.assertEquals(2, customersPage.size()); + Assertions.assertEquals(customer1, customersPage.get(0)); + Assertions.assertEquals(customer2, customersPage.get(1)); + } + ); + } +} diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/by/string/QueryTest.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/by/string/QueryTest.java new file mode 100644 index 00000000..b3fe7e27 --- /dev/null +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/by/string/QueryTest.java @@ -0,0 +1,184 @@ +/* + * Copyright © 2024 XDEV Software (https://xdev.software) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.xdev.spring.data.eclipse.store.integration.isolated.tests.query.by.string; + +import java.util.List; +import java.util.Optional; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; + +import software.xdev.spring.data.eclipse.store.helper.TestData; +import software.xdev.spring.data.eclipse.store.helper.TestUtil; +import software.xdev.spring.data.eclipse.store.integration.isolated.IsolatedTestAnnotations; + + +@IsolatedTestAnnotations +@ContextConfiguration(classes = {QueryTestConfiguration.class}) +class QueryTest +{ + @Autowired + private CustomerRepository customerRepository; + @Autowired + private CustomerWithChildRepository customerWithChildRepository; + + @Autowired + private QueryTestConfiguration configuration; + + @Test + void basicFindAllByLastNameTwoResults() + { + final Customer customer1 = new Customer(TestData.FIRST_NAME, TestData.LAST_NAME); + this.customerRepository.save(customer1); + final Customer customer2 = new Customer(TestData.FIRST_NAME_ALTERNATIVE, TestData.LAST_NAME); + this.customerRepository.save(customer2); + + TestUtil.doBeforeAndAfterRestartOfDatastore( + this.configuration, + () -> { + final List customers = + TestUtil.iterableToList(this.customerRepository.findAllByLastName(TestData.LAST_NAME)); + Assertions.assertEquals(2, customers.size()); + Assertions.assertEquals(customer1, Customer.getCustomerWithFirstName(customers, TestData.FIRST_NAME)); + Assertions.assertEquals( + customer2, + Customer.getCustomerWithFirstName(customers, TestData.FIRST_NAME_ALTERNATIVE)); + } + ); + } + + @Test + void basicFindAllByLastNameOneResult() + { + final Customer customer1 = new Customer(TestData.FIRST_NAME, TestData.LAST_NAME); + this.customerRepository.save(customer1); + final Customer customer2 = new Customer(TestData.FIRST_NAME_ALTERNATIVE, TestData.LAST_NAME_ALTERNATIVE); + this.customerRepository.save(customer2); + + TestUtil.doBeforeAndAfterRestartOfDatastore( + this.configuration, + () -> { + final List customers = + TestUtil.iterableToList(this.customerRepository.findAllByLastName(TestData.LAST_NAME_ALTERNATIVE)); + Assertions.assertEquals(1, customers.size()); + Assertions.assertEquals(customer2, customers.get(0)); + } + ); + } + + @Test + void basicFindByFirstNameOneResult() + { + final Customer customer1 = new Customer(TestData.FIRST_NAME, TestData.LAST_NAME); + this.customerRepository.save(customer1); + + TestUtil.doBeforeAndAfterRestartOfDatastore( + this.configuration, + () -> { + final Optional foundCustomer = this.customerRepository.findByFirstName(TestData.FIRST_NAME); + Assertions.assertTrue(foundCustomer.isPresent()); + Assertions.assertEquals(TestData.FIRST_NAME, foundCustomer.get().getFirstName()); + } + ); + } + + @Test + void findByOrderByLastNameAsc() + { + final Customer customer1 = new Customer(TestData.FIRST_NAME, "B"); + this.customerRepository.save(customer1); + final Customer customer2 = new Customer(TestData.FIRST_NAME, "A"); + this.customerRepository.save(customer2); + + TestUtil.doBeforeAndAfterRestartOfDatastore( + this.configuration, + () -> { + final List customersPage = + TestUtil.iterableToList(this.customerRepository.findByOrderByLastNameAsc()); + Assertions.assertEquals(2, customersPage.size()); + Assertions.assertEquals(customer2, customersPage.get(0)); + Assertions.assertEquals(customer1, customersPage.get(1)); + } + ); + } + + @Test + void findByChildExistingChild() + { + final Child child = new Child(TestData.FIRST_NAME_ALTERNATIVE, TestData.LAST_NAME_ALTERNATIVE); + final CustomerWithChild customer = new CustomerWithChild(TestData.FIRST_NAME, TestData.LAST_NAME, child); + this.customerWithChildRepository.save(customer); + + TestUtil.doBeforeAndAfterRestartOfDatastore( + this.configuration, + () -> { + final Optional foundCustomer = this.customerWithChildRepository.findByChild(child); + Assertions.assertTrue(foundCustomer.isPresent()); + } + ); + } + + @Test + void findByChildNewChild() + { + final Child child = new Child(TestData.FIRST_NAME_ALTERNATIVE, TestData.LAST_NAME_ALTERNATIVE); + final CustomerWithChild customer = new CustomerWithChild(TestData.FIRST_NAME, TestData.LAST_NAME, child); + this.customerWithChildRepository.save(customer); + + TestUtil.doBeforeAndAfterRestartOfDatastore( + this.configuration, + () -> { + final Child queryChild = new Child(TestData.FIRST_NAME_ALTERNATIVE, TestData.LAST_NAME_ALTERNATIVE); + final Optional foundCustomer = + this.customerWithChildRepository.findByChild(queryChild); + Assertions.assertTrue(foundCustomer.isPresent()); + } + ); + } + + @Test + void findByChildWhenNullNotExists() + { + final Child child = new Child(TestData.FIRST_NAME_ALTERNATIVE, TestData.LAST_NAME_ALTERNATIVE); + final CustomerWithChild customer = new CustomerWithChild(TestData.FIRST_NAME, TestData.LAST_NAME, child); + this.customerWithChildRepository.save(customer); + + TestUtil.doBeforeAndAfterRestartOfDatastore( + this.configuration, + () -> { + final Optional foundCustomer = this.customerWithChildRepository.findByChild(null); + Assertions.assertTrue(foundCustomer.isEmpty()); + } + ); + } + + @Test + void findByChildWhenNullExists() + { + final CustomerWithChild customer = new CustomerWithChild(TestData.FIRST_NAME, TestData.LAST_NAME, null); + this.customerWithChildRepository.save(customer); + + TestUtil.doBeforeAndAfterRestartOfDatastore( + this.configuration, + () -> { + final Optional foundCustomer = this.customerWithChildRepository.findByChild(null); + Assertions.assertTrue(foundCustomer.isPresent()); + } + ); + } +} diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/by/string/QueryTestConfiguration.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/by/string/QueryTestConfiguration.java new file mode 100644 index 00000000..bab32f2e --- /dev/null +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/by/string/QueryTestConfiguration.java @@ -0,0 +1,38 @@ +/* + * Copyright © 2024 XDEV Software (https://xdev.software) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.xdev.spring.data.eclipse.store.integration.isolated.tests.query.by.string; + +import org.eclipse.store.integrations.spring.boot.types.configuration.EclipseStoreProperties; +import org.eclipse.store.integrations.spring.boot.types.factories.EmbeddedStorageFoundationFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; + +import software.xdev.spring.data.eclipse.store.integration.TestConfiguration; +import software.xdev.spring.data.eclipse.store.repository.config.EnableEclipseStoreRepositories; + + +@Configuration +@EnableEclipseStoreRepositories +public class QueryTestConfiguration extends TestConfiguration +{ + @Autowired + protected QueryTestConfiguration( + final EclipseStoreProperties defaultEclipseStoreProperties, + final EmbeddedStorageFoundationFactory defaultEclipseStoreProvider) + { + super(defaultEclipseStoreProperties, defaultEclipseStoreProvider); + } +} diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/by/string/User.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/by/string/User.java new file mode 100644 index 00000000..252a1364 --- /dev/null +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/by/string/User.java @@ -0,0 +1,142 @@ +/* + * Copyright © 2024 XDEV Software (https://xdev.software) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.xdev.spring.data.eclipse.store.integration.isolated.tests.query.by.string; + +import java.time.LocalDate; + +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; + + +public class User +{ + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + + private String firstName; + private String lastName; + private Integer age; + private String email; + private String city; + private LocalDate dateOfBirth; + private Boolean isActive; + + // Constructors, Getters, and Setters + + public User() + { + } + + public User( + final String firstName, + final String lastName, + final Integer age, + final String email, + final String city, + final LocalDate dateOfBirth, + final Boolean isActive) + { + this.firstName = firstName; + this.lastName = lastName; + this.age = age; + this.email = email; + this.city = city; + this.dateOfBirth = dateOfBirth; + this.isActive = isActive; + } + + public Long getId() + { + return this.id; + } + + public void setId(final Long id) + { + this.id = id; + } + + public String getFirstName() + { + return this.firstName; + } + + public void setFirstName(final String firstName) + { + this.firstName = firstName; + } + + public String getLastName() + { + return this.lastName; + } + + public void setLastName(final String lastName) + { + this.lastName = lastName; + } + + public Integer getAge() + { + return this.age; + } + + public void setAge(final Integer age) + { + this.age = age; + } + + public String getEmail() + { + return this.email; + } + + public void setEmail(final String email) + { + this.email = email; + } + + public String getCity() + { + return this.city; + } + + public void setCity(final String city) + { + this.city = city; + } + + public LocalDate getDateOfBirth() + { + return this.dateOfBirth; + } + + public void setDateOfBirth(final LocalDate dateOfBirth) + { + this.dateOfBirth = dateOfBirth; + } + + public Boolean getActive() + { + return this.isActive; + } + + public void setActive(final Boolean active) + { + this.isActive = active; + } +} diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/by/string/UserRepository.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/by/string/UserRepository.java new file mode 100644 index 00000000..1f1fdc2c --- /dev/null +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/by/string/UserRepository.java @@ -0,0 +1,100 @@ +/* + * Copyright © 2024 XDEV Software (https://xdev.software) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.xdev.spring.data.eclipse.store.integration.isolated.tests.query.by.string; + +import java.time.LocalDate; +import java.util.List; + +import org.springframework.stereotype.Repository; + +import software.xdev.spring.data.eclipse.store.repository.interfaces.EclipseStoreRepository; + + +@Repository +public interface UserRepository extends EclipseStoreRepository +{ + + // Test keyword: And + List findByFirstNameAndLastName(String firstName, String lastName); + + // Test keyword: Or + List findByFirstNameOrLastName(String firstName, String lastName); + + // Test keyword: Between + List findByAgeBetween(Integer startAge, Integer endAge); + + // Test keyword: LessThan + List findByAgeLessThan(Integer age); + + // Test keyword: LessThanEqual + List findByAgeLessThanEqual(Integer age); + + // Test keyword: GreaterThan + List findByAgeGreaterThan(Integer age); + + // Test keyword: GreaterThanEqual + List findByAgeGreaterThanEqual(Integer age); + + // Test keyword: After + List findByDateOfBirthAfter(LocalDate date); + + // Test keyword: Before + List findByDateOfBirthBefore(LocalDate date); + + // Test keyword: IsNull + List findByEmailIsNull(); + + // Test keyword: IsNotNull + List findByEmailIsNotNull(); + + // Test keyword: Like + List findByFirstNameLike(String pattern); + + List findByFirstNameLikeIgnoreCase(String pattern); + + // Test keyword: NotLike + List findByFirstNameNotLike(String pattern); + + // Test keyword: StartingWith + List findByFirstNameStartingWith(String prefix); + + // Test keyword: EndingWith + List findByFirstNameEndingWith(String suffix); + + // Test keyword: Containing + List findByFirstNameContaining(String infix); + + // Test keyword: OrderBy + List findByCityOrderByFirstNameAsc(String city); + + // Test keyword: Not + List findByFirstNameNot(String firstName); + + // Test keyword: In + List findByAgeIn(List ages); + + // Test keyword: NotIn + List findByAgeNotIn(List ages); + + // Test keyword: True + List findByIsActiveTrue(); + + // Test keyword: False + List findByIsActiveFalse(); + + // Additional fields to handle boolean flag for active status + List findByIsActive(Boolean isActive); +} diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/by/string/UserRepositoryTests.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/by/string/UserRepositoryTests.java new file mode 100644 index 00000000..c34a7a0d --- /dev/null +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/by/string/UserRepositoryTests.java @@ -0,0 +1,461 @@ +/* + * Copyright © 2024 XDEV Software (https://xdev.software) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.xdev.spring.data.eclipse.store.integration.isolated.tests.query.by.string; + +import java.time.LocalDate; +import java.util.List; +import java.util.stream.Stream; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; + +import software.xdev.spring.data.eclipse.store.integration.isolated.IsolatedTestAnnotations; + + +@IsolatedTestAnnotations +@ContextConfiguration(classes = {QueryTestConfiguration.class}) +public class UserRepositoryTests +{ + @Autowired + private UserRepository userRepository; + + @BeforeEach + public void setUp() + { + this.userRepository.deleteAll(); + + this.userRepository.save(new User( + "John", + "Doe", + 25, + "john.doe@example.com", + "New York", + LocalDate.of(1998, 1, 1), + true)); + this.userRepository.save(new User( + "Jane", + "Doe", + 30, + "jane.doe@example.com", + "Los Angeles", + LocalDate.of(1993, 2, 2), + false)); + this.userRepository.save(new User( + "Alice", + "Smith", + 28, + "alice.smith@example.com", + "New York", + LocalDate.of(1996, 3, 3), + true)); + this.userRepository.save(new User( + "Bob", + "Brown", + 35, + "bob.brown@example.com", + "Chicago", + LocalDate.of(1988, 4, 4), + true)); + } + + @ParameterizedTest + @MethodSource("provideArgumentsForFindByFirstNameAndLastName") + void testFindByFirstNameAndLastName(final String firstName, final String lastName, final int expectedSize) + { + final List users = this.userRepository.findByFirstNameAndLastName(firstName, lastName); + Assertions.assertEquals(expectedSize, users.size()); + } + + static Stream provideArgumentsForFindByFirstNameAndLastName() + { + return Stream.of( + Arguments.of("John", "Doe", 1), + Arguments.of("Jane", "Doe", 1), + Arguments.of("Alice", "Smith", 1), + Arguments.of("Bob", "Brown", 1), + Arguments.of("NonExistent", "User", 0) + ); + } + + @ParameterizedTest + @MethodSource("provideArgumentsForFindByFirstNameOrLastName") + void testFindByFirstNameOrLastName(final String firstName, final String lastName, final int expectedSize) + { + final List users = this.userRepository.findByFirstNameOrLastName(firstName, lastName); + Assertions.assertEquals(expectedSize, users.size()); + } + + static Stream provideArgumentsForFindByFirstNameOrLastName() + { + return Stream.of( + Arguments.of("John", "Smith", 2), + Arguments.of("Jane", "Doe", 2), + Arguments.of("NonExistent", "Brown", 1) + ); + } + + @ParameterizedTest + @MethodSource("provideArgumentsForFindByAgeBetween") + void testFindByAgeBetween(final int startAge, final int endAge, final int expectedSize) + { + final List users = this.userRepository.findByAgeBetween(startAge, endAge); + Assertions.assertEquals(expectedSize, users.size()); + } + + static Stream provideArgumentsForFindByAgeBetween() + { + return Stream.of( + Arguments.of(20, 30, 3), + Arguments.of(25, 35, 4), + Arguments.of(30, 40, 2) + ); + } + + @ParameterizedTest + @MethodSource("provideArgumentsForFindByAgeLessThan") + void testFindByAgeLessThan(final int age, final int expectedSize) + { + final List users = this.userRepository.findByAgeLessThan(age); + Assertions.assertEquals(expectedSize, users.size()); + } + + static Stream provideArgumentsForFindByAgeLessThan() + { + return Stream.of( + Arguments.of(30, 2), + Arguments.of(35, 3), + Arguments.of(40, 4) + ); + } + + @ParameterizedTest + @MethodSource("provideArgumentsForFindByAgeLessThanEqual") + void testFindByAgeLessThanEqual(final int age, final int expectedSize) + { + final List users = this.userRepository.findByAgeLessThanEqual(age); + Assertions.assertEquals(expectedSize, users.size()); + } + + static Stream provideArgumentsForFindByAgeLessThanEqual() + { + return Stream.of( + Arguments.of(30, 3), + Arguments.of(35, 4), + Arguments.of(25, 1) + ); + } + + @ParameterizedTest + @MethodSource("provideArgumentsForFindByAgeGreaterThan") + void testFindByAgeGreaterThan(final int age, final int expectedSize) + { + final List users = this.userRepository.findByAgeGreaterThan(age); + Assertions.assertEquals(expectedSize, users.size()); + } + + static Stream provideArgumentsForFindByAgeGreaterThan() + { + return Stream.of( + Arguments.of(25, 3), + Arguments.of(30, 1), + Arguments.of(35, 0) + ); + } + + @ParameterizedTest + @MethodSource("provideArgumentsForFindByAgeGreaterThanEqual") + void testFindByAgeGreaterThanEqual(final int age, final int expectedSize) + { + final List users = this.userRepository.findByAgeGreaterThanEqual(age); + Assertions.assertEquals(expectedSize, users.size()); + } + + static Stream provideArgumentsForFindByAgeGreaterThanEqual() + { + return Stream.of( + Arguments.of(25, 4), + Arguments.of(30, 2), + Arguments.of(35, 1) + ); + } + + @ParameterizedTest + @MethodSource("provideArgumentsForFindByDateOfBirthAfter") + void testFindByDateOfBirthAfter(final LocalDate date, final int expectedSize) + { + final List users = this.userRepository.findByDateOfBirthAfter(date); + Assertions.assertEquals(expectedSize, users.size()); + } + + static Stream provideArgumentsForFindByDateOfBirthAfter() + { + return Stream.of( + Arguments.of(LocalDate.of(1990, 1, 1), 3), + Arguments.of(LocalDate.of(2000, 1, 1), 0), + Arguments.of(LocalDate.of(1985, 1, 1), 4) + ); + } + + @ParameterizedTest + @MethodSource("provideArgumentsForFindByDateOfBirthBefore") + void testFindByDateOfBirthBefore(final LocalDate date, final int expectedSize) + { + final List users = this.userRepository.findByDateOfBirthBefore(date); + Assertions.assertEquals(expectedSize, users.size()); + } + + static Stream provideArgumentsForFindByDateOfBirthBefore() + { + return Stream.of( + Arguments.of(LocalDate.of(1990, 1, 1), 1), + Arguments.of(LocalDate.of(2000, 1, 1), 4), + Arguments.of(LocalDate.of(1985, 1, 1), 0) + ); + } + + @ParameterizedTest + @MethodSource("provideArgumentsForFindByEmailIsNull") + void testFindByEmailIsNull(final int expectedSize) + { + final List users = this.userRepository.findByEmailIsNull(); + Assertions.assertEquals(expectedSize, users.size()); + } + + static Stream provideArgumentsForFindByEmailIsNull() + { + return Stream.of( + Arguments.of(0) + ); + } + + @ParameterizedTest + @MethodSource("provideArgumentsForFindByEmailIsNotNull") + void testFindByEmailIsNotNull(final int expectedSize) + { + final List users = this.userRepository.findByEmailIsNotNull(); + Assertions.assertEquals(expectedSize, users.size()); + } + + static Stream provideArgumentsForFindByEmailIsNotNull() + { + return Stream.of( + Arguments.of(4) + ); + } + + @ParameterizedTest + @MethodSource("provideArgumentsForFindByFirstNameLike") + void testFindByFirstNameLike(final String pattern, final int expectedSize) + { + final List users = this.userRepository.findByFirstNameLike(pattern); + Assertions.assertEquals(expectedSize, users.size()); + } + + static Stream provideArgumentsForFindByFirstNameLike() + { + return Stream.of( + Arguments.of("John", 1), + Arguments.of("J%", 2), + Arguments.of("A%", 1) + ); + } + + @ParameterizedTest + @MethodSource("provideArgumentsForFindByFirstNameLikeIgnoreCase") + void testFindByFirstNameLikeIgnoreCase(final String pattern, final int expectedSize) + { + final List users = this.userRepository.findByFirstNameLikeIgnoreCase(pattern); + Assertions.assertEquals(expectedSize, users.size()); + } + + static Stream provideArgumentsForFindByFirstNameLikeIgnoreCase() + { + return Stream.of( + Arguments.of("John", 1), + Arguments.of("J%", 2), + Arguments.of("j%", 2), + Arguments.of("A%", 1), + Arguments.of("a%", 1) + ); + } + + @ParameterizedTest + @MethodSource("provideArgumentsForFindByFirstNameNotLike") + void testFindByFirstNameNotLike(final String pattern, final int expectedSize) + { + final List users = this.userRepository.findByFirstNameNotLike(pattern); + Assertions.assertEquals(expectedSize, users.size()); + } + + static Stream provideArgumentsForFindByFirstNameNotLike() + { + return Stream.of( + Arguments.of("John", 3), + Arguments.of("J%", 2), + Arguments.of("A%", 3) + ); + } + + @ParameterizedTest + @MethodSource("provideArgumentsForFindByFirstNameStartingWith") + void testFindByFirstNameStartingWith(final String prefix, final int expectedSize) + { + final List users = this.userRepository.findByFirstNameStartingWith(prefix); + Assertions.assertEquals(expectedSize, users.size()); + } + + static Stream provideArgumentsForFindByFirstNameStartingWith() + { + return Stream.of( + Arguments.of("J", 2), + Arguments.of("A", 1), + Arguments.of("B", 1), + Arguments.of("b", 0), + Arguments.of("x", 0) + ); + } + + @ParameterizedTest + @MethodSource("provideArgumentsForFindByFirstNameEndingWith") + void testFindByFirstNameEndingWith(final String suffix, final int expectedSize) + { + final List users = this.userRepository.findByFirstNameEndingWith(suffix); + Assertions.assertEquals(expectedSize, users.size()); + } + + static Stream provideArgumentsForFindByFirstNameEndingWith() + { + return Stream.of( + Arguments.of("n", 1), + Arguments.of("e", 2), + Arguments.of("b", 1), + Arguments.of("x", 0), + Arguments.of("", 4) + ); + } + + @ParameterizedTest + @MethodSource("provideArgumentsForFindByFirstNameContaining") + void testFindByFirstNameContaining(final String infix, final int expectedSize) + { + final List users = this.userRepository.findByFirstNameContaining(infix); + Assertions.assertEquals(expectedSize, users.size()); + } + + static Stream provideArgumentsForFindByFirstNameContaining() + { + return Stream.of( + Arguments.of("o", 2), + Arguments.of("a", 1), + Arguments.of("A", 1), + Arguments.of("i", 1), + Arguments.of("", 4) + ); + } + + @ParameterizedTest + @MethodSource("provideArgumentsForFindByCityOrderByFirstNameAsc") + void testFindByCityOrderByFirstNameAsc(final String city, final int expectedSize, final String firstName) + { + final List users = this.userRepository.findByCityOrderByFirstNameAsc(city); + Assertions.assertEquals(expectedSize, users.size()); + Assertions.assertEquals(firstName, users.get(0).getFirstName()); + } + + static Stream provideArgumentsForFindByCityOrderByFirstNameAsc() + { + return Stream.of( + Arguments.of("New York", 2, "Alice"), + Arguments.of("Los Angeles", 1, "Jane"), + Arguments.of("Chicago", 1, "Bob") + ); + } + + @ParameterizedTest + @MethodSource("provideArgumentsForFindByFirstNameNot") + void testFindByFirstNameNot(final String firstName, final int expectedSize) + { + final List users = this.userRepository.findByFirstNameNot(firstName); + Assertions.assertEquals(expectedSize, users.size()); + } + + static Stream provideArgumentsForFindByFirstNameNot() + { + return Stream.of( + Arguments.of("John", 3), + Arguments.of("Jane", 3), + Arguments.of("Alice", 3), + Arguments.of("Bob", 3), + Arguments.of("Sepp", 4) + ); + } + + @ParameterizedTest + @MethodSource("provideArgumentsForFindByAgeIn") + void testFindByAgeIn(final List ages, final int expectedSize) + { + final List users = this.userRepository.findByAgeIn(ages); + Assertions.assertEquals(expectedSize, users.size()); + } + + static Stream provideArgumentsForFindByAgeIn() + { + return Stream.of( + Arguments.of(List.of(25, 30), 2), + Arguments.of(List.of(28, 35), 2), + Arguments.of(List.of(40), 0) + ); + } + + @ParameterizedTest + @MethodSource("provideArgumentsForFindByAgeNotIn") + void testFindByAgeNotIn(final List ages, final int expectedSize) + { + final List users = this.userRepository.findByAgeNotIn(ages); + Assertions.assertEquals(expectedSize, users.size()); + } + + static Stream provideArgumentsForFindByAgeNotIn() + { + return Stream.of( + Arguments.of(List.of(25, 30), 2), + Arguments.of(List.of(28, 35), 2), + Arguments.of(List.of(40), 4) + ); + } + + @Test + void testFindByIsActiveTrue() + { + // Assuming all users are active for this example + final List users = this.userRepository.findByIsActiveTrue(); + Assertions.assertEquals(3, users.size()); + } + + @Test + void testFindByIsActiveFalse() + { + // Assuming all users are inactive for this example + final List users = this.userRepository.findByIsActiveFalse(); + Assertions.assertEquals(1, users.size()); + } +} diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/hsql/HsqlTest.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/hsql/HsqlTest.java new file mode 100644 index 00000000..620afbc7 --- /dev/null +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/hsql/HsqlTest.java @@ -0,0 +1,349 @@ +/* + * Copyright © 2024 XDEV Software (https://xdev.software) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.xdev.spring.data.eclipse.store.integration.isolated.tests.query.hsql; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotSame; + +import java.util.Arrays; +import java.util.Calendar; +import java.util.Date; +import java.util.List; +import java.util.stream.Stream; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; + +import software.xdev.spring.data.eclipse.store.helper.TestData; +import software.xdev.spring.data.eclipse.store.integration.isolated.IsolatedTestAnnotations; + + +@IsolatedTestAnnotations +@ContextConfiguration(classes = {HsqlTestConfiguration.class}) +class HsqlTest +{ + @Autowired + private MyEntityRepository repository; + + @Test + void workingCopyAndNotSameObject() + { + final MyEntity testEntity = new MyEntity(); + testEntity.setName(TestData.FIRST_NAME); + this.repository.save(testEntity); + + final List result = this.repository.findAllEntities(); + assertEquals(1, result.size()); + assertEquals(testEntity, result.get(0)); + assertNotSame(testEntity, result.get(0)); + } + + private static Stream provideTestDataFindAllEntities() + { + return Stream.of( + Arguments.of(createEntityLists(0), 3), + Arguments.of(createEntityLists(1), 3), + Arguments.of(createEntityLists(2), 2), + Arguments.of(createEntityLists(3), 0) + ); + } + @ParameterizedTest + @MethodSource("provideTestDataFindAllEntities") + void findAllEntities(final List entities, final int expectedSize) + { + this.repository.saveAll(entities); + final List result = this.repository.findAllEntities(); + assertEquals(expectedSize, result.size()); + } + + private static Stream provideTestDataFindByName() + { + return Stream.of( + Arguments.of(createEntityLists(0), 2), + Arguments.of(createEntityLists(1), 1), + Arguments.of(createEntityLists(2), 0), + Arguments.of(createEntityLists(3), 0) + ); + } + @ParameterizedTest + @MethodSource("provideTestDataFindByName") + void findByName(final List entities, final int expectedSize) + { + this.repository.saveAll(entities); + final List result = this.repository.findByName("John"); + assertEquals(expectedSize, result.size()); + if(expectedSize > 0) + { + assertEquals("John", result.get(0).getName()); + } + } + + private static Stream provideTestDataFindByNameAndAgeGreaterThan() + { + return Stream.of( + Arguments.of(createEntityLists(0), 0), + Arguments.of(createEntityLists(1), 1), + Arguments.of(createEntityLists(2), 0), + Arguments.of(createEntityLists(3), 0) + ); + } + @ParameterizedTest + @MethodSource("provideTestDataFindByNameAndAgeGreaterThan") + void findByNameAndAgeGreaterThan(final List entities, final int expectedSize) + { + this.repository.saveAll(entities); + final List result = this.repository.findByNameAndAgeGreaterThan("John", 25); + assertEquals(expectedSize, result.size()); + if(expectedSize > result.size()) + { + assertEquals("John", result.get(0).getName()); + } + } + + private static Stream provideTestDataFindAllOrderByAgeDesc() + { + return Stream.of( + Arguments.of(createEntityLists(0), 40), + Arguments.of(createEntityLists(1), 40), + Arguments.of(createEntityLists(2), 28), + Arguments.of(createEntityLists(3), null) + ); + } + @ParameterizedTest + @MethodSource("provideTestDataFindAllOrderByAgeDesc") + void findAllOrderByAgeDesc(final List entities, final Integer expectedFirstAge) + { + this.repository.saveAll(entities); + final List result = this.repository.findAllOrderByAgeDesc(); + assertEquals(entities.size(), result.size()); + if(expectedFirstAge != null) + { + assertEquals(expectedFirstAge, result.get(0).getAge()); + } + } + + private static Stream provideTestDataFindByNameIn() + { + return Stream.of( + Arguments.of(createEntityLists(0), 2), + Arguments.of(createEntityLists(1), 2), + Arguments.of(createEntityLists(2), 1), + Arguments.of(createEntityLists(3), 0) + ); + } + @ParameterizedTest + @MethodSource("provideTestDataFindByNameIn") + void findByNameIn(final List entities, final int expectedSize) + { + this.repository.saveAll(entities); + final List result = this.repository.findByNameIn(Arrays.asList("John", "Jane")); + assertEquals(expectedSize, result.size()); + } + + private static Stream provideTestDataFindByNameContaining() + { + return Stream.of( + Arguments.of(createEntityLists(0), 2), + Arguments.of(createEntityLists(1), 1), + Arguments.of(createEntityLists(2), 0), + Arguments.of(createEntityLists(3), 0) + ); + } + @ParameterizedTest + @MethodSource("provideTestDataFindByNameContaining") + void findByNameContaining(final List entities, final int expectedSize) + { + this.repository.saveAll(entities); + final List result = this.repository.findByNameContaining("Jo"); + assertEquals(expectedSize, result.size()); + } + + private static Stream provideTestDataFindByNameNative() + { + return Stream.of( + Arguments.of(createEntityLists(0), 2), + Arguments.of(createEntityLists(1), 1), + Arguments.of(createEntityLists(2), 0), + Arguments.of(createEntityLists(3), 0) + ); + } + @ParameterizedTest + @MethodSource("provideTestDataFindByNameNative") + void findByNameNative(final List entities, final int expectedSize) + { + this.repository.saveAll(entities); + final List result = this.repository.findByNameNative("John"); + assertEquals(expectedSize, result.size()); + } + + // TODO: This does not work currently, due to non existing parser in + // com.googlecode.cqengine.query.parser.common.QueryParser + // private static Stream provideTestDataFindByCreationDateAfter() + // { + // return Stream.of( + // Arguments.of(createEntityLists(0), 2), + // Arguments.of(createEntityLists(1), 3), + // Arguments.of(createEntityLists(2), 2), + // Arguments.of(createEntityLists(3), 0) + // ); + // } + // @ParameterizedTest + // @MethodSource("provideTestDataFindByCreationDateAfter") + // void findByCreationDateAfter(final List entities, final int expectedSize) + // { + // this.repository.saveAll(entities); + // final List result = + // this.repository.findByCreationDateAfter(LocalDate.now().minusDays(1)); + // assertEquals(expectedSize, result.size()); + // } + + private static Stream provideTestDataFindByAgeBetween() + { + return Stream.of( + Arguments.of(createEntityLists(0), 2), + Arguments.of(createEntityLists(1), 2), + Arguments.of(createEntityLists(2), 2), + Arguments.of(createEntityLists(3), 0) + ); + } + @ParameterizedTest + @MethodSource("provideTestDataFindByAgeBetween") + void findByAgeBetween(final List entities, final int expectedSize) + { + this.repository.saveAll(entities); + final List result = this.repository.findByAgeBetween(20, 30); + assertEquals(expectedSize, result.size()); + } + + private static Stream provideTestDataFindAllActive() + { + return Stream.of( + Arguments.of(createEntityLists(0), 2), + Arguments.of(createEntityLists(1), 0), + Arguments.of(createEntityLists(2), 2), + Arguments.of(createEntityLists(3), 0) + ); + } + @ParameterizedTest + @MethodSource("provideTestDataFindAllActive") + void findAllActive(final List entities, final int expectedSize) + { + this.repository.saveAll(entities); + final List result = this.repository.findAllActive(); + assertEquals(expectedSize, result.size()); + } + + private static Stream provideTestDataFindWhereOtherEntityIsNull() + { + return Stream.of( + Arguments.of(createEntityLists(0), 0), + Arguments.of(createEntityLists(1), 1), + Arguments.of(createEntityLists(2), 2), + Arguments.of(createEntityLists(3), 0) + ); + } + + @ParameterizedTest + @MethodSource("provideTestDataFindWhereOtherEntityIsNull") + void findWhereOtherEntityIsNull(final List entities, final int expectedSize) + { + this.repository.saveAll(entities); + final List result = this.repository.findWhereOtherEntityIsNull(); + assertEquals(expectedSize, result.size()); + } + + private static Stream provideTestDataFindWhereOtherEntityIsNotNull() + { + return Stream.of( + Arguments.of(createEntityLists(0), 3), + Arguments.of(createEntityLists(1), 2), + Arguments.of(createEntityLists(2), 0), + Arguments.of(createEntityLists(3), 0) + ); + } + @ParameterizedTest + @MethodSource("provideTestDataFindWhereOtherEntityIsNotNull") + void findWhereOtherEntityIsNotNull(final List entities, final int expectedSize) + { + this.repository.saveAll(entities); + final List result = this.repository.findWhereOtherEntityIsNotNull(); + assertEquals(expectedSize, result.size()); + } + + private static List createEntityLists(final int testDataSetIndex) + { + final OtherEntity otherEntity = new OtherEntity(); + otherEntity.setDescription("Test OtherEntity"); + + final Calendar calendarPastOneYear = Calendar.getInstance(); + calendarPastOneYear.add(Calendar.YEAR, -1); + + return switch(testDataSetIndex) + { + case 0 -> Arrays.asList( + createMyEntity("John", 21, calendarPastOneYear.getTime(), true, otherEntity), + createMyEntity("John", 25, false, otherEntity), + createMyEntity("Doe", 40, true, otherEntity) + ); + case 1 -> Arrays.asList( + createMyEntity("John", 30, false, otherEntity), + createMyEntity("Jane", 25, false, otherEntity), + createMyEntity("Doe", 40, false, null) + ); + case 2 -> Arrays.asList( + createMyEntity("Jane", 22, true, null), + createMyEntity("Bob", 28, true, null) + ); + case 3 -> List.of(); + default -> throw new RuntimeException("Wrong index!"); + }; + } + + private static MyEntity createMyEntity( + final String name, + final int age, + final boolean active, + final OtherEntity otherEntity) + { + return createMyEntity( + name, + age, + new Date(), + active, + otherEntity + ); + } + + private static MyEntity createMyEntity( + final String name, + final int age, + final Date creationDate, + final boolean active, + final OtherEntity otherEntity) + { + final MyEntity entity = new MyEntity(); + entity.setName(name); + entity.setAge(age); + entity.setCreationDate(creationDate); + entity.setActive(active); + entity.setOtherEntity(otherEntity); + return entity; + } +} diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/hsql/HsqlTestConfiguration.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/hsql/HsqlTestConfiguration.java new file mode 100644 index 00000000..cfd5d871 --- /dev/null +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/hsql/HsqlTestConfiguration.java @@ -0,0 +1,38 @@ +/* + * Copyright © 2024 XDEV Software (https://xdev.software) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.xdev.spring.data.eclipse.store.integration.isolated.tests.query.hsql; + +import org.eclipse.store.integrations.spring.boot.types.configuration.EclipseStoreProperties; +import org.eclipse.store.integrations.spring.boot.types.factories.EmbeddedStorageFoundationFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; + +import software.xdev.spring.data.eclipse.store.integration.TestConfiguration; +import software.xdev.spring.data.eclipse.store.repository.config.EnableEclipseStoreRepositories; + + +@Configuration +@EnableEclipseStoreRepositories +public class HsqlTestConfiguration extends TestConfiguration +{ + @Autowired + protected HsqlTestConfiguration( + final EclipseStoreProperties defaultEclipseStoreProperties, + final EmbeddedStorageFoundationFactory defaultEclipseStoreProvider) + { + super(defaultEclipseStoreProperties, defaultEclipseStoreProvider); + } +} diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/hsql/MyEntity.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/hsql/MyEntity.java new file mode 100644 index 00000000..41f7259c --- /dev/null +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/hsql/MyEntity.java @@ -0,0 +1,146 @@ +/* + * Copyright © 2024 XDEV Software (https://xdev.software) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.xdev.spring.data.eclipse.store.integration.isolated.tests.query.hsql; + +import java.util.Date; +import java.util.Objects; + +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; + + +public class MyEntity +{ + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + + private String name; + + private Integer age; + + private Date creationDate; + + private Boolean active; + + private OtherEntity otherEntity; + + public MyEntity() + { + } + + public MyEntity( + final Long id, + final String name, + final int age, + final Date creationDate, + final boolean active, + final OtherEntity otherEntity) + { + this.id = id; + this.name = name; + this.age = age; + this.creationDate = creationDate; + this.active = active; + this.otherEntity = otherEntity; + } + + public Long getId() + { + return this.id; + } + + public void setId(final Long id) + { + this.id = id; + } + + public String getName() + { + return this.name; + } + + public void setName(final String name) + { + this.name = name; + } + + public int getAge() + { + return this.age; + } + + public void setAge(final int age) + { + this.age = age; + } + + public Date getCreationDate() + { + return this.creationDate; + } + + public void setCreationDate(final Date creationDate) + { + this.creationDate = creationDate; + } + + public boolean isActive() + { + return this.active; + } + + public void setActive(final boolean active) + { + this.active = active; + } + + public OtherEntity getOtherEntity() + { + return this.otherEntity; + } + + public void setOtherEntity(final OtherEntity otherEntity) + { + this.otherEntity = otherEntity; + } + + @Override + public boolean equals(final Object o) + { + if(this == o) + { + return true; + } + if(o == null || this.getClass() != o.getClass()) + { + return false; + } + final MyEntity myEntity = (MyEntity)o; + return Objects.equals(this.id, myEntity.id) && Objects.equals(this.name, myEntity.name) + && Objects.equals(this.age, myEntity.age) && Objects.equals(this.creationDate, myEntity.creationDate) + && Objects.equals(this.active, myEntity.active) && Objects.equals( + this.otherEntity, + myEntity.otherEntity); + } + + @Override + public int hashCode() + { + return Objects.hash(this.id, this.name, this.age, this.creationDate, this.active, this.otherEntity); + } +} diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/hsql/MyEntityRepository.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/hsql/MyEntityRepository.java new file mode 100644 index 00000000..2e9b7ae6 --- /dev/null +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/hsql/MyEntityRepository.java @@ -0,0 +1,76 @@ +/* + * Copyright © 2024 XDEV Software (https://xdev.software) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.xdev.spring.data.eclipse.store.integration.isolated.tests.query.hsql; + +import java.util.List; + +import org.springframework.data.repository.ListCrudRepository; + +import software.xdev.spring.data.eclipse.store.repository.Query; + + +public interface MyEntityRepository extends ListCrudRepository +{ + // Simple Select + @Query("SELECT * FROM MyEntity") + List findAllEntities(); + + // Select with a where clause + @Query("SELECT * FROM MyEntity WHERE name = '?1'") + List findByName(String name); + + // Select with multiple where clauses + @Query(" SELECT * FROM MyEntity WHERE (name = '?1' AND age > ?2)") + List findByNameAndAgeGreaterThan(String name, int age); + + // Select with order by + @Query(" SELECT * FROM MyEntity ORDER BY age DESC") + List findAllOrderByAgeDesc(); + + // Select with IN clause + @Query(" SELECT * FROM MyEntity WHERE name IN ?1") + List findByNameIn(List names); + + // Select with LIKE clause + @Query(" SELECT * FROM MyEntity WHERE 'name' LIKE '%?1%'") + List findByNameContaining(String keyword); + + // Select with native query + @Query(value = "SELECT * FROM my_entity WHERE name = '?1'") + List findByNameNative(String name); + + // Select with date comparison + // TODO: This does not work currently, due to non existing parser in + // com.googlecode.cqengine.query.parser.common.QueryParser + // @Query("SELECT * FROM MyEntity WHERE creationDate > '?1'") + // List findByCreationDateAfter(LocalDate date); + + // Select with between clause + @Query("SELECT * FROM MyEntity WHERE age BETWEEN ?1 AND ?2") + List findByAgeBetween(int startAge, int endAge); + + // Select with boolean condition + @Query("SELECT * FROM MyEntity WHERE active = true") + List findAllActive(); + + // Select with is null condition + @Query("SELECT * FROM MyEntity WHERE otherEntity IS NULL") + List findWhereOtherEntityIsNull(); + + // Select with is not null condition + @Query("SELECT * FROM MyEntity WHERE otherEntity IS NOT NULL") + List findWhereOtherEntityIsNotNull(); +} diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/hsql/OtherEntity.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/hsql/OtherEntity.java new file mode 100644 index 00000000..da86a4d1 --- /dev/null +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/hsql/OtherEntity.java @@ -0,0 +1,56 @@ +/* + * Copyright © 2024 XDEV Software (https://xdev.software) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.xdev.spring.data.eclipse.store.integration.isolated.tests.query.hsql; + +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; + + +public class OtherEntity +{ + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + + private String description; + + public OtherEntity(final Long id, final String description) + { + this.id = id; + this.description = description; + } + + public OtherEntity() + { + } + + public String getDescription() + { + return this.description; + } + + public void setDescription(final String description) + { + this.description = description; + } + + public Long getId() + { + return this.id; + } +} + diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/tests/JpaCompatibilityTest.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/tests/JpaCompatibilityTest.java deleted file mode 100644 index 5a1a095f..00000000 --- a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/tests/JpaCompatibilityTest.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright © 2024 XDEV Software (https://xdev.software) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package software.xdev.spring.data.eclipse.store.integration.shared.tests; - -import java.util.List; -import java.util.Optional; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; - -import software.xdev.spring.data.eclipse.store.helper.TestData; -import software.xdev.spring.data.eclipse.store.helper.TestUtil; -import software.xdev.spring.data.eclipse.store.integration.shared.DefaultTestAnnotations; -import software.xdev.spring.data.eclipse.store.integration.shared.SharedTestConfiguration; -import software.xdev.spring.data.eclipse.store.integration.shared.repositories.CustomerRepositoryWithQuery; -import software.xdev.spring.data.eclipse.store.integration.shared.repositories.CustomerWithQuery; - - -@DefaultTestAnnotations -class JpaCompatibilityTest -{ - @Autowired - private CustomerRepositoryWithQuery customerRepository; - - @Autowired - private SharedTestConfiguration configuration; - - @Test - void testUseQueryAnnotationFindAll() - { - final CustomerWithQuery customer1 = new CustomerWithQuery(TestData.FIRST_NAME, TestData.LAST_NAME); - this.customerRepository.save(customer1); - - TestUtil.doBeforeAndAfterRestartOfDatastore( - this.configuration, - () -> { - final List customers = TestUtil.iterableToList(this.customerRepository.findAll()); - Assertions.assertEquals(1, customers.size()); - Assertions.assertEquals(customer1, customers.get(0)); - Assertions.assertNotSame(customer1, customers.get(0)); - } - ); - } - - @Test - void testUseQueryAnnotationFindByFirstName() - { - final CustomerWithQuery customer1 = new CustomerWithQuery(TestData.FIRST_NAME, TestData.LAST_NAME); - this.customerRepository.save(customer1); - - TestUtil.doBeforeAndAfterRestartOfDatastore( - this.configuration, - () -> { - final Optional foundCustomer = - this.customerRepository.findByFirstName(TestData.FIRST_NAME); - Assertions.assertTrue(foundCustomer.isPresent()); - Assertions.assertEquals(customer1, foundCustomer.get()); - Assertions.assertNotSame(customer1, foundCustomer.get()); - } - ); - } - - @Test - void testUseQueryAnnotationFindAllByLastName() - { - final CustomerWithQuery customer1 = new CustomerWithQuery(TestData.FIRST_NAME, TestData.LAST_NAME); - this.customerRepository.save(customer1); - - TestUtil.doBeforeAndAfterRestartOfDatastore( - this.configuration, - () -> { - final List customers = - TestUtil.iterableToList(this.customerRepository.findAllByLastName(TestData.LAST_NAME)); - Assertions.assertEquals(1, customers.size()); - Assertions.assertEquals(customer1, customers.get(0)); - Assertions.assertNotSame(customer1, customers.get(0)); - } - ); - } -} diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/tests/QueryTest.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/tests/QueryTest.java deleted file mode 100644 index 00d2905b..00000000 --- a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/tests/QueryTest.java +++ /dev/null @@ -1,408 +0,0 @@ -/* - * Copyright © 2024 XDEV Software (https://xdev.software) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package software.xdev.spring.data.eclipse.store.integration.shared.tests; - -import java.util.List; -import java.util.Optional; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Sort; - -import software.xdev.spring.data.eclipse.store.helper.TestData; -import software.xdev.spring.data.eclipse.store.helper.TestUtil; -import software.xdev.spring.data.eclipse.store.integration.shared.DefaultTestAnnotations; -import software.xdev.spring.data.eclipse.store.integration.shared.SharedTestConfiguration; -import software.xdev.spring.data.eclipse.store.integration.shared.repositories.Child; -import software.xdev.spring.data.eclipse.store.integration.shared.repositories.Customer; -import software.xdev.spring.data.eclipse.store.integration.shared.repositories.CustomerRepository; -import software.xdev.spring.data.eclipse.store.integration.shared.repositories.CustomerWithChild; -import software.xdev.spring.data.eclipse.store.integration.shared.repositories.CustomerWithChildRepository; - - -@DefaultTestAnnotations -class QueryTest -{ - @Autowired - private CustomerRepository customerRepository; - @Autowired - private CustomerWithChildRepository customerWithChildRepository; - - @Autowired - private SharedTestConfiguration configuration; - - @Test - void testBasicFindAllByLastNameTwoResults() - { - final Customer customer1 = new Customer(TestData.FIRST_NAME, TestData.LAST_NAME); - this.customerRepository.save(customer1); - final Customer customer2 = new Customer(TestData.FIRST_NAME_ALTERNATIVE, TestData.LAST_NAME); - this.customerRepository.save(customer2); - - TestUtil.doBeforeAndAfterRestartOfDatastore( - this.configuration, - () -> { - final List customers = - TestUtil.iterableToList(this.customerRepository.findAllByLastName(TestData.LAST_NAME)); - Assertions.assertEquals(2, customers.size()); - Assertions.assertEquals(customer1, Customer.getCustomerWithFirstName(customers, TestData.FIRST_NAME)); - Assertions.assertEquals( - customer2, - Customer.getCustomerWithFirstName(customers, TestData.FIRST_NAME_ALTERNATIVE)); - } - ); - } - - @Test - void testBasicFindAllByLastNameOneResult() - { - final Customer customer1 = new Customer(TestData.FIRST_NAME, TestData.LAST_NAME); - this.customerRepository.save(customer1); - final Customer customer2 = new Customer(TestData.FIRST_NAME_ALTERNATIVE, TestData.LAST_NAME_ALTERNATIVE); - this.customerRepository.save(customer2); - - TestUtil.doBeforeAndAfterRestartOfDatastore( - this.configuration, - () -> { - final List customers = - TestUtil.iterableToList(this.customerRepository.findAllByLastName(TestData.LAST_NAME_ALTERNATIVE)); - Assertions.assertEquals(1, customers.size()); - Assertions.assertEquals(customer2, customers.get(0)); - } - ); - } - - @Test - void testBasicFindByFirstNameOneResult() - { - final Customer customer1 = new Customer(TestData.FIRST_NAME, TestData.LAST_NAME); - this.customerRepository.save(customer1); - - TestUtil.doBeforeAndAfterRestartOfDatastore( - this.configuration, - () -> { - final Optional foundCustomer = this.customerRepository.findByFirstName(TestData.FIRST_NAME); - Assertions.assertTrue(foundCustomer.isPresent()); - Assertions.assertEquals(TestData.FIRST_NAME, foundCustomer.get().getFirstName()); - } - ); - } - - @Test - void testPageableFindAllTwoPages() - { - final Customer customer1 = new Customer(TestData.FIRST_NAME, TestData.LAST_NAME); - this.customerRepository.save(customer1); - final Customer customer2 = new Customer(TestData.FIRST_NAME_ALTERNATIVE, TestData.LAST_NAME_ALTERNATIVE); - this.customerRepository.save(customer2); - - TestUtil.doBeforeAndAfterRestartOfDatastore( - this.configuration, - () -> { - final Pageable pageable = PageRequest.of(0, 1); - final List customersPage1 = - TestUtil.iterableToList(this.customerRepository.findAll(pageable)); - Assertions.assertEquals(1, customersPage1.size()); - - final List customersPage2 = - TestUtil.iterableToList(this.customerRepository.findAll(pageable.next())); - Assertions.assertEquals(1, customersPage2.size()); - - Assertions.assertNotEquals(customersPage1.get(0), customersPage2.get(0)); - } - ); - } - - @Test - void testPageableFindAllTwoPagesWithNextPageable() - { - final Customer customer1 = new Customer(TestData.FIRST_NAME, TestData.LAST_NAME); - this.customerRepository.save(customer1); - final Customer customer2 = new Customer(TestData.FIRST_NAME_ALTERNATIVE, TestData.LAST_NAME_ALTERNATIVE); - this.customerRepository.save(customer2); - - TestUtil.doBeforeAndAfterRestartOfDatastore( - this.configuration, - () -> { - final Page customersPage1 = this.customerRepository.findAll(PageRequest.of(0, 1)); - Assertions.assertEquals(1, customersPage1.getContent().size()); - - final List customersPage2 = - TestUtil.iterableToList(this.customerRepository.findAll(customersPage1.nextPageable())); - Assertions.assertEquals(1, customersPage2.size()); - - Assertions.assertNotEquals(customersPage1.getContent().get(0), customersPage2.get(0)); - } - ); - } - - @Test - void testPageableFindAllUnpaged() - { - final Customer customer1 = new Customer(TestData.FIRST_NAME, TestData.LAST_NAME); - this.customerRepository.save(customer1); - final Customer customer2 = new Customer(TestData.FIRST_NAME_ALTERNATIVE, TestData.LAST_NAME_ALTERNATIVE); - this.customerRepository.save(customer2); - - TestUtil.doBeforeAndAfterRestartOfDatastore( - this.configuration, - () -> { - final Pageable pageable = Pageable.unpaged(); - final List customersPage = TestUtil.iterableToList(this.customerRepository.findAll(pageable)); - Assertions.assertEquals(2, customersPage.size()); - Assertions.assertNotEquals(customersPage.get(0), customersPage.get(1)); - } - ); - } - - @Test - void testPageableFindAllOnePage() - { - final Customer customer1 = new Customer(TestData.FIRST_NAME, TestData.LAST_NAME); - this.customerRepository.save(customer1); - final Customer customer2 = new Customer(TestData.FIRST_NAME_ALTERNATIVE, TestData.LAST_NAME_ALTERNATIVE); - this.customerRepository.save(customer2); - - TestUtil.doBeforeAndAfterRestartOfDatastore( - this.configuration, - () -> { - final Pageable pageable = PageRequest.of(0, 2); - final List customersPage1 = - TestUtil.iterableToList(this.customerRepository.findAll(pageable)); - Assertions.assertEquals(2, customersPage1.size()); - Assertions.assertEquals( - customer1, - Customer.getCustomerWithFirstName(customersPage1, TestData.FIRST_NAME)); - Assertions.assertEquals( - customer2, - Customer.getCustomerWithFirstName(customersPage1, TestData.FIRST_NAME_ALTERNATIVE)); - } - ); - } - - @Test - void testPageableFindByLastName() - { - final Customer customer1 = new Customer(TestData.FIRST_NAME, TestData.LAST_NAME); - this.customerRepository.save(customer1); - final Customer customer2 = new Customer(TestData.FIRST_NAME_ALTERNATIVE, TestData.LAST_NAME_ALTERNATIVE); - this.customerRepository.save(customer2); - - TestUtil.doBeforeAndAfterRestartOfDatastore( - this.configuration, - () -> { - final Pageable pageable = PageRequest.of(0, 10); - final List customersPage = - TestUtil.iterableToList(this.customerRepository.findAllByLastName(TestData.LAST_NAME, pageable)); - Assertions.assertEquals(1, customersPage.size()); - Assertions.assertEquals(customer1, customersPage.get(0)); - } - ); - } - - @Test - void testPageableFindByFirstNameWithList() - { - final Customer customer1 = new Customer(TestData.FIRST_NAME, TestData.LAST_NAME); - this.customerRepository.save(customer1); - final Customer customer2 = new Customer(TestData.FIRST_NAME_ALTERNATIVE, TestData.LAST_NAME_ALTERNATIVE); - this.customerRepository.save(customer2); - - TestUtil.doBeforeAndAfterRestartOfDatastore( - this.configuration, - () -> { - final Pageable pageable = PageRequest.of(0, 10); - final List customersPage = - TestUtil.iterableToList(this.customerRepository.findAllByFirstName(TestData.FIRST_NAME, pageable)); - Assertions.assertEquals(1, customersPage.size()); - Assertions.assertEquals(customer1, customersPage.get(0)); - } - ); - } - - @Test - void testSortFindByLastNameDefault() - { - final Customer customer1 = new Customer("B", TestData.LAST_NAME); - this.customerRepository.save(customer1); - final Customer customer2 = new Customer("A", TestData.LAST_NAME); - this.customerRepository.save(customer2); - - TestUtil.doBeforeAndAfterRestartOfDatastore( - this.configuration, - () -> { - final Sort sort = Sort.by("firstName"); - final List customersPage = - TestUtil.iterableToList(this.customerRepository.findAllByLastName(TestData.LAST_NAME, sort)); - Assertions.assertEquals(2, customersPage.size()); - Assertions.assertEquals(customer2, customersPage.get(0)); - Assertions.assertEquals(customer1, customersPage.get(1)); - } - ); - } - - @Test - void testSortFindByFirstNameAscending() - { - final Customer customer1 = new Customer("B", TestData.LAST_NAME); - this.customerRepository.save(customer1); - final Customer customer2 = new Customer("A", TestData.LAST_NAME); - this.customerRepository.save(customer2); - - TestUtil.doBeforeAndAfterRestartOfDatastore( - this.configuration, - () -> { - final Sort sort = Sort.by("firstName").ascending(); - final List customersPage = - TestUtil.iterableToList(this.customerRepository.findAllByLastName(TestData.LAST_NAME, sort)); - Assertions.assertEquals(2, customersPage.size()); - Assertions.assertEquals(customer2, customersPage.get(0)); - Assertions.assertEquals(customer1, customersPage.get(1)); - } - ); - } - - @Test - void testSortFindByFirstNameAscendingVariant() - { - final Customer customer1 = new Customer(TestData.FIRST_NAME, "B"); - this.customerRepository.save(customer1); - final Customer customer2 = new Customer(TestData.FIRST_NAME, "A"); - this.customerRepository.save(customer2); - - TestUtil.doBeforeAndAfterRestartOfDatastore( - this.configuration, - () -> { - final Sort sort = Sort.by(Sort.Direction.ASC, "lastName"); - final List customersPage = - TestUtil.iterableToList(this.customerRepository.findAll(sort)); - Assertions.assertEquals(2, customersPage.size()); - Assertions.assertEquals(customer2, customersPage.get(0)); - Assertions.assertEquals(customer1, customersPage.get(1)); - } - ); - } - - @Test - void testFindByOrderByLastNameAsc() - { - final Customer customer1 = new Customer(TestData.FIRST_NAME, "B"); - this.customerRepository.save(customer1); - final Customer customer2 = new Customer(TestData.FIRST_NAME, "A"); - this.customerRepository.save(customer2); - - TestUtil.doBeforeAndAfterRestartOfDatastore( - this.configuration, - () -> { - final List customersPage = - TestUtil.iterableToList(this.customerRepository.findByOrderByLastNameAsc()); - Assertions.assertEquals(2, customersPage.size()); - Assertions.assertEquals(customer2, customersPage.get(0)); - Assertions.assertEquals(customer1, customersPage.get(1)); - } - ); - } - - @Test - void testSortFindByLastNameDescending() - { - final Customer customer1 = new Customer("B", TestData.LAST_NAME); - this.customerRepository.save(customer1); - final Customer customer2 = new Customer("A", TestData.LAST_NAME); - this.customerRepository.save(customer2); - - TestUtil.doBeforeAndAfterRestartOfDatastore( - this.configuration, - () -> { - final Sort sort = Sort.by("firstName").descending(); - final List customersPage = - TestUtil.iterableToList(this.customerRepository.findAllByLastName(TestData.LAST_NAME, sort)); - Assertions.assertEquals(2, customersPage.size()); - Assertions.assertEquals(customer1, customersPage.get(0)); - Assertions.assertEquals(customer2, customersPage.get(1)); - } - ); - } - - @Test - void testFindByChildExistingChild() - { - final Child child = new Child(TestData.FIRST_NAME_ALTERNATIVE, TestData.LAST_NAME_ALTERNATIVE); - final CustomerWithChild customer = new CustomerWithChild(TestData.FIRST_NAME, TestData.LAST_NAME, child); - this.customerWithChildRepository.save(customer); - - TestUtil.doBeforeAndAfterRestartOfDatastore( - this.configuration, - () -> { - final Optional foundCustomer = this.customerWithChildRepository.findByChild(child); - Assertions.assertTrue(foundCustomer.isPresent()); - } - ); - } - - @Test - void testFindByChildNewChild() - { - final Child child = new Child(TestData.FIRST_NAME_ALTERNATIVE, TestData.LAST_NAME_ALTERNATIVE); - final CustomerWithChild customer = new CustomerWithChild(TestData.FIRST_NAME, TestData.LAST_NAME, child); - this.customerWithChildRepository.save(customer); - - TestUtil.doBeforeAndAfterRestartOfDatastore( - this.configuration, - () -> { - final Child queryChild = new Child(TestData.FIRST_NAME_ALTERNATIVE, TestData.LAST_NAME_ALTERNATIVE); - final Optional foundCustomer = - this.customerWithChildRepository.findByChild(queryChild); - Assertions.assertTrue(foundCustomer.isPresent()); - } - ); - } - - @Test - void testFindByChildWhenNullNotExists() - { - final Child child = new Child(TestData.FIRST_NAME_ALTERNATIVE, TestData.LAST_NAME_ALTERNATIVE); - final CustomerWithChild customer = new CustomerWithChild(TestData.FIRST_NAME, TestData.LAST_NAME, child); - this.customerWithChildRepository.save(customer); - - TestUtil.doBeforeAndAfterRestartOfDatastore( - this.configuration, - () -> { - final Optional foundCustomer = this.customerWithChildRepository.findByChild(null); - Assertions.assertTrue(foundCustomer.isEmpty()); - } - ); - } - - @Test - void testFindByChildWhenNullExists() - { - final CustomerWithChild customer = new CustomerWithChild(TestData.FIRST_NAME, TestData.LAST_NAME, null); - this.customerWithChildRepository.save(customer); - - TestUtil.doBeforeAndAfterRestartOfDatastore( - this.configuration, - () -> { - final Optional foundCustomer = this.customerWithChildRepository.findByChild(null); - Assertions.assertTrue(foundCustomer.isPresent()); - } - ); - } -} diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/repository/query/EclipseStoreQueryCreatorStringTest.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/repository/query/EclipseStoreQueryCreatorStringTest.java index 905ba506..bcdf402e 100644 --- a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/repository/query/EclipseStoreQueryCreatorStringTest.java +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/repository/query/EclipseStoreQueryCreatorStringTest.java @@ -48,8 +48,8 @@ static Stream generateDataWithCountOfFirstNameLike() return Stream.of( Arguments.of("%", 3), Arguments.of("M%", 2), - Arguments.of("m%", 2), - Arguments.of("m_ck", 2), + Arguments.of("m%", 0), + Arguments.of("m_ck", 0), Arguments.of("%ick", 2), Arguments.of("%k", 2), Arguments.of("%ck%", 2), @@ -93,8 +93,8 @@ static Stream generateDataWithCountOfFirstNameNotLike() return Stream.of( Arguments.of("%", 0), Arguments.of("M%", 1), - Arguments.of("m%", 1), - Arguments.of("m_ck", 1), + Arguments.of("m%", 3), + Arguments.of("m_ck", 3), Arguments.of("%ick", 1), Arguments.of("%k", 1), Arguments.of("%ck%", 1),