@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
Reference in New Issue
Block a user