Polishing.

Fix version reference to Spring Data Commons. Avoid stream creation when fetching all/one/first element. Move off deprecated API. Add override comments. Add null guargs.

See #2294
Original pull request: #2326.
This commit is contained in:
Mark Paluch
2021-10-08 11:23:58 +02:00
parent ea430f34aa
commit a31c39db7a
5 changed files with 162 additions and 42 deletions

View File

@@ -25,7 +25,7 @@
<hibernate>5.5.3.Final</hibernate>
<mysql-connector-java>8.0.23</mysql-connector-java>
<postgresql>42.2.19</postgresql>
<springdata.commons>2.6.0-2228-SNAPSHOT</springdata.commons>
<springdata.commons>2.6.0-SNAPSHOT</springdata.commons>
<vavr>0.10.3</vavr>
<hibernate.groupId>org.hibernate</hibernate.groupId>

View File

@@ -15,10 +15,10 @@
*/
package org.springframework.data.jpa.repository.support;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.persistence.EntityManager;
@@ -37,6 +37,7 @@ import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery;
import org.springframework.data.support.PageableExecutionUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* Immutable implementation of {@link FetchableFluentQuery} based on Query by {@link Example}. All methods that return a
@@ -45,6 +46,7 @@ import org.springframework.lang.Nullable;
* @param <S> Domain type
* @param <R> Result type
* @author Greg Turnquist
* @author Mark Paluch
* @since 2.6
*/
class FetchableFluentQueryByExample<S, R> extends FluentQuerySupport<R> implements FetchableFluentQuery<R> {
@@ -79,24 +81,39 @@ class FetchableFluentQueryByExample<S, R> extends FluentQuerySupport<R> implemen
this.escapeCharacter = escapeCharacter;
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#sortBy(org.springframework.data.domain.Sort)
*/
@Override
public FetchableFluentQuery<R> sortBy(Sort sort) {
return new FetchableFluentQueryByExample<S, R>(this.example, this.resultType, this.sort.and(sort), this.properties,
Assert.notNull(sort, "Sort must not be null!");
return new FetchableFluentQueryByExample<>(this.example, this.resultType, this.sort.and(sort), this.properties,
this.finder, this.countOperation, this.existsOperation, this.context, this.entityManager, this.escapeCharacter);
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#as(java.lang.Class)
*/
@Override
public <NR> FetchableFluentQuery<NR> as(Class<NR> resultType) {
Assert.notNull(resultType, "Projection target type must not be null!");
if (!resultType.isInterface()) {
throw new UnsupportedOperationException("Class-based DTOs are not yet supported.");
}
return new FetchableFluentQueryByExample<S, NR>(this.example, resultType, this.sort, this.properties, this.finder,
return new FetchableFluentQueryByExample<>(this.example, resultType, this.sort, this.properties, this.finder,
this.countOperation, this.existsOperation, this.context, this.entityManager, this.escapeCharacter);
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#project(java.util.Collection)
*/
@Override
public FetchableFluentQuery<R> project(Collection<String> properties) {
@@ -104,62 +121,86 @@ class FetchableFluentQueryByExample<S, R> extends FluentQuerySupport<R> implemen
this.finder, this.countOperation, this.existsOperation, this.context, this.entityManager, this.escapeCharacter);
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#oneValue()
*/
@Override
public R oneValue() {
TypedQuery<S> limitedQuery = this.finder.apply(this.sort);
limitedQuery.setMaxResults(2); // Never need more than 2 values
List<R> results = limitedQuery //
.getResultStream() //
.map(getConversionFunction(this.example.getProbeType(), this.resultType)) //
.collect(Collectors.toList());
;
List<S> results = limitedQuery.getResultList();
if (results.size() > 1) {
throw new IncorrectResultSizeDataAccessException(1);
}
return results.isEmpty() ? null : results.get(0);
return results.isEmpty() ? null : getConversionFunction().apply(results.get(0));
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#firstValue()
*/
@Override
public R firstValue() {
TypedQuery<S> limitedQuery = this.finder.apply(this.sort);
limitedQuery.setMaxResults(1); // Never need more than 1 value
List<R> results = limitedQuery //
.getResultStream() //
.map(getConversionFunction(this.example.getProbeType(), this.resultType)) //
.collect(Collectors.toList());
List<S> results = limitedQuery.getResultList();
return results.isEmpty() ? null : results.get(0);
return results.isEmpty() ? null : getConversionFunction().apply(results.get(0));
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#all()
*/
@Override
public List<R> all() {
return stream().collect(Collectors.toList());
List<S> resultList = this.finder.apply(this.sort).getResultList();
return convert(resultList);
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#page(org.springframework.data.domain.Pageable)
*/
@Override
public Page<R> page(Pageable pageable) {
return pageable.isUnpaged() ? new PageImpl<>(all()) : readPage(pageable);
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#stream()
*/
@Override
public Stream<R> stream() {
return this.finder.apply(this.sort) //
.getResultStream() //
.map(getConversionFunction(this.example.getProbeType(), this.resultType));
.map(getConversionFunction());
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#count()
*/
@Override
public long count() {
return this.countOperation.apply(example);
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#exists()
*/
@Override
public boolean exists() {
return this.existsOperation.apply(example);
@@ -174,10 +215,24 @@ class FetchableFluentQueryByExample<S, R> extends FluentQuerySupport<R> implemen
pagedQuery.setMaxResults(pageable.getPageSize());
}
List<R> paginatedResults = pagedQuery.getResultStream() //
.map(getConversionFunction(this.example.getProbeType(), this.resultType)) //
.collect(Collectors.toList());
List<R> paginatedResults = convert(pagedQuery.getResultList());
return PageableExecutionUtils.getPage(paginatedResults, pageable, () -> this.countOperation.apply(this.example));
}
private List<R> convert(List<S> resultList) {
Function<Object, R> conversionFunction = getConversionFunction();
List<R> mapped = new ArrayList<>(resultList.size());
for (S s : resultList) {
mapped.add(conversionFunction.apply(s));
}
return mapped;
}
private Function<Object, R> getConversionFunction() {
return getConversionFunction(this.example.getProbeType(), this.resultType);
}
}

View File

@@ -15,11 +15,11 @@
*/
package org.springframework.data.jpa.repository.support;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
@@ -33,6 +33,7 @@ import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery;
import org.springframework.data.support.PageableExecutionUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import com.querydsl.core.types.Predicate;
import com.querydsl.jpa.JPQLQuery;
@@ -44,6 +45,7 @@ import com.querydsl.jpa.JPQLQuery;
* @param <S> Domain type
* @param <R> Result type
* @author Greg Turnquist
* @author Mark Paluch
* @since 2.6
*/
class FetchableFluentQueryByPredicate<S, R> extends FluentQuerySupport<R> implements FetchableFluentQuery<R> {
@@ -78,16 +80,27 @@ class FetchableFluentQueryByPredicate<S, R> extends FluentQuerySupport<R> implem
this.entityType = entityType;
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#sortBy(org.springframework.data.domain.Sort)
*/
@Override
public FetchableFluentQuery<R> sortBy(Sort sort) {
Assert.notNull(sort, "Sort must not be null!");
return new FetchableFluentQueryByPredicate<>(this.predicate, this.resultType, this.sort.and(sort), this.properties,
this.finder, this.pagedFinder, this.countOperation, this.existsOperation, this.entityType, this.context);
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#as(java.lang.Class)
*/
@Override
public <NR> FetchableFluentQuery<NR> as(Class<NR> resultType) {
Assert.notNull(resultType, "Projection target type must not be null!");
if (!resultType.isInterface()) {
throw new UnsupportedOperationException("Class-based DTOs are not yet supported.");
}
@@ -96,6 +109,10 @@ class FetchableFluentQueryByPredicate<S, R> extends FluentQuerySupport<R> implem
this.pagedFinder, this.countOperation, this.existsOperation, this.entityType, this.context);
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#project(java.util.Collection)
*/
@Override
public FetchableFluentQuery<R> project(Collection<String> properties) {
@@ -104,57 +121,83 @@ class FetchableFluentQueryByPredicate<S, R> extends FluentQuerySupport<R> implem
this.entityType, this.context);
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#oneValue()
*/
@Override
public R oneValue() {
List<R> results = this.finder.apply(this.sort) //
List<S> results = this.finder.apply(this.sort) //
.limit(2) // Never need more than 2 values
.stream() //
.map(getConversionFunction(this.entityType, this.resultType)) //
.collect(Collectors.toList());
.fetch();
if (results.size() > 1) {
throw new IncorrectResultSizeDataAccessException(1);
}
return results.isEmpty() ? null : results.get(0);
return results.isEmpty() ? null : getConversionFunction().apply(results.get(0));
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#firstValue()
*/
@Override
public R firstValue() {
List<R> results = this.finder.apply(this.sort) //
List<S> results = this.finder.apply(this.sort) //
.limit(1) // Never need more than 1 value
.stream() //
.map(getConversionFunction(this.entityType, this.resultType)) //
.collect(Collectors.toList());
.fetch();
return results.isEmpty() ? null : results.get(0);
return results.isEmpty() ? null : getConversionFunction().apply(results.get(0));
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#all()
*/
@Override
public List<R> all() {
return stream().collect(Collectors.toList());
JPQLQuery<S> query = this.finder.apply(this.sort);
return convert(query.fetch());
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#page(org.springframework.data.domain.Pageable)
*/
@Override
public Page<R> page(Pageable pageable) {
return pageable.isUnpaged() ? new PageImpl<>(all()) : readPage(pageable);
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#stream()
*/
@Override
public Stream<R> stream() {
return this.finder.apply(this.sort) //
.stream() //
.map(getConversionFunction(this.entityType, this.resultType));
.map(getConversionFunction());
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#count()
*/
@Override
public long count() {
return this.countOperation.apply(this.predicate);
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#exists()
*/
@Override
public boolean exists() {
return this.existsOperation.apply(this.predicate);
@@ -163,11 +206,25 @@ class FetchableFluentQueryByPredicate<S, R> extends FluentQuerySupport<R> implem
private Page<R> readPage(Pageable pageable) {
JPQLQuery<S> pagedQuery = this.pagedFinder.apply(this.sort, pageable);
List<R> paginatedResults = pagedQuery.stream() //
.map(getConversionFunction(this.entityType, this.resultType)) //
.collect(Collectors.toList());
List<R> paginatedResults = convert(pagedQuery.fetch());
return PageableExecutionUtils.getPage(paginatedResults, pageable, () -> this.countOperation.apply(this.predicate));
}
private List<R> convert(List<S> resultList) {
Function<Object, R> conversionFunction = getConversionFunction();
List<R> mapped = new ArrayList<>(resultList.size());
for (S s : resultList) {
mapped.add(conversionFunction.apply(s));
}
return mapped;
}
private Function<Object, R> getConversionFunction() {
return getConversionFunction(this.entityType, this.resultType);
}
}

View File

@@ -34,7 +34,7 @@ import org.springframework.data.querydsl.EntityPathResolver;
import org.springframework.data.querydsl.QSort;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery;
import org.springframework.data.repository.support.PageableExecutionUtils;
import org.springframework.data.support.PageableExecutionUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
@@ -167,11 +167,16 @@ public class QuerydslJpaPredicateExecutor<T> implements QuerydslPredicateExecuto
return PageableExecutionUtils.getPage(query.fetch(), pageable, countQuery::fetchCount);
}
/*
* (non-Javadoc)
* @see org.springframework.data.querydsl.QuerydslPredicateExecutor#findBy(com.querydsl.core.types.Predicate, java.util.function.Function)
*/
@SuppressWarnings("unchecked")
@Override
public <S extends T, R> R findBy(Predicate predicate, Function<FetchableFluentQuery<S>, R> queryFunction) {
Assert.notNull(predicate, "Predicate must not be null!");
Assert.notNull(queryFunction, "Function must not be null!");
Assert.notNull(queryFunction, "Query function must not be null!");
Function<Sort, JPQLQuery<T>> finder = sort -> {
JPQLQuery<T> select = createQuery(predicate).select(path);
@@ -194,12 +199,12 @@ public class QuerydslJpaPredicateExecutor<T> implements QuerydslPredicateExecuto
return select;
};
FetchableFluentQuery<S> fluentQuery = (FetchableFluentQuery<S>) new FetchableFluentQueryByPredicate<>(predicate,
FetchableFluentQueryByPredicate<T, T> fluentQuery = new FetchableFluentQueryByPredicate<>(predicate,
entityInformation.getJavaType(), finder, pagedFinder, this::count, this::exists,
this.entityInformation.getJavaType(),
new JpaMetamodelMappingContext(Collections.singleton(this.entityManager.getMetamodel())));
return queryFunction.apply(fluentQuery);
return queryFunction.apply((FetchableFluentQuery<S>) fluentQuery);
}
/*

View File

@@ -58,7 +58,7 @@ import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery;
import org.springframework.data.repository.support.PageableExecutionUtils;
import org.springframework.data.support.PageableExecutionUtils;
import org.springframework.data.util.ProxyUtils;
import org.springframework.data.util.Streamable;
import org.springframework.lang.Nullable;
@@ -583,6 +583,9 @@ public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T
@Override
public <S extends T, R> R findBy(Example<S> example, Function<FetchableFluentQuery<S>, R> queryFunction) {
Assert.notNull(example, "Sample must not be null!");
Assert.notNull(queryFunction, "Query function must not be null!");
Function<Sort, TypedQuery<S>> finder = sort -> {
ExampleSpecification<S> spec = new ExampleSpecification<>(example, escapeCharacter);