From a80ea1517286dabdab331b0046f023f589007f3c Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Mon, 24 Jan 2022 13:27:09 +0100 Subject: [PATCH 1/2] 2535-list-interfaces - Prepare branch --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a43ca5f89b..72024be032 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-commons - 3.0.0-SNAPSHOT + 3.0.0-2535-list-interfaces-SNAPSHOT Spring Data Core From 92fdc9efe8bca03e8404563fde3076c7e81a073d Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Mon, 24 Jan 2022 14:50:34 +0100 Subject: [PATCH 2/2] Adds List based repository interfaces. This introduces the interfaces * `ListCrudRepository` * `ListQuerydslPredicateExecutor` * `ListQueryByExampleExecutor` They return `List` instead of `Iterable` when returning multiple entities. They can be used in the same way the interfaces without the `List` prefix can be used. Closes #2535. --- src/main/asciidoc/repositories.adoc | 4 +- .../ListQuerydslPredicateExecutor.java | 123 ++++++++++++++++ .../data/repository/ListCrudRepository.java | 135 ++++++++++++++++++ .../query/ListQueryByExampleExecutor.java | 101 +++++++++++++ 4 files changed, 362 insertions(+), 1 deletion(-) create mode 100644 src/main/java/org/springframework/data/querydsl/ListQuerydslPredicateExecutor.java create mode 100644 src/main/java/org/springframework/data/repository/ListCrudRepository.java create mode 100644 src/main/java/org/springframework/data/repository/query/ListQueryByExampleExecutor.java diff --git a/src/main/asciidoc/repositories.adoc b/src/main/asciidoc/repositories.adoc index d1ab4600c8..9d74dc1e6e 100644 --- a/src/main/asciidoc/repositories.adoc +++ b/src/main/asciidoc/repositories.adoc @@ -23,7 +23,7 @@ For detailed information on the specific features of your module, see the chapte The central interface in the Spring Data repository abstraction is `Repository`. It takes the domain class to manage as well as the ID type of the domain class as type arguments. This interface acts primarily as a marker interface to capture the types to work with and to help you to discover interfaces that extend this one. -The https://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/repository/CrudRepository.html[`CrudRepository`] interface provides sophisticated CRUD functionality for the entity class that is being managed. +The https://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/repository/CrudRepository.html[`CrudRepository`] and https://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/repository/ListCrudRepository.html[`ListCrudRepository`] interfaces provide sophisticated CRUD functionality for the entity class that is being managed. [[repositories.repository]] .`CrudRepository` Interface @@ -55,6 +55,8 @@ public interface CrudRepository extends Repository { <6> Indicates whether an entity with the given ID exists. ==== +`ListCrudRepository` offers equivalent methods, but they return `List` where the `CrudRepository` methods return an `Iterable`. + NOTE: We also provide persistence technology-specific abstractions, such as `JpaRepository` or `MongoRepository`. Those interfaces extend `CrudRepository` and expose the capabilities of the underlying persistence technology in addition to the rather generic persistence technology-agnostic interfaces such as `CrudRepository`. diff --git a/src/main/java/org/springframework/data/querydsl/ListQuerydslPredicateExecutor.java b/src/main/java/org/springframework/data/querydsl/ListQuerydslPredicateExecutor.java new file mode 100644 index 0000000000..c2fb073a07 --- /dev/null +++ b/src/main/java/org/springframework/data/querydsl/ListQuerydslPredicateExecutor.java @@ -0,0 +1,123 @@ +/* + * Copyright 2022 the original author or authors. + * + * 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 + * + * https://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 org.springframework.data.querydsl; + +import java.util.List; +import java.util.Optional; +import java.util.function.Function; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.repository.query.FluentQuery; + +import com.querydsl.core.types.OrderSpecifier; +import com.querydsl.core.types.Predicate; + +/** + * Interface to allow execution of QueryDsl {@link Predicate} instances.This identical to * + * {@link QuerydslPredicateExecutor} but returns {@link List} instead of {@link Iterable} where applicable. + * + * @author Jens Schauder + * @since 3.0 + * @see QuerydslPredicateExecutor + */ +public interface ListQuerydslPredicateExecutor { + + /** + * Returns a single entity matching the given {@link Predicate} or {@link Optional#empty()} if none was found. + * + * @param predicate must not be {@literal null}. + * @return a single entity matching the given {@link Predicate} or {@link Optional#empty()} if none was found. + * @throws org.springframework.dao.IncorrectResultSizeDataAccessException if the predicate yields more than one + * result. + */ + Optional findOne(Predicate predicate); + + /** + * Returns all entities matching the given {@link Predicate}. In case no match could be found an empty {@link List} is + * returned. + * + * @param predicate must not be {@literal null}. + * @return all entities matching the given {@link Predicate}. + */ + List findAll(Predicate predicate); + + /** + * Returns all entities matching the given {@link Predicate} applying the given {@link Sort}. In case no match could + * be found an empty {@link List} is returned. + * + * @param predicate must not be {@literal null}. + * @param sort the {@link Sort} specification to sort the results by, may be {@link Sort#unsorted()}, must not be + * {@literal null}. + * @return all entities matching the given {@link Predicate}. + */ + List findAll(Predicate predicate, Sort sort); + + /** + * Returns all entities matching the given {@link Predicate} applying the given {@link OrderSpecifier}s. In case no + * match could be found an empty {@link List} is returned. + * + * @param predicate must not be {@literal null}. + * @param orders the {@link OrderSpecifier}s to sort the results by, must not be {@literal null}. + * @return all entities matching the given {@link Predicate} applying the given {@link OrderSpecifier}s. + */ + List findAll(Predicate predicate, OrderSpecifier... orders); + + /** + * Returns all entities ordered by the given {@link OrderSpecifier}s. + * + * @param orders the {@link OrderSpecifier}s to sort the results by, must not be {@literal null}. + * @return all entities ordered by the given {@link OrderSpecifier}s. + */ + List findAll(OrderSpecifier... orders); + + /** + * Returns a {@link Page} of entities matching the given {@link Predicate}. In case no match could be found, an empty + * {@link Page} is returned. + * + * @param predicate must not be {@literal null}. + * @param pageable may be {@link Pageable#unpaged()}, must not be {@literal null}. + * @return a {@link Page} of entities matching the given {@link Predicate}. + */ + Page findAll(Predicate predicate, Pageable pageable); + + /** + * Returns the number of instances matching the given {@link Predicate}. + * + * @param predicate the {@link Predicate} to count instances for, must not be {@literal null}. + * @return the number of instances matching the {@link Predicate}. + */ + long count(Predicate predicate); + + /** + * Checks whether the data store contains elements that match the given {@link Predicate}. + * + * @param predicate the {@link Predicate} to use for the existence check, must not be {@literal null}. + * @return {@literal true} if the data store contains elements that match the given {@link Predicate}. + */ + boolean exists(Predicate predicate); + + /** + * Returns entities matching the given {@link Predicate} applying the {@link Function queryFunction} that defines the + * query and its result type. + * + * @param predicate must not be {@literal null}. + * @param queryFunction the query function defining projection, sorting, and the result type + * @return all entities matching the given {@link Predicate}. + */ + R findBy(Predicate predicate, Function, R> queryFunction); +} diff --git a/src/main/java/org/springframework/data/repository/ListCrudRepository.java b/src/main/java/org/springframework/data/repository/ListCrudRepository.java new file mode 100644 index 0000000000..e58cb15879 --- /dev/null +++ b/src/main/java/org/springframework/data/repository/ListCrudRepository.java @@ -0,0 +1,135 @@ +/* + * Copyright 2022 the original author or authors. + * + * 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 + * + * https://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 org.springframework.data.repository; + +import java.util.List; +import java.util.Optional; + +/** + * Interface for generic CRUD operations on a repository for a specific type. This identical to {@link CrudRepository} + * but returns {@link List} instead of {@link Iterable} where applicable. + * + * @author Jens Schauder + * @see CrudRepository + * @since 3.0 + */ +@NoRepositoryBean +public interface ListCrudRepository extends Repository { + + /** + * Saves a given entity. Use the returned instance for further operations as the save operation might have changed the + * entity instance completely. + * + * @param entity must not be {@literal null}. + * @return the saved entity; will never be {@literal null}. + * @throws IllegalArgumentException in case the given {@literal entity} is {@literal null}. + */ + S save(S entity); + + /** + * Saves all given entities. + * + * @param entities must not be {@literal null} nor must it contain {@literal null}. + * @return the saved entities; will never be {@literal null}. The returned {@literal Iterable} will have the same size + * as the {@literal Iterable} passed as an argument. + * @throws IllegalArgumentException in case the given {@link Iterable entities} or one of its entities is + * {@literal null}. + */ + List saveAll(Iterable entities); + + /** + * Retrieves an entity by its id. + * + * @param id must not be {@literal null}. + * @return the entity with the given id or {@literal Optional#empty()} if none found. + * @throws IllegalArgumentException if {@literal id} is {@literal null}. + */ + Optional findById(ID id); + + /** + * Returns whether an entity with the given id exists. + * + * @param id must not be {@literal null}. + * @return {@literal true} if an entity with the given id exists, {@literal false} otherwise. + * @throws IllegalArgumentException if {@literal id} is {@literal null}. + */ + boolean existsById(ID id); + + /** + * Returns all instances of the type. + * + * @return all entities + */ + List findAll(); + + /** + * Returns all instances of the type {@code T} with the given IDs. + *

