Extract Querydsl fragment from QuerydslLdapRepository.

Closes #273
This commit is contained in:
Mark Paluch
2021-10-11 14:02:39 +02:00
parent 79ce689593
commit c9be21a679
5 changed files with 529 additions and 337 deletions

View File

@@ -20,7 +20,7 @@ import javax.naming.Name;
import org.springframework.data.repository.core.support.AbstractEntityInformation;
import org.springframework.lang.Nullable;
import org.springframework.ldap.odm.core.impl.DefaultObjectDirectoryMapper;
import org.springframework.ldap.odm.core.ObjectDirectoryMapper;
/**
* ODM-based {@link org.springframework.data.repository.core.EntityInformation} for LDAP entities.
@@ -32,16 +32,17 @@ import org.springframework.ldap.odm.core.impl.DefaultObjectDirectoryMapper;
*/
class LdapEntityInformation<T> extends AbstractEntityInformation<T, Name> {
private final DefaultObjectDirectoryMapper MAPPER = new DefaultObjectDirectoryMapper();
private final ObjectDirectoryMapper odm;
public LdapEntityInformation(Class<T> domainClass) {
public LdapEntityInformation(Class<T> domainClass, ObjectDirectoryMapper odm) {
super(domainClass);
this.odm = odm;
}
@Nullable
@Override
public Name getId(T entity) {
return MAPPER.getId(entity);
return odm.getId(entity);
}
@Override

View File

@@ -16,11 +16,13 @@
package org.springframework.data.ldap.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.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.ldap.core.mapping.LdapMappingContext;
import org.springframework.data.ldap.repository.query.AnnotatedLdapRepositoryQuery;
import org.springframework.data.ldap.repository.query.LdapQueryMethod;
@@ -94,9 +96,9 @@ public class LdapRepositoryFactory extends RepositoryFactorySupport {
* @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getEntityInformation(java.lang.Class)
*/
@Override
@SuppressWarnings("unchecked")
@SuppressWarnings({ "unchecked", "rawtypes" })
public <T, ID> EntityInformation<T, ID> getEntityInformation(Class<T> domainClass) {
return new LdapEntityInformation(domainClass);
return new LdapEntityInformation(domainClass, ldapOperations.getObjectDirectoryMapper());
}
/*
@@ -105,11 +107,46 @@ public class LdapRepositoryFactory extends RepositoryFactorySupport {
*/
@Override
protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
return SimpleLdapRepository.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, this.ldapOperations);
}
/**
* Creates {@link RepositoryFragments} based on {@link RepositoryMetadata} to add LDAP-specific extensions. Typically,
* adds a {@link QuerydslLdapPredicateExecutor} if the repository interface uses Querydsl.
* <p>
* Can be overridden by subclasses to customize {@link RepositoryFragments}.
*
* @param metadata repository metadata.
* @param operations the LDAP operations manager.
* @return
* @since 2.6
*/
protected RepositoryFragments getRepositoryFragments(RepositoryMetadata metadata, LdapOperations operations) {
boolean isQueryDslRepository = QUERY_DSL_PRESENT
&& QuerydslPredicateExecutor.class.isAssignableFrom(metadata.getRepositoryInterface());
return isQueryDslRepository ? QuerydslLdapRepository.class : SimpleLdapRepository.class;
if (isQueryDslRepository) {
if (metadata.isReactiveRepository()) {
throw new InvalidDataAccessApiUsageException(
"Cannot combine Querydsl and reactive repository support in a single interface");
}
return RepositoryFragments.just(new QuerydslLdapPredicateExecutor<>(
getEntityInformation(metadata.getDomainType()), getProjectionFactory(), operations, mappingContext));
}
return RepositoryFragments.empty();
}
/*

View File

@@ -0,0 +1,460 @@
/*
* Copyright 2016-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.ldap.repository.support;
import java.beans.FeatureDescriptor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.ldap.repository.query.DtoInstantiatingConverter;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.model.EntityInstantiators;
import org.springframework.data.projection.ProjectionFactory;
import org.springframework.data.projection.ProjectionInformation;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import org.springframework.data.repository.core.EntityInformation;
import org.springframework.data.repository.query.FluentQuery;
import org.springframework.data.support.PageableExecutionUtils;
import org.springframework.lang.Nullable;
import org.springframework.ldap.core.LdapOperations;
import org.springframework.ldap.query.LdapQueryBuilder;
import org.springframework.util.Assert;
import com.querydsl.core.types.OrderSpecifier;
import com.querydsl.core.types.Predicate;
/**
* LDAP-specific {@link QuerydslPredicateExecutor}.
*
* @author Mark Paluch
* @since 2.6
*/
public class QuerydslLdapPredicateExecutor<T> implements QuerydslPredicateExecutor<T> {
private final EntityInformation<T, ?> entityInformation;
private final ProjectionFactory projectionFactory;
private final LdapOperations ldapOperations;
private final MappingContext<? extends PersistentEntity<?, ?>, ? extends PersistentProperty<?>> mappingContext;
private final EntityInstantiators entityInstantiators = new EntityInstantiators();
/**
* Creates a new {@link QuerydslLdapPredicateExecutor}.
*
* @param entityInformation must not be {@literal null}.
* @param projectionFactory must not be {@literal null}.
* @param ldapOperations must not be {@literal null}.
* @param mappingContext must not be {@literal null}.
*/
public QuerydslLdapPredicateExecutor(Class<T> entityType, ProjectionFactory projectionFactory,
LdapOperations ldapOperations,
MappingContext<? extends PersistentEntity<?, ?>, ? extends PersistentProperty<?>> mappingContext) {
Assert.notNull(entityType, "Entity type must not be null!");
Assert.notNull(projectionFactory, "ProjectionFactory must not be null!");
Assert.notNull(ldapOperations, "LdapOperations must not be null!");
Assert.notNull(mappingContext, "MappingContext must not be null!");
this.entityInformation = new LdapEntityInformation<>(entityType, ldapOperations.getObjectDirectoryMapper());
this.ldapOperations = ldapOperations;
this.projectionFactory = projectionFactory;
this.mappingContext = mappingContext;
}
/**
* Creates a new {@link QuerydslLdapPredicateExecutor}.
*
* @param entityInformation must not be {@literal null}.
* @param projectionFactory must not be {@literal null}.
* @param ldapOperations must not be {@literal null}.
* @param mappingContext must not be {@literal null}.
*/
public QuerydslLdapPredicateExecutor(EntityInformation<T, ?> entityInformation, ProjectionFactory projectionFactory,
LdapOperations ldapOperations,
MappingContext<? extends PersistentEntity<?, ?>, ? extends PersistentProperty<?>> mappingContext) {
Assert.notNull(entityInformation, "EntityInformation must not be null!");
Assert.notNull(projectionFactory, "ProjectionFactory must not be null!");
Assert.notNull(ldapOperations, "LdapOperations must not be null!");
Assert.notNull(mappingContext, "MappingContext must not be null!");
this.entityInformation = entityInformation;
this.ldapOperations = ldapOperations;
this.projectionFactory = projectionFactory;
this.mappingContext = mappingContext;
}
/*
* (non-Javadoc)
* @see org.springframework.data.querydsl.QueryDslPredicateExecutor#findOne(com.querydsl.core.types.Predicate)
*/
@Override
public Optional<T> findOne(Predicate predicate) {
return findBy(predicate, Function.identity()).one();
}
/* (non-Javadoc)
* @see org.springframework.data.querydsl.QueryDslPredicateExecutor#findAll(com.querydsl.core.types.Predicate)
*/
@Override
public List<T> findAll(Predicate predicate) {
return queryFor(predicate).list();
}
/* (non-Javadoc)
* @see org.springframework.data.querydsl.QueryDslPredicateExecutor#count(com.querydsl.core.types.Predicate)
*/
@Override
public long count(Predicate predicate) {
return findBy(predicate, FluentQuery.FetchableFluentQuery::count);
}
/* (non-Javadoc)
* @see org.springframework.data.querydsl.QueryDslPredicateExecutor#exists(com.querydsl.core.types.Predicate)
*/
public boolean exists(Predicate predicate) {
return findBy(predicate, FluentQuery.FetchableFluentQuery::exists);
}
/* (non-Javadoc)
* @see org.springframework.data.querydsl.QueryDslPredicateExecutor#findAll(com.querydsl.core.types.Predicate, org.springframework.data.domain.Sort)
*/
public Iterable<T> findAll(Predicate predicate, Sort sort) {
Assert.notNull(sort, "Pageable must not be null!");
if (sort.isUnsorted()) {
return findAll(predicate);
}
throw new UnsupportedOperationException("Sorting is not supported");
}
/* (non-Javadoc)
* @see org.springframework.data.querydsl.QueryDslPredicateExecutor#findAll(com.querydsl.core.types.OrderSpecifier[])
*/
public Iterable<T> findAll(OrderSpecifier<?>... orders) {
if (orders.length == 0) {
return findAll();
}
throw new UnsupportedOperationException("Sorting is not supported");
}
/* (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) {
if (orders.length == 0) {
return findAll(predicate);
}
throw new UnsupportedOperationException("Sorting is not supported");
}
/* (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(pageable, "Pageable must not be null!");
if (pageable.isUnpaged()) {
return PageableExecutionUtils.getPage(findAll(predicate), pageable, () -> count(predicate));
}
if (pageable.getSort().isUnsorted() && pageable.getPageNumber() == 0) {
return PageableExecutionUtils.getPage(queryFor(predicate, q -> q.countLimit(pageable.getPageSize())).list(),
pageable, () -> count(predicate));
}
throw new UnsupportedOperationException("Pagination and Sorting is not supported");
}
/*
* (non-Javadoc)
* @see org.springframework.data.querydsl.QuerydslPredicateExecutor#findBy(com.querydsl.core.types.Predicate, java.util.function.Function)
*/
@Override
@SuppressWarnings("unchecked")
public <S extends T, R> R findBy(Predicate predicate,
Function<FluentQuery.FetchableFluentQuery<S>, R> queryFunction) {
Assert.notNull(queryFunction, "Query function must not be null!");
return queryFunction.apply(new FluentQuerydsl<>(predicate, (Class<S>) entityInformation.getJavaType()));
}
private QuerydslLdapQuery<T> queryFor(Predicate predicate) {
return queryFor(predicate, it -> {
});
}
private QuerydslLdapQuery<T> queryFor(Predicate predicate, Consumer<LdapQueryBuilder> queryBuilderConsumer) {
Assert.notNull(predicate, "Predicate must not be null!");
return new QuerydslLdapQuery<>(ldapOperations, entityInformation.getJavaType(), queryBuilderConsumer)
.where(predicate);
}
/**
* {@link FetchableFluentQuery} using {@link Example}.
*
* @author Mark Paluch
* @since 2.6
*/
class FluentQuerydsl<R> implements FluentQuery.FetchableFluentQuery<R> {
private final Predicate predicate;
private final Sort sort;
private final Class<R> resultType;
private final List<String> projection;
FluentQuerydsl(Predicate predicate, Class<R> resultType) {
this(predicate, Sort.unsorted(), resultType, Collections.emptyList());
}
FluentQuerydsl(Predicate predicate, Sort sort, Class<R> resultType, List<String> projection) {
this.predicate = predicate;
this.sort = sort;
this.resultType = resultType;
this.projection = projection;
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#sortBy(org.springframework.data.domain.Sort)
*/
@Override
public FetchableFluentQuery<R> sortBy(Sort sort) {
throw new UnsupportedOperationException();
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#as(java.lang.Class)
*/
@Override
public <R1> FetchableFluentQuery<R1> as(Class<R1> resultType) {
Assert.notNull(projection, "Projection target type must not be null!");
return new FluentQuerydsl<>(predicate, sort, resultType, projection);
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#project(java.util.Collection)
*/
@Override
public FetchableFluentQuery<R> project(Collection<String> properties) {
Assert.notNull(properties, "Projection properties must not be null!");
return new FluentQuerydsl<>(predicate, sort, resultType, new ArrayList<>(properties));
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#oneValue()
*/
@Nullable
@Override
public R oneValue() {
List<T> results = findTop(2);
if (results.isEmpty()) {
return null;
}
if (results.size() > 1) {
throw new IncorrectResultSizeDataAccessException(1);
}
T one = results.get(0);
return getConversionFunction().apply(one);
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#firstValue()
*/
@Nullable
@Override
public R firstValue() {
List<T> results = findTop(2);
if (results.isEmpty()) {
return null;
}
T one = results.get(0);
return getConversionFunction().apply(one);
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#all()
*/
@Override
public List<R> all() {
return stream().collect(Collectors.toList());
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#page(org.springframework.data.domain.Pageable)
*/
@Override
public Page<R> page(Pageable pageable) {
Assert.notNull(pageable, "Pageable must not be null!");
if (pageable.isUnpaged()) {
return PageableExecutionUtils.getPage(all(), pageable, this::count);
}
if (pageable.getSort().isUnsorted() && pageable.getPageNumber() == 0) {
Function<Object, R> conversionFunction = getConversionFunction();
return PageableExecutionUtils.getPage(
findTop(pageable.getPageSize()).stream().map(conversionFunction).collect(Collectors.toList()), pageable,
this::count);
}
throw new UnsupportedOperationException("Pagination and Sorting is not supported");
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#stream()
*/
@Override
public Stream<R> stream() {
Function<Object, R> conversionFunction = getConversionFunction();
return search(null, QuerydslLdapQuery::list).stream().map(conversionFunction);
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#count()
*/
@Override
public long count() {
return search(null, q -> q.search(it -> true)).size();
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#exists()
*/
@Override
public boolean exists() {
return !search(1, q -> q.search(it -> true)).isEmpty();
}
private List<T> findTop(int limit) {
return search(limit, QuerydslLdapQuery::list);
}
private <S> S search(@Nullable Integer limit, Function<QuerydslLdapQuery<T>, S> searchFunction) {
QuerydslLdapQuery<T> q = queryFor(predicate, query -> {
List<String> projection = getProjection();
if (!projection.isEmpty()) {
query.attributes(projection.toArray(new String[0]));
}
if (limit != null) {
query.countLimit(limit);
}
});
return searchFunction.apply(q);
}
@SuppressWarnings("unchecked")
private <P> Function<Object, P> getConversionFunction(Class<?> inputType, Class<P> targetType) {
if (targetType.isAssignableFrom(inputType)) {
return (Function<Object, P>) Function.identity();
}
if (targetType.isInterface()) {
return o -> projectionFactory.createProjection(targetType, o);
}
DtoInstantiatingConverter converter = new DtoInstantiatingConverter(targetType, mappingContext,
entityInstantiators);
return o -> (P) converter.convert(o);
}
private Function<Object, R> getConversionFunction() {
return getConversionFunction(entityInformation.getJavaType(), resultType);
}
private List<String> getProjection() {
if (projection.isEmpty()) {
if (resultType.isAssignableFrom(entityInformation.getJavaType())) {
return projection;
}
if (resultType.isInterface()) {
ProjectionInformation projectionInformation = projectionFactory.getProjectionInformation(resultType);
if (projectionInformation.isClosed()) {
return projectionInformation.getInputProperties().stream().map(FeatureDescriptor::getName)
.collect(Collectors.toList());
}
}
}
return projection;
}
}
}

View File

@@ -15,42 +15,22 @@
*/
package org.springframework.data.ldap.repository.support;
import java.beans.FeatureDescriptor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.ldap.core.mapping.LdapMappingContext;
import org.springframework.data.ldap.repository.query.DtoInstantiatingConverter;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.model.EntityInstantiators;
import org.springframework.data.projection.ProjectionInformation;
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import org.springframework.data.repository.query.FluentQuery;
import org.springframework.data.support.PageableExecutionUtils;
import org.springframework.lang.Nullable;
import org.springframework.ldap.core.LdapOperations;
import org.springframework.ldap.odm.core.ObjectDirectoryMapper;
import org.springframework.ldap.query.LdapQueryBuilder;
import org.springframework.util.Assert;
import com.querydsl.core.types.OrderSpecifier;
import com.querydsl.core.types.Predicate;
@@ -61,15 +41,12 @@ import com.querydsl.core.types.Predicate;
* @author Mattias Hellborg Arthursson
* @author Eddu Melendez
* @author Mark Paluch
* @deprecated since 2.6, use {@link QuerydslLdapPredicateExecutor} instead.
*/
public class QuerydslLdapRepository<T> extends SimpleLdapRepository<T>
implements QuerydslPredicateExecutor<T>, BeanFactoryAware, BeanClassLoaderAware {
@Deprecated
public class QuerydslLdapRepository<T> extends SimpleLdapRepository<T> implements QuerydslPredicateExecutor<T> {
private final LdapOperations ldapOperations;
private final MappingContext<? extends PersistentEntity<?, ?>, ? extends PersistentProperty<?>> context;
private final Class<T> entityType;
private final SpelAwareProxyProjectionFactory projectionFactory = new SpelAwareProxyProjectionFactory();
private final EntityInstantiators entityInstantiators = new EntityInstantiators();
private final QuerydslLdapPredicateExecutor<T> executor;
/**
* Creates a new {@link QuerydslLdapRepository}.
@@ -82,9 +59,8 @@ public class QuerydslLdapRepository<T> extends SimpleLdapRepository<T>
super(ldapOperations, odm, entityType);
this.ldapOperations = ldapOperations;
this.entityType = entityType;
this.context = new LdapMappingContext();
this.executor = new QuerydslLdapPredicateExecutor<>(entityType, new SpelAwareProxyProjectionFactory(),
ldapOperations, new LdapMappingContext());
}
/**
@@ -102,19 +78,8 @@ public class QuerydslLdapRepository<T> extends SimpleLdapRepository<T>
super(ldapOperations, context, odm, entityType);
this.ldapOperations = ldapOperations;
this.context = context;
this.entityType = entityType;
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
projectionFactory.setBeanClassLoader(classLoader);
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
projectionFactory.setBeanFactory(beanFactory);
this.executor = new QuerydslLdapPredicateExecutor<>(entityType, new SpelAwareProxyProjectionFactory(),
ldapOperations, new LdapMappingContext());
}
/*
@@ -123,7 +88,7 @@ public class QuerydslLdapRepository<T> extends SimpleLdapRepository<T>
*/
@Override
public Optional<T> findOne(Predicate predicate) {
return findBy(predicate, Function.identity()).one();
return executor.findOne(predicate);
}
/* (non-Javadoc)
@@ -131,7 +96,7 @@ public class QuerydslLdapRepository<T> extends SimpleLdapRepository<T>
*/
@Override
public List<T> findAll(Predicate predicate) {
return queryFor(predicate).list();
return executor.findAll(predicate);
}
/* (non-Javadoc)
@@ -139,40 +104,28 @@ public class QuerydslLdapRepository<T> extends SimpleLdapRepository<T>
*/
@Override
public long count(Predicate predicate) {
return findBy(predicate, FluentQuery.FetchableFluentQuery::count);
return executor.count(predicate);
}
/* (non-Javadoc)
* @see org.springframework.data.querydsl.QueryDslPredicateExecutor#exists(com.querydsl.core.types.Predicate)
*/
public boolean exists(Predicate predicate) {
return findBy(predicate, FluentQuery.FetchableFluentQuery::exists);
return executor.exists(predicate);
}
/* (non-Javadoc)
* @see org.springframework.data.querydsl.QueryDslPredicateExecutor#findAll(com.querydsl.core.types.Predicate, org.springframework.data.domain.Sort)
*/
public Iterable<T> findAll(Predicate predicate, Sort sort) {
Assert.notNull(sort, "Pageable must not be null!");
if (sort.isUnsorted()) {
return findAll(predicate);
}
throw new UnsupportedOperationException("Sorting is not supported");
return executor.findAll(predicate, sort);
}
/* (non-Javadoc)
* @see org.springframework.data.querydsl.QueryDslPredicateExecutor#findAll(com.querydsl.core.types.OrderSpecifier[])
*/
public Iterable<T> findAll(OrderSpecifier<?>... orders) {
if (orders.length == 0) {
return findAll();
}
throw new UnsupportedOperationException("Sorting is not supported");
return executor.findAll(orders);
}
/* (non-Javadoc)
@@ -180,12 +133,7 @@ public class QuerydslLdapRepository<T> extends SimpleLdapRepository<T>
*/
@Override
public Iterable<T> findAll(Predicate predicate, OrderSpecifier<?>... orders) {
if (orders.length == 0) {
return findAll(predicate);
}
throw new UnsupportedOperationException("Sorting is not supported");
return executor.findAll(predicate, orders);
}
/* (non-Javadoc)
@@ -193,20 +141,7 @@ public class QuerydslLdapRepository<T> extends SimpleLdapRepository<T>
*/
@Override
public Page<T> findAll(Predicate predicate, Pageable pageable) {
Assert.notNull(pageable, "Pageable must not be null!");
if (pageable.isUnpaged()) {
return PageableExecutionUtils.getPage(findAll(predicate), pageable, () -> count(predicate));
}
if (pageable.getSort().isUnsorted() && pageable.getPageNumber() == 0) {
return PageableExecutionUtils.getPage(queryFor(predicate, q -> q.countLimit(pageable.getPageSize())).list(),
pageable, () -> count(predicate));
}
throw new UnsupportedOperationException("Pagination and Sorting is not supported");
return executor.findAll(predicate, pageable);
}
/*
@@ -217,250 +152,7 @@ public class QuerydslLdapRepository<T> extends SimpleLdapRepository<T>
@SuppressWarnings("unchecked")
public <S extends T, R> R findBy(Predicate predicate,
Function<FluentQuery.FetchableFluentQuery<S>, R> queryFunction) {
Assert.notNull(queryFunction, "Query function must not be null!");
return queryFunction.apply(new FluentQuerydsl<>(predicate, (Class<S>) entityType));
}
private QuerydslLdapQuery<T> queryFor(Predicate predicate) {
return queryFor(predicate, it -> {
});
}
private QuerydslLdapQuery<T> queryFor(Predicate predicate, Consumer<LdapQueryBuilder> queryBuilderConsumer) {
Assert.notNull(predicate, "Predicate must not be null!");
return new QuerydslLdapQuery<>(ldapOperations, entityType, queryBuilderConsumer).where(predicate);
}
/**
* {@link org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery} using {@link Example}.
*
* @author Mark Paluch
* @since 2.6
*/
class FluentQuerydsl<R> implements FluentQuery.FetchableFluentQuery<R> {
private final Predicate predicate;
private final Sort sort;
private final Class<R> resultType;
private final List<String> projection;
FluentQuerydsl(Predicate predicate, Class<R> resultType) {
this(predicate, Sort.unsorted(), resultType, Collections.emptyList());
}
FluentQuerydsl(Predicate predicate, Sort sort, Class<R> resultType, List<String> projection) {
this.predicate = predicate;
this.sort = sort;
this.resultType = resultType;
this.projection = projection;
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#sortBy(org.springframework.data.domain.Sort)
*/
@Override
public FetchableFluentQuery<R> sortBy(Sort sort) {
throw new UnsupportedOperationException();
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#as(java.lang.Class)
*/
@Override
public <R1> FetchableFluentQuery<R1> as(Class<R1> resultType) {
Assert.notNull(projection, "Projection target type must not be null!");
return new FluentQuerydsl<>(predicate, sort, resultType, projection);
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#project(java.util.Collection)
*/
@Override
public FetchableFluentQuery<R> project(Collection<String> properties) {
Assert.notNull(properties, "Projection properties must not be null!");
return new FluentQuerydsl<>(predicate, sort, resultType, new ArrayList<>(properties));
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#oneValue()
*/
@Nullable
@Override
public R oneValue() {
List<T> results = findTop(2);
if (results.isEmpty()) {
return null;
}
if (results.size() > 1) {
throw new IncorrectResultSizeDataAccessException(1);
}
T one = results.get(0);
return getConversionFunction().apply(one);
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#firstValue()
*/
@Nullable
@Override
public R firstValue() {
List<T> results = findTop(2);
if (results.isEmpty()) {
return null;
}
T one = results.get(0);
return getConversionFunction().apply(one);
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#all()
*/
@Override
public List<R> all() {
return stream().collect(Collectors.toList());
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#page(org.springframework.data.domain.Pageable)
*/
@Override
public Page<R> page(Pageable pageable) {
Assert.notNull(pageable, "Pageable must not be null!");
if (pageable.isUnpaged()) {
return PageableExecutionUtils.getPage(all(), pageable, this::count);
}
if (pageable.getSort().isUnsorted() && pageable.getPageNumber() == 0) {
Function<Object, R> conversionFunction = getConversionFunction();
return PageableExecutionUtils.getPage(
findTop(pageable.getPageSize()).stream().map(conversionFunction).collect(Collectors.toList()), pageable,
this::count);
}
throw new UnsupportedOperationException("Pagination and Sorting is not supported");
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#stream()
*/
@Override
public Stream<R> stream() {
Function<Object, R> conversionFunction = getConversionFunction();
return search(null, QuerydslLdapQuery::list).stream().map(conversionFunction);
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#count()
*/
@Override
public long count() {
return search(null, q -> q.search(it -> true)).size();
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#exists()
*/
@Override
public boolean exists() {
return !search(1, q -> q.search(it -> true)).isEmpty();
}
private List<T> findTop(int limit) {
return search(limit, QuerydslLdapQuery::list);
}
private <S> S search(@Nullable Integer limit, Function<QuerydslLdapQuery<T>, S> searchFunction) {
QuerydslLdapQuery<T> q = queryFor(predicate, query -> {
List<String> projection = getProjection();
if (!projection.isEmpty()) {
query.attributes(projection.toArray(new String[0]));
}
if (limit != null) {
query.countLimit(limit);
}
});
return searchFunction.apply(q);
}
@SuppressWarnings("unchecked")
private <P> Function<Object, P> getConversionFunction(Class<?> inputType, Class<P> targetType) {
if (targetType.isAssignableFrom(inputType)) {
return (Function<Object, P>) Function.identity();
}
if (targetType.isInterface()) {
return o -> projectionFactory.createProjection(targetType, o);
}
DtoInstantiatingConverter converter = new DtoInstantiatingConverter(targetType, context, entityInstantiators);
return o -> (P) converter.convert(o);
}
private Function<Object, R> getConversionFunction() {
return getConversionFunction(entityType, resultType);
}
private List<String> getProjection() {
if (projection.isEmpty()) {
if (resultType.isAssignableFrom(entityType)) {
return projection;
}
if (resultType.isInterface()) {
ProjectionInformation projectionInformation = projectionFactory.getProjectionInformation(resultType);
if (projectionInformation.isClosed()) {
return projectionInformation.getInputProperties().stream().map(FeatureDescriptor::getName)
.collect(Collectors.toList());
}
}
}
return projection;
}
return executor.findBy(predicate, queryFunction);
}
}

View File

@@ -38,6 +38,8 @@ import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.ldap.core.mapping.LdapMappingContext;
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
import org.springframework.data.repository.query.FluentQuery;
import org.springframework.ldap.core.ContextMapper;
import org.springframework.ldap.core.LdapOperations;
@@ -45,24 +47,24 @@ import org.springframework.ldap.odm.core.impl.DefaultObjectDirectoryMapper;
import org.springframework.ldap.query.LdapQuery;
/**
* Unit tests for {@link QuerydslLdapRepository}.
* Unit tests for {@link QuerydslLdapPredicateExecutor}.
*
* @author Mark Paluch
*/
@MockitoSettings
class QuerydslLdapRepositoryUnitTests {
class QuerydslLdapPredicateExecutorUnitTests {
@Mock LdapOperations ldapOperations;
UnitTestPerson walter, hank;
QuerydslLdapRepository<UnitTestPerson> repository;
QuerydslLdapPredicateExecutor<UnitTestPerson> repository;
@BeforeEach
void before() throws Exception {
when(ldapOperations.getObjectDirectoryMapper()).thenReturn(new DefaultObjectDirectoryMapper());
repository = new QuerydslLdapRepository<>(ldapOperations, ldapOperations.getObjectDirectoryMapper(),
UnitTestPerson.class);
repository = new QuerydslLdapPredicateExecutor<>(UnitTestPerson.class, new SpelAwareProxyProjectionFactory(),
ldapOperations, new LdapMappingContext());
walter = new UnitTestPerson(new LdapName("cn=walter"), "Walter", "White", Collections.emptyList(), "US",
"Heisenberg", "000");