Consistently document JpaSpecificationExecutor to require non-null Specification.

Closes #2877
This commit is contained in:
Mark Paluch
2023-03-21 11:45:09 +01:00
parent 8d4c40f5cb
commit b5474bd61b
2 changed files with 63 additions and 61 deletions

View File

@@ -24,7 +24,6 @@ import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.repository.query.FluentQuery;
import org.springframework.lang.Nullable;
/**
* Interface to allow execution of {@link Specification}s based on the JPA criteria API.
@@ -32,65 +31,66 @@ import org.springframework.lang.Nullable;
* @author Oliver Gierke
* @author Christoph Strobl
* @author Diego Krupitza
* @author Mark Paluch
*/
public interface JpaSpecificationExecutor<T> {
/**
* Returns a single entity matching the given {@link Specification} or {@link Optional#empty()} if none found.
*
* @param spec can be {@literal null}.
* @param spec must not be {@literal null}.
* @return never {@literal null}.
* @throws org.springframework.dao.IncorrectResultSizeDataAccessException if more than one entity found.
*/
Optional<T> findOne(@Nullable Specification<T> spec);
Optional<T> findOne(Specification<T> spec);
/**
* Returns all entities matching the given {@link Specification}.
*
* @param spec can be {@literal null}.
* @param spec must not be {@literal null}.
* @return never {@literal null}.
*/
List<T> findAll(@Nullable Specification<T> spec);
List<T> findAll(Specification<T> spec);
/**
* Returns a {@link Page} of entities matching the given {@link Specification}.
*
* @param spec can be {@literal null}.
* @param spec must not be {@literal null}.
* @param pageable must not be {@literal null}.
* @return never {@literal null}.
*/
Page<T> findAll(@Nullable Specification<T> spec, Pageable pageable);
Page<T> findAll(Specification<T> spec, Pageable pageable);
/**
* Returns all entities matching the given {@link Specification} and {@link Sort}.
*
* @param spec can be {@literal null}.
* @param spec must not be {@literal null}.
* @param sort must not be {@literal null}.
* @return never {@literal null}.
*/
List<T> findAll(@Nullable Specification<T> spec, Sort sort);
List<T> findAll(Specification<T> spec, Sort sort);
/**
* Returns the number of instances that the given {@link Specification} will return.
*
* @param spec the {@link Specification} to count instances for. Can be {@literal null}.
* @param spec the {@link Specification} to count instances for, must not be {@literal null}.
* @return the number of instances.
*/
long count(@Nullable Specification<T> spec);
long count(Specification<T> spec);
/**
* Checks whether the data store contains elements that match the given {@link Specification}.
*
* @param spec the {@link Specification} to use for the existence check. Must not be {@literal null}.
* @return <code>true</code> if the data store contains elements that match the given {@link Specification} otherwise
* <code>false</code>.
*
* @param spec the {@link Specification} to use for the existence check, ust not be {@literal null}.
* @return {@code true} if the data store contains elements that match the given {@link Specification} otherwise
* {@code false}.
*/
boolean exists(Specification<T> spec);
/**
* Deletes by the {@link Specification} and returns the number of rows deleted.
*
* @param spec the {@link Specification} to use for the existence check. Must not be {@literal null}.
* @param spec the {@link Specification} to use for the existence check, must not be {@literal null}.
* @return the number of entities deleted
*/
long delete(Specification<T> spec);
@@ -99,8 +99,8 @@ public interface JpaSpecificationExecutor<T> {
* Returns entities matching the given {@link Specification} applying the {@code queryFunction} that defines the query
* and its result type.
*
* @param spec must not be null.
* @param queryFunction the query function defining projection, sorting, and the result type
* @param spec must not be null.
* @param queryFunction the query function defining projection, sorting, and the result type
* @return all entities matching the given Example.
* @since 3.0
*/

View File

@@ -447,7 +447,7 @@ public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T
}
@Override
public Optional<T> findOne(@Nullable Specification<T> spec) {
public Optional<T> findOne(Specification<T> spec) {
try {
return Optional.of(getQuery(spec, Sort.unsorted()).setMaxResults(2).getSingleResult());
@@ -457,12 +457,12 @@ public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T
}
@Override
public List<T> findAll(@Nullable Specification<T> spec) {
public List<T> findAll(Specification<T> spec) {
return getQuery(spec, Sort.unsorted()).getResultList();
}
@Override
public Page<T> findAll(@Nullable Specification<T> spec, Pageable pageable) {
public Page<T> findAll(Specification<T> spec, Pageable pageable) {
TypedQuery<T> query = getQuery(spec, pageable);
return isUnpaged(pageable) ? new PageImpl<>(query.getResultList())
@@ -470,10 +470,51 @@ public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T
}
@Override
public List<T> findAll(@Nullable Specification<T> spec, Sort sort) {
public List<T> findAll(Specification<T> spec, Sort sort) {
return getQuery(spec, sort).getResultList();
}
@Override
public boolean exists(Specification<T> spec) {
CriteriaQuery<Integer> cq = this.em.getCriteriaBuilder().createQuery(Integer.class);
cq.select(this.em.getCriteriaBuilder().literal(1));
applySpecificationToCriteria(spec, getDomainClass(), cq);
TypedQuery<Integer> query = applyRepositoryMethodMetadata(this.em.createQuery(cq));
return query.setMaxResults(1).getResultList().size() == 1;
}
@Override
public long delete(Specification<T> spec) {
CriteriaBuilder builder = this.em.getCriteriaBuilder();
CriteriaDelete<T> delete = builder.createCriteriaDelete(getDomainClass());
if (spec != null) {
Predicate predicate = spec.toPredicate(delete.from(getDomainClass()), null, builder);
if (predicate != null) {
delete.where(predicate);
}
}
return this.em.createQuery(delete).executeUpdate();
}
@Override
public <S extends T, R> R findBy(Specification<T> spec, Function<FetchableFluentQuery<S>, R> queryFunction) {
Assert.notNull(spec, "Specification must not be null");
Assert.notNull(queryFunction, "Query function must not be null");
Function<Sort, TypedQuery<T>> finder = sort -> getQuery(spec, getDomainClass(), sort);
FetchableFluentQuery<R> fluentQuery = new FetchableFluentQueryBySpecification<T, R>(spec, getDomainClass(),
Sort.unsorted(), null, finder, this::count, this::exists, this.em);
return queryFunction.apply((FetchableFluentQuery<S>) fluentQuery);
}
@Override
public <S extends T> Optional<S> findOne(Example<S> example) {
@@ -503,32 +544,6 @@ public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T
return query.setMaxResults(1).getResultList().size() == 1;
}
@Override
public boolean exists(Specification<T> spec) {
CriteriaQuery<Integer> cq = this.em.getCriteriaBuilder().createQuery(Integer.class);
cq.select(this.em.getCriteriaBuilder().literal(1));
applySpecificationToCriteria(spec, getDomainClass(), cq);
TypedQuery<Integer> query = applyRepositoryMethodMetadata(this.em.createQuery(cq));
return query.setMaxResults(1).getResultList().size() == 1;
}
@Override
public long delete(Specification<T> spec) {
CriteriaBuilder builder = this.em.getCriteriaBuilder();
CriteriaDelete<T> delete = builder.createCriteriaDelete(getDomainClass());
if (spec != null) {
Predicate predicate = spec.toPredicate(delete.from(getDomainClass()), null, builder);
if (predicate != null) {
delete.where(predicate);
}
}
return this.em.createQuery(delete).executeUpdate();
}
@Override
public <S extends T> List<S> findAll(Example<S> example) {
@@ -571,19 +586,6 @@ public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T
return queryFunction.apply(fluentQuery);
}
@Override
public <S extends T, R> R findBy(Specification<T> spec, Function<FetchableFluentQuery<S>, R> queryFunction) {
Assert.notNull(spec, "Specification must not be null");
Assert.notNull(queryFunction, "Query function must not be null");
Function<Sort, TypedQuery<T>> finder = sort -> getQuery(spec, getDomainClass(), sort);
FetchableFluentQuery<R> fluentQuery = new FetchableFluentQueryBySpecification<T, R>(spec, getDomainClass(),
Sort.unsorted(), null, finder, this::count, this::exists, this.em);
return queryFunction.apply((FetchableFluentQuery<S>) fluentQuery);
}
@Override
public long count() {