+ * If some or all ids are not found, no entities are returned for these IDs. + *

+ * Note that the order of elements in the result is not guaranteed. + * + * @param ids must not be {@literal null} nor contain any {@literal null} values. + * @return guaranteed to be not {@literal null}. The size can be equal or less than the number of given + * {@literal ids}. + * @throws IllegalArgumentException in case the given {@link Iterable ids} or one of its items is {@literal null}. + */ + List findAllById(Iterable ids); + + /** + * Returns the number of entities available. + * + * @return the number of entities. + */ + long count(); + + /** + * Deletes the entity with the given id. + * + * @param id must not be {@literal null}. + * @throws IllegalArgumentException in case the given {@literal id} is {@literal null} + */ + void deleteById(ID id); + + /** + * Deletes a given entity. + * + * @param entity must not be {@literal null}. + * @throws IllegalArgumentException in case the given entity is {@literal null}. + */ + void delete(T entity); + + /** + * Deletes all instances of the type {@code T} with the given IDs. + * + * @param ids must not be {@literal null}. Must not contain {@literal null} elements. + * @throws IllegalArgumentException in case the given {@literal ids} or one of its elements is {@literal null}. + */ + void deleteAllById(Iterable ids); + + /** + * Deletes the given entities. + * + * @param entities must not be {@literal null}. Must not contain {@literal null} elements. + * @throws IllegalArgumentException in case the given {@literal entities} or one of its entities is {@literal null}. + */ + void deleteAll(Iterable entities); + + /** + * Deletes all entities managed by the repository. + */ + void deleteAll(); +} diff --git a/src/main/java/org/springframework/data/repository/query/ListQueryByExampleExecutor.java b/src/main/java/org/springframework/data/repository/query/ListQueryByExampleExecutor.java new file mode 100644 index 0000000000..375265d5e9 --- /dev/null +++ b/src/main/java/org/springframework/data/repository/query/ListQueryByExampleExecutor.java @@ -0,0 +1,101 @@ +/* + * Copyright 2022 the original author or authors. + * + * 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 + * + * https://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 org.springframework.data.repository.query; + +import java.util.List; +import java.util.Optional; +import java.util.function.Function; + +import org.springframework.data.domain.Example; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; + +/** + * Interface to allow execution of Query by Example {@link Example} instances. This identical to + * {@link QueryByExampleExecutor} but returns {@link List} instead of {@link Iterable} where applicable. + * + * @param the type of entity for which this executor acts on. + * @author Jens Schauder + * @since 3.0 + * @see QueryByExampleExecutor + */ +public interface ListQueryByExampleExecutor { + + /** + * Returns a single entity matching the given {@link Example} or {@link Optional#empty()} if none was found. + * + * @param example must not be {@literal null}. + * @return a single entity matching the given {@link Example} or {@link Optional#empty()} if none was found. + * @throws org.springframework.dao.IncorrectResultSizeDataAccessException if the Example yields more than one result. + */ + Optional findOne(Example example); + + /** + * Returns all entities matching the given {@link Example}. In case no match could be found an empty {@link List} is + * returned. + * + * @param example must not be {@literal null}. + * @return all entities matching the given {@link Example}. + */ + List findAll(Example example); + + /** + * Returns all entities matching the given {@link Example} applying the given {@link Sort}. In case no match could be + * found an empty {@link List} is returned. + * + * @param example must not be {@literal null}. + * @param sort the {@link Sort} specification to sort the results by, must not be {@literal null}. + * @return all entities matching the given {@link Example}. + */ + List findAll(Example example, Sort sort); + + /** + * Returns a {@link Page} of entities matching the given {@link Example}. In case no match could be found, an empty + * {@link Page} is returned. + * + * @param example must not be {@literal null}. + * @param pageable can be {@literal null}. + * @return a {@link Page} of entities matching the given {@link Example}. + */ + Page findAll(Example example, Pageable pageable); + + /** + * Returns the number of instances matching the given {@link Example}. + * + * @param example the {@link Example} to count instances for. Must not be {@literal null}. + * @return the number of instances matching the {@link Example}. + */ + long count(Example example); + + /** + * Checks whether the data store contains elements that match the given {@link Example}. + * + * @param example the {@link Example} to use for the existence check. Must not be {@literal null}. + * @return {@literal true} if the data store contains elements that match the given {@link Example}. + */ + boolean exists(Example example); + + /** + * Returns entities matching the given {@link Example} applying the {@link Function queryFunction} that defines the + * query and its result type. + * + * @param example must not be {@literal null}. + * @param queryFunction the query function defining projection, sorting, and the result type + * @return all entities matching the given {@link Example}. + */ + R findBy(Example example, Function, R> queryFunction); +}