Extract QuerydslPredicateExecutor to its own fragment.

Closes #398
This commit is contained in:
Mark Paluch
2021-10-11 10:26:28 +02:00
parent 44ab6db763
commit 201b957f9e
4 changed files with 284 additions and 101 deletions

View File

@@ -16,12 +16,14 @@
package org.springframework.data.keyvalue.repository.support;
import static org.springframework.data.querydsl.QuerydslUtils.*;
import static org.springframework.data.repository.core.support.RepositoryComposition.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Optional;
import org.springframework.beans.BeanUtils;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.keyvalue.core.KeyValueOperations;
import org.springframework.data.keyvalue.repository.query.KeyValuePartTreeQuery;
import org.springframework.data.keyvalue.repository.query.SpelQueryCreator;
@@ -136,8 +138,43 @@ public class KeyValueRepositoryFactory extends RepositoryFactorySupport {
*/
@Override
protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
return isQueryDslRepository(metadata.getRepositoryInterface()) ? QuerydslKeyValueRepository.class
: SimpleKeyValueRepository.class;
return SimpleKeyValueRepository.class;
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getRepositoryFragments(org.springframework.data.repository.core.RepositoryMetadata)
*/
@Override
protected RepositoryFragments getRepositoryFragments(RepositoryMetadata metadata) {
return getRepositoryFragments(metadata, keyValueOperations);
}
/**
* Creates {@link RepositoryFragments} based on {@link RepositoryMetadata} to add Key-Value-specific extensions.
* Typically adds a {@link QuerydslMongoPredicateExecutor} if the repository interface uses Querydsl.
* <p>
* Can be overridden by subclasses to customize {@link RepositoryFragments}.
*
* @param metadata repository metadata.
* @param operations the MongoDB operations manager.
* @return
* @since 2.6
*/
protected RepositoryFragments getRepositoryFragments(RepositoryMetadata metadata, KeyValueOperations operations) {
if (isQueryDslRepository(metadata.getRepositoryInterface())) {
if (metadata.isReactiveRepository()) {
throw new InvalidDataAccessApiUsageException(
"Cannot combine Querydsl and reactive repository support in a single interface");
}
return RepositoryFragments
.just(new QuerydslKeyValuePredicateExecutor<>(getEntityInformation(metadata.getDomainType()), operations));
}
return RepositoryFragments.empty();
}
/**

View File

@@ -0,0 +1,231 @@
/*
* Copyright 2021 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.keyvalue.repository.support;
import static org.springframework.data.keyvalue.repository.support.KeyValueQuerydslUtils.*;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Supplier;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.keyvalue.core.KeyValueOperations;
import org.springframework.data.querydsl.EntityPathResolver;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import org.springframework.data.querydsl.SimpleEntityPathResolver;
import org.springframework.data.repository.core.EntityInformation;
import org.springframework.data.repository.query.FluentQuery;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import com.querydsl.collections.AbstractCollQuery;
import com.querydsl.collections.CollQuery;
import com.querydsl.core.NonUniqueResultException;
import com.querydsl.core.types.EntityPath;
import com.querydsl.core.types.OrderSpecifier;
import com.querydsl.core.types.Predicate;
import com.querydsl.core.types.dsl.PathBuilder;
/**
* {@link QuerydslPredicateExecutor} capable of applying {@link Predicate}s using {@link CollQuery}.
*
* @author Mark Paluch
* @since 2.6
*/
public class QuerydslKeyValuePredicateExecutor<T> implements QuerydslPredicateExecutor<T> {
private static final EntityPathResolver DEFAULT_ENTITY_PATH_RESOLVER = SimpleEntityPathResolver.INSTANCE;
private final PathBuilder<T> builder;
private final Supplier<Iterable<T>> findAll;
/**
* Creates a new {@link QuerydslKeyValuePredicateExecutor} for the given {@link EntityInformation}.
*
* @param entityInformation must not be {@literal null}.
* @param operations must not be {@literal null}.
*/
public QuerydslKeyValuePredicateExecutor(EntityInformation<T, ?> entityInformation, KeyValueOperations operations) {
this(entityInformation, operations, DEFAULT_ENTITY_PATH_RESOLVER);
}
/**
* Creates a new {@link QuerydslKeyValuePredicateExecutor} for the given {@link EntityInformation}, and
* {@link EntityPathResolver}.
*
* @param entityInformation must not be {@literal null}.
* @param operations must not be {@literal null}.
* @param resolver must not be {@literal null}.
*/
public QuerydslKeyValuePredicateExecutor(EntityInformation<T, ?> entityInformation, KeyValueOperations operations,
EntityPathResolver resolver) {
Assert.notNull(resolver, "EntityPathResolver must not be null!");
EntityPath<T> path = resolver.createPath(entityInformation.getJavaType());
this.builder = new PathBuilder<>(path.getType(), path.getMetadata());
findAll = () -> operations.findAll(entityInformation.getJavaType());
}
/*
* (non-Javadoc)
* @see org.springframework.data.querydsl.QueryDslPredicateExecutor#findOne(com.querydsl.core.types.Predicate)
*/
@Override
public Optional<T> findOne(Predicate predicate) {
Assert.notNull(predicate, "Predicate must not be null!");
try {
return Optional.ofNullable(prepareQuery(predicate).fetchOne());
} catch (NonUniqueResultException o_O) {
throw new IncorrectResultSizeDataAccessException("Expected one or no result but found more than one!", 1, o_O);
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.querydsl.QueryDslPredicateExecutor#findAll(com.querydsl.core.types.Predicate)
*/
@Override
public Iterable<T> findAll(Predicate predicate) {
Assert.notNull(predicate, "Predicate must not be null!");
return prepareQuery(predicate).fetchResults().getResults();
}
/*
* (non-Javadoc)
* @see org.springframework.data.querydsl.QueryDslPredicateExecutor#findAll(com.querydsl.core.types.Predicate, com.querydsl.core.types.OrderSpecifier[])
*/
@Override
public Iterable<T> findAll(Predicate predicate, OrderSpecifier<?>... orders) {
Assert.notNull(predicate, "Predicate must not be null!");
Assert.notNull(orders, "OrderSpecifiers must not be null!");
AbstractCollQuery<T, ?> query = prepareQuery(predicate);
query.orderBy(orders);
return query.fetchResults().getResults();
}
/*
* (non-Javadoc)
* @see org.springframework.data.querydsl.QueryDslPredicateExecutor#findAll(com.querydsl.core.types.Predicate, org.springframework.data.domain.Sort)
*/
@Override
public Iterable<T> findAll(Predicate predicate, Sort sort) {
Assert.notNull(predicate, "Predicate must not be null!");
Assert.notNull(sort, "Sort must not be null!");
return findAll(predicate, toOrderSpecifier(sort, builder));
}
/*
* (non-Javadoc)
* @see org.springframework.data.querydsl.QueryDslPredicateExecutor#findAll(com.querydsl.core.types.Predicate, org.springframework.data.domain.Pageable)
*/
@Override
public Page<T> findAll(Predicate predicate, Pageable pageable) {
Assert.notNull(predicate, "Predicate must not be null!");
Assert.notNull(pageable, "Pageable must not be null!");
AbstractCollQuery<T, ?> query = prepareQuery(predicate);
if (pageable.isPaged() || pageable.getSort().isSorted()) {
query.offset(pageable.getOffset());
query.limit(pageable.getPageSize());
if (pageable.getSort().isSorted()) {
query.orderBy(toOrderSpecifier(pageable.getSort(), builder));
}
}
return new PageImpl<>(query.fetchResults().getResults(), pageable, count(predicate));
}
/*
* (non-Javadoc)
* @see org.springframework.data.querydsl.QueryDslPredicateExecutor#findAll(com.querydsl.core.types.OrderSpecifier[])
*/
@Override
public Iterable<T> findAll(OrderSpecifier<?>... orders) {
Assert.notNull(orders, "OrderSpecifiers must not be null!");
if (orders.length == 0) {
return findAll.get();
}
AbstractCollQuery<T, ?> query = prepareQuery(null);
query.orderBy(orders);
return query.fetchResults().getResults();
}
/*
* (non-Javadoc)
* @see org.springframework.data.querydsl.QueryDslPredicateExecutor#count(com.querydsl.core.types.Predicate)
*/
@Override
public long count(Predicate predicate) {
Assert.notNull(predicate, "Predicate must not be null!");
return prepareQuery(predicate).fetchCount();
}
/*
* (non-Javadoc)
* @see org.springframework.data.querydsl.QueryDslPredicateExecutor#exists(com.querydsl.core.types.Predicate)
*/
@Override
public boolean exists(Predicate predicate) {
Assert.notNull(predicate, "Predicate must not be null!");
return count(predicate) > 0;
}
@Override
public <S extends T, R> R findBy(Predicate predicate,
Function<FluentQuery.FetchableFluentQuery<S>, R> queryFunction) {
throw new UnsupportedOperationException("Not yet supported");
}
/**
* Creates executable query for given {@link Predicate}.
*
* @param predicate
* @return
*/
protected AbstractCollQuery<T, ?> prepareQuery(@Nullable Predicate predicate) {
CollQuery<T> query = new CollQuery<>();
query.from(builder, findAll.get());
return predicate != null ? query.where(predicate) : query;
}
}

View File

@@ -15,14 +15,10 @@
*/
package org.springframework.data.keyvalue.repository.support;
import static org.springframework.data.keyvalue.repository.support.KeyValueQuerydslUtils.*;
import java.util.Optional;
import java.util.function.Function;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.keyvalue.core.KeyValueOperations;
@@ -32,17 +28,11 @@ import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import org.springframework.data.querydsl.SimpleEntityPathResolver;
import org.springframework.data.repository.core.EntityInformation;
import org.springframework.data.repository.query.FluentQuery;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import com.querydsl.collections.AbstractCollQuery;
import com.querydsl.collections.CollQuery;
import com.querydsl.core.NonUniqueResultException;
import com.querydsl.core.types.EntityPath;
import com.querydsl.core.types.OrderSpecifier;
import com.querydsl.core.types.Predicate;
import com.querydsl.core.types.dsl.PathBuilder;
/**
* {@link KeyValueRepository} implementation capable of executing {@link Predicate}s using {@link CollQuery}.
@@ -53,13 +43,13 @@ import com.querydsl.core.types.dsl.PathBuilder;
* @author Mark Paluch
* @param <T> the domain type to manage
* @param <ID> the identifier type of the domain type
* @deprecated since 2.6, use {@link QuerydslKeyValuePredicateExecutor} instead.
*/
@Deprecated
public class QuerydslKeyValueRepository<T, ID> extends SimpleKeyValueRepository<T, ID>
implements QuerydslPredicateExecutor<T> {
private static final EntityPathResolver DEFAULT_ENTITY_PATH_RESOLVER = SimpleEntityPathResolver.INSTANCE;
private final PathBuilder<T> builder;
private final QuerydslKeyValuePredicateExecutor<T> executor;
/**
* Creates a new {@link QuerydslKeyValueRepository} for the given {@link EntityInformation} and
@@ -69,7 +59,7 @@ public class QuerydslKeyValueRepository<T, ID> extends SimpleKeyValueRepository<
* @param operations must not be {@literal null}.
*/
public QuerydslKeyValueRepository(EntityInformation<T, ID> entityInformation, KeyValueOperations operations) {
this(entityInformation, operations, DEFAULT_ENTITY_PATH_RESOLVER);
this(entityInformation, operations, SimpleEntityPathResolver.INSTANCE);
}
/**
@@ -87,8 +77,7 @@ public class QuerydslKeyValueRepository<T, ID> extends SimpleKeyValueRepository<
Assert.notNull(resolver, "EntityPathResolver must not be null!");
EntityPath<T> path = resolver.createPath(entityInformation.getJavaType());
this.builder = new PathBuilder<>(path.getType(), path.getMetadata());
this.executor = new QuerydslKeyValuePredicateExecutor<>(entityInformation, operations, resolver);
}
/*
@@ -97,14 +86,7 @@ public class QuerydslKeyValueRepository<T, ID> extends SimpleKeyValueRepository<
*/
@Override
public Optional<T> findOne(Predicate predicate) {
Assert.notNull(predicate, "Predicate must not be null!");
try {
return Optional.ofNullable(prepareQuery(predicate).fetchOne());
} catch (NonUniqueResultException o_O) {
throw new IncorrectResultSizeDataAccessException("Expected one or no result but found more than one!", 1, o_O);
}
return executor.findOne(predicate);
}
/*
@@ -113,10 +95,7 @@ public class QuerydslKeyValueRepository<T, ID> extends SimpleKeyValueRepository<
*/
@Override
public Iterable<T> findAll(Predicate predicate) {
Assert.notNull(predicate, "Predicate must not be null!");
return prepareQuery(predicate).fetchResults().getResults();
return executor.findAll(predicate);
}
/*
@@ -125,14 +104,7 @@ public class QuerydslKeyValueRepository<T, ID> extends SimpleKeyValueRepository<
*/
@Override
public Iterable<T> findAll(Predicate predicate, OrderSpecifier<?>... orders) {
Assert.notNull(predicate, "Predicate must not be null!");
Assert.notNull(orders, "OrderSpecifiers must not be null!");
AbstractCollQuery<T, ?> query = prepareQuery(predicate);
query.orderBy(orders);
return query.fetchResults().getResults();
return executor.findAll(predicate, orders);
}
/*
@@ -141,11 +113,7 @@ public class QuerydslKeyValueRepository<T, ID> extends SimpleKeyValueRepository<
*/
@Override
public Iterable<T> findAll(Predicate predicate, Sort sort) {
Assert.notNull(predicate, "Predicate must not be null!");
Assert.notNull(sort, "Sort must not be null!");
return findAll(predicate, toOrderSpecifier(sort, builder));
return executor.findAll(predicate, sort);
}
/*
@@ -154,23 +122,7 @@ public class QuerydslKeyValueRepository<T, ID> extends SimpleKeyValueRepository<
*/
@Override
public Page<T> findAll(Predicate predicate, Pageable pageable) {
Assert.notNull(predicate, "Predicate must not be null!");
Assert.notNull(pageable, "Pageable must not be null!");
AbstractCollQuery<T, ?> query = prepareQuery(predicate);
if (pageable.isPaged() || pageable.getSort().isSorted()) {
query.offset(pageable.getOffset());
query.limit(pageable.getPageSize());
if (pageable.getSort().isSorted()) {
query.orderBy(toOrderSpecifier(pageable.getSort(), builder));
}
}
return new PageImpl<>(query.fetchResults().getResults(), pageable, count(predicate));
return executor.findAll(predicate, pageable);
}
/*
@@ -179,17 +131,7 @@ public class QuerydslKeyValueRepository<T, ID> extends SimpleKeyValueRepository<
*/
@Override
public Iterable<T> findAll(OrderSpecifier<?>... orders) {
Assert.notNull(orders, "OrderSpecifiers must not be null!");
if (ObjectUtils.isEmpty(orders)) {
return findAll();
}
AbstractCollQuery<T, ?> query = prepareQuery(null);
query.orderBy(orders);
return query.fetchResults().getResults();
return executor.findAll(orders);
}
/*
@@ -198,10 +140,7 @@ public class QuerydslKeyValueRepository<T, ID> extends SimpleKeyValueRepository<
*/
@Override
public long count(Predicate predicate) {
Assert.notNull(predicate, "Predicate must not be null!");
return prepareQuery(predicate).fetchCount();
return executor.count(predicate);
}
/*
@@ -210,29 +149,13 @@ public class QuerydslKeyValueRepository<T, ID> extends SimpleKeyValueRepository<
*/
@Override
public boolean exists(Predicate predicate) {
Assert.notNull(predicate, "Predicate must not be null!");
return count(predicate) > 0;
return executor.exists(predicate);
}
@Override
public <S extends T, R> R findBy(Predicate predicate,
Function<FluentQuery.FetchableFluentQuery<S>, R> queryFunction) {
throw new UnsupportedOperationException("Not yet supported");
return executor.findBy(predicate, queryFunction);
}
/**
* Creates executable query for given {@link Predicate}.
*
* @param predicate
* @return
*/
protected AbstractCollQuery<T, ?> prepareQuery(@Nullable Predicate predicate) {
CollQuery<T> query = new CollQuery<>();
query.from(builder, findAll());
return predicate != null ? query.where(predicate) : query;
}
}

View File

@@ -16,12 +16,10 @@
package org.springframework.data.map;
import static org.assertj.core.api.Assertions.*;
import static org.assertj.core.api.Assumptions.*;
import java.util.List;
import java.util.Optional;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
@@ -37,7 +35,6 @@ import org.springframework.data.map.QuerydslKeyValueRepositoryUnitTests.QPersonR
import org.springframework.data.querydsl.QSort;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import org.springframework.data.util.Streamable;
import org.springframework.data.util.Version;
/**
* Unit tests for {@link QuerydslKeyValueRepository}.
@@ -49,11 +46,6 @@ import org.springframework.data.util.Version;
*/
public class QuerydslKeyValueRepositoryUnitTests extends AbstractRepositoryUnitTests<QPersonRepository> {
@BeforeEach
void before() {
assumeThat(Version.javaVersion().toString()).startsWith("1.8");
}
@Test // DATACMNS-525
void findOneIsExecutedCorrectly() {