diff --git a/src/main/java/org/springframework/data/ldap/repository/support/LdapEntityInformation.java b/src/main/java/org/springframework/data/ldap/repository/support/LdapEntityInformation.java index 70ba386..e3944d7 100644 --- a/src/main/java/org/springframework/data/ldap/repository/support/LdapEntityInformation.java +++ b/src/main/java/org/springframework/data/ldap/repository/support/LdapEntityInformation.java @@ -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 extends AbstractEntityInformation { - private final DefaultObjectDirectoryMapper MAPPER = new DefaultObjectDirectoryMapper(); + private final ObjectDirectoryMapper odm; - public LdapEntityInformation(Class domainClass) { + public LdapEntityInformation(Class domainClass, ObjectDirectoryMapper odm) { super(domainClass); + this.odm = odm; } @Nullable @Override public Name getId(T entity) { - return MAPPER.getId(entity); + return odm.getId(entity); } @Override diff --git a/src/main/java/org/springframework/data/ldap/repository/support/LdapRepositoryFactory.java b/src/main/java/org/springframework/data/ldap/repository/support/LdapRepositoryFactory.java index 00855ac..9523ef2 100644 --- a/src/main/java/org/springframework/data/ldap/repository/support/LdapRepositoryFactory.java +++ b/src/main/java/org/springframework/data/ldap/repository/support/LdapRepositoryFactory.java @@ -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 EntityInformation getEntityInformation(Class 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. + *

+ * 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(); } /* diff --git a/src/main/java/org/springframework/data/ldap/repository/support/QuerydslLdapPredicateExecutor.java b/src/main/java/org/springframework/data/ldap/repository/support/QuerydslLdapPredicateExecutor.java new file mode 100644 index 0000000..8aba774 --- /dev/null +++ b/src/main/java/org/springframework/data/ldap/repository/support/QuerydslLdapPredicateExecutor.java @@ -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 implements QuerydslPredicateExecutor { + + private final EntityInformation entityInformation; + private final ProjectionFactory projectionFactory; + private final LdapOperations ldapOperations; + private final MappingContext, ? 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 entityType, ProjectionFactory projectionFactory, + LdapOperations ldapOperations, + MappingContext, ? 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 entityInformation, ProjectionFactory projectionFactory, + LdapOperations ldapOperations, + MappingContext, ? 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 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 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 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 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 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 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 R findBy(Predicate predicate, + Function, R> queryFunction) { + + Assert.notNull(queryFunction, "Query function must not be null!"); + + return queryFunction.apply(new FluentQuerydsl<>(predicate, (Class) entityInformation.getJavaType())); + } + + private QuerydslLdapQuery queryFor(Predicate predicate) { + return queryFor(predicate, it -> { + + }); + } + + private QuerydslLdapQuery queryFor(Predicate predicate, Consumer 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 implements FluentQuery.FetchableFluentQuery { + + private final Predicate predicate; + private final Sort sort; + private final Class resultType; + private final List projection; + + FluentQuerydsl(Predicate predicate, Class resultType) { + this(predicate, Sort.unsorted(), resultType, Collections.emptyList()); + } + + FluentQuerydsl(Predicate predicate, Sort sort, Class resultType, List 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 sortBy(Sort sort) { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#as(java.lang.Class) + */ + @Override + public FetchableFluentQuery as(Class 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 project(Collection 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 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 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 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 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 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 stream() { + + Function 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 findTop(int limit) { + return search(limit, QuerydslLdapQuery::list); + } + + private S search(@Nullable Integer limit, Function, S> searchFunction) { + + QuerydslLdapQuery q = queryFor(predicate, query -> { + + List 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

Function getConversionFunction(Class inputType, Class

targetType) { + + if (targetType.isAssignableFrom(inputType)) { + return (Function) 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 getConversionFunction() { + return getConversionFunction(entityInformation.getJavaType(), resultType); + } + + private List 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; + } + + } + +} diff --git a/src/main/java/org/springframework/data/ldap/repository/support/QuerydslLdapRepository.java b/src/main/java/org/springframework/data/ldap/repository/support/QuerydslLdapRepository.java index 643f87e..6a2a3ba 100644 --- a/src/main/java/org/springframework/data/ldap/repository/support/QuerydslLdapRepository.java +++ b/src/main/java/org/springframework/data/ldap/repository/support/QuerydslLdapRepository.java @@ -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 extends SimpleLdapRepository - implements QuerydslPredicateExecutor, BeanFactoryAware, BeanClassLoaderAware { +@Deprecated +public class QuerydslLdapRepository extends SimpleLdapRepository implements QuerydslPredicateExecutor { - private final LdapOperations ldapOperations; - private final MappingContext, ? extends PersistentProperty> context; - private final Class entityType; - private final SpelAwareProxyProjectionFactory projectionFactory = new SpelAwareProxyProjectionFactory(); - private final EntityInstantiators entityInstantiators = new EntityInstantiators(); + private final QuerydslLdapPredicateExecutor executor; /** * Creates a new {@link QuerydslLdapRepository}. @@ -82,9 +59,8 @@ public class QuerydslLdapRepository extends SimpleLdapRepository 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 extends SimpleLdapRepository 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 extends SimpleLdapRepository */ @Override public Optional findOne(Predicate predicate) { - return findBy(predicate, Function.identity()).one(); + return executor.findOne(predicate); } /* (non-Javadoc) @@ -131,7 +96,7 @@ public class QuerydslLdapRepository extends SimpleLdapRepository */ @Override public List findAll(Predicate predicate) { - return queryFor(predicate).list(); + return executor.findAll(predicate); } /* (non-Javadoc) @@ -139,40 +104,28 @@ public class QuerydslLdapRepository extends SimpleLdapRepository */ @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 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 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 extends SimpleLdapRepository */ @Override public Iterable 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 extends SimpleLdapRepository */ @Override public Page 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 extends SimpleLdapRepository @SuppressWarnings("unchecked") public R findBy(Predicate predicate, Function, R> queryFunction) { - - Assert.notNull(queryFunction, "Query function must not be null!"); - - return queryFunction.apply(new FluentQuerydsl<>(predicate, (Class) entityType)); - } - - private QuerydslLdapQuery queryFor(Predicate predicate) { - return queryFor(predicate, it -> { - - }); - } - - private QuerydslLdapQuery queryFor(Predicate predicate, Consumer 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 implements FluentQuery.FetchableFluentQuery { - - private final Predicate predicate; - private final Sort sort; - private final Class resultType; - private final List projection; - - FluentQuerydsl(Predicate predicate, Class resultType) { - this(predicate, Sort.unsorted(), resultType, Collections.emptyList()); - } - - FluentQuerydsl(Predicate predicate, Sort sort, Class resultType, List 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 sortBy(Sort sort) { - throw new UnsupportedOperationException(); - } - - /* - * (non-Javadoc) - * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#as(java.lang.Class) - */ - @Override - public FetchableFluentQuery as(Class 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 project(Collection 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 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 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 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 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 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 stream() { - - Function 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 findTop(int limit) { - return search(limit, QuerydslLdapQuery::list); - } - - private S search(@Nullable Integer limit, Function, S> searchFunction) { - - QuerydslLdapQuery q = queryFor(predicate, query -> { - - List 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

Function getConversionFunction(Class inputType, Class

targetType) { - - if (targetType.isAssignableFrom(inputType)) { - return (Function) 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 getConversionFunction() { - return getConversionFunction(entityType, resultType); - } - - private List 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); } } diff --git a/src/test/java/org/springframework/data/ldap/repository/support/QuerydslLdapRepositoryUnitTests.java b/src/test/java/org/springframework/data/ldap/repository/support/QuerydslLdapPredicateExecutorUnitTests.java similarity index 94% rename from src/test/java/org/springframework/data/ldap/repository/support/QuerydslLdapRepositoryUnitTests.java rename to src/test/java/org/springframework/data/ldap/repository/support/QuerydslLdapPredicateExecutorUnitTests.java index f743401..9e32b0c 100644 --- a/src/test/java/org/springframework/data/ldap/repository/support/QuerydslLdapRepositoryUnitTests.java +++ b/src/test/java/org/springframework/data/ldap/repository/support/QuerydslLdapPredicateExecutorUnitTests.java @@ -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 repository; + QuerydslLdapPredicateExecutor 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");