Skip to content

Commit 79fe924

Browse files
committed
Allow queries using Pageable for the first page only.
Closes #274
1 parent 1fe2928 commit 79fe924

File tree

3 files changed

+143
-16
lines changed

3 files changed

+143
-16
lines changed

src/main/java/org/springframework/data/ldap/repository/support/LdapRepositoryFactory.java

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import static org.springframework.data.querydsl.QuerydslUtils.*;
1919

20+
import java.lang.reflect.Constructor;
2021
import java.lang.reflect.Method;
2122
import java.util.Optional;
2223

@@ -115,7 +116,15 @@ protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
115116
*/
116117
@Override
117118
protected Object getTargetRepository(RepositoryInformation information) {
118-
return getTargetRepositoryViaReflection(information, ldapOperations, mappingContext,
119+
120+
boolean acceptsMappingContext = acceptsMappingContext(information);
121+
122+
if (acceptsMappingContext) {
123+
return getTargetRepositoryViaReflection(information, ldapOperations, mappingContext,
124+
ldapOperations.getObjectDirectoryMapper(), information.getDomainType());
125+
}
126+
127+
return getTargetRepositoryViaReflection(information, ldapOperations,
119128
ldapOperations.getObjectDirectoryMapper(),
120129
information.getDomainType());
121130
}
@@ -130,9 +139,35 @@ protected Optional<QueryLookupStrategy> getQueryLookupStrategy(@Nullable Key key
130139
return Optional.of(queryLookupStrategy);
131140
}
132141

142+
/**
143+
* Allow creation of repository base classes that do not accept a {@link LdapMappingContext} that was introduced with
144+
* version 2.6.
145+
*
146+
* @param information
147+
* @return
148+
*/
149+
private static boolean acceptsMappingContext(RepositoryInformation information) {
150+
151+
Class<?> repositoryBaseClass = information.getRepositoryBaseClass();
152+
153+
Constructor<?>[] declaredConstructors = repositoryBaseClass.getDeclaredConstructors();
154+
155+
boolean acceptsMappingContext = false;
156+
157+
for (Constructor<?> declaredConstructor : declaredConstructors) {
158+
Class<?>[] parameterTypes = declaredConstructor.getParameterTypes();
159+
160+
if (parameterTypes.length == 4 && parameterTypes[1].isAssignableFrom(LdapMappingContext.class)) {
161+
acceptsMappingContext = true;
162+
}
163+
}
164+
165+
return acceptsMappingContext;
166+
}
167+
133168
private static final class LdapQueryLookupStrategy implements QueryLookupStrategy {
134169

135-
private LdapOperations ldapOperations;
170+
private final LdapOperations ldapOperations;
136171

137172
/**
138173
* @param ldapOperations must not be {@literal null}.

src/main/java/org/springframework/data/ldap/repository/support/QuerydslLdapRepository.java

Lines changed: 65 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
import org.springframework.beans.factory.BeanClassLoaderAware;
3131
import org.springframework.beans.factory.BeanFactory;
3232
import org.springframework.beans.factory.BeanFactoryAware;
33-
import org.springframework.dao.EmptyResultDataAccessException;
3433
import org.springframework.dao.IncorrectResultSizeDataAccessException;
3534
import org.springframework.data.domain.Example;
3635
import org.springframework.data.domain.Page;
@@ -46,6 +45,7 @@
4645
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
4746
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
4847
import org.springframework.data.repository.query.FluentQuery;
48+
import org.springframework.data.support.PageableExecutionUtils;
4949
import org.springframework.lang.Nullable;
5050
import org.springframework.ldap.core.LdapOperations;
5151
import org.springframework.ldap.odm.core.ObjectDirectoryMapper;
@@ -139,45 +139,74 @@ public List<T> findAll(Predicate predicate) {
139139
*/
140140
@Override
141141
public long count(Predicate predicate) {
142-
return findAll(predicate).size();
142+
return findBy(predicate, FluentQuery.FetchableFluentQuery::count);
143143
}
144144

145145
/* (non-Javadoc)
146146
* @see org.springframework.data.querydsl.QueryDslPredicateExecutor#exists(com.querydsl.core.types.Predicate)
147147
*/
148148
public boolean exists(Predicate predicate) {
149-
return count(predicate) > 0;
149+
return findBy(predicate, FluentQuery.FetchableFluentQuery::exists);
150150
}
151151

152152
/* (non-Javadoc)
153153
* @see org.springframework.data.querydsl.QueryDslPredicateExecutor#findAll(com.querydsl.core.types.Predicate, org.springframework.data.domain.Sort)
154154
*/
155155
public Iterable<T> findAll(Predicate predicate, Sort sort) {
156-
throw new UnsupportedOperationException();
157-
}
158156

157+
Assert.notNull(sort, "Pageable must not be null!");
158+
159+
if (sort.isUnsorted()) {
160+
return findAll(predicate);
161+
}
162+
163+
throw new UnsupportedOperationException("Sorting is not supported");
164+
}
159165

160166
/* (non-Javadoc)
161167
* @see org.springframework.data.querydsl.QueryDslPredicateExecutor#findAll(com.querydsl.core.types.OrderSpecifier[])
162168
*/
163169
public Iterable<T> findAll(OrderSpecifier<?>... orders) {
164-
throw new UnsupportedOperationException();
170+
171+
if (orders.length == 0) {
172+
return findAll();
173+
}
174+
175+
throw new UnsupportedOperationException("Sorting is not supported");
165176
}
166177

167178
/* (non-Javadoc)
168179
* @see org.springframework.data.querydsl.QueryDslPredicateExecutor#findAll(com.querydsl.core.types.Predicate, com.querydsl.core.types.OrderSpecifier[])
169180
*/
170181
@Override
171182
public Iterable<T> findAll(Predicate predicate, OrderSpecifier<?>... orders) {
172-
throw new UnsupportedOperationException();
183+
184+
if (orders.length == 0) {
185+
return findAll(predicate);
186+
}
187+
188+
throw new UnsupportedOperationException("Sorting is not supported");
173189
}
174190

175191
/* (non-Javadoc)
176192
* @see org.springframework.data.querydsl.QueryDslPredicateExecutor#findAll(com.querydsl.core.types.Predicate, org.springframework.data.domain.Pageable)
177193
*/
178194
@Override
179195
public Page<T> findAll(Predicate predicate, Pageable pageable) {
180-
throw new UnsupportedOperationException();
196+
197+
Assert.notNull(pageable, "Pageable must not be null!");
198+
199+
if (pageable.isUnpaged()) {
200+
return PageableExecutionUtils.getPage(findAll(predicate), pageable, () -> count(predicate));
201+
}
202+
203+
if (pageable.getSort().isUnsorted() && pageable.getPageNumber() == 0) {
204+
205+
return PageableExecutionUtils.getPage(queryFor(predicate, q -> q.countLimit(pageable.getPageSize())).list(),
206+
pageable, () -> count(predicate));
207+
}
208+
209+
throw new UnsupportedOperationException("Pagination and Sorting is not supported");
181210
}
182211

183212
/*
@@ -189,7 +218,6 @@ public Page<T> findAll(Predicate predicate, Pageable pageable) {
189218
public <S extends T, R> R findBy(Predicate predicate,
190219
Function<FluentQuery.FetchableFluentQuery<S>, R> queryFunction) {
191220

192-
Assert.notNull(predicate, "Predicate must not be null!");
193221
Assert.notNull(queryFunction, "Query function must not be null!");
194222

195223
return queryFunction.apply(new FluentQuerydsl<>(predicate, (Class<S>) entityType));
@@ -202,6 +230,9 @@ private QuerydslLdapQuery<T> queryFor(Predicate predicate) {
202230
}
203231

204232
private QuerydslLdapQuery<T> queryFor(Predicate predicate, Consumer<LdapQueryBuilder> queryBuilderConsumer) {
233+
234+
Assert.notNull(predicate, "Predicate must not be null!");
235+
205236
return new QuerydslLdapQuery<>(ldapOperations, entityType, queryBuilderConsumer).where(predicate);
206237
}
207238

@@ -281,7 +312,7 @@ public R oneValue() {
281312
}
282313

283314
T one = results.get(0);
284-
return getConversionFunction(entityType, resultType).apply(one);
315+
return getConversionFunction().apply(one);
285316
}
286317

287318
/*
@@ -299,9 +330,10 @@ public R firstValue() {
299330
}
300331

301332
T one = results.get(0);
302-
return getConversionFunction(entityType, resultType).apply(one);
333+
return getConversionFunction().apply(one);
303334
}
304335

336+
305337
/*
306338
* (non-Javadoc)
307339
* @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#all()
@@ -319,7 +351,21 @@ public List<R> all() {
319351
public Page<R> page(Pageable pageable) {
320352

321353
Assert.notNull(pageable, "Pageable must not be null!");
322-
throw new UnsupportedOperationException();
354+
355+
if (pageable.isUnpaged()) {
356+
return PageableExecutionUtils.getPage(all(), pageable, this::count);
357+
}
358+
359+
if (pageable.getSort().isUnsorted() && pageable.getPageNumber() == 0) {
360+
361+
Function<Object, R> conversionFunction = getConversionFunction();
362+
363+
return PageableExecutionUtils.getPage(
364+
findTop(pageable.getPageSize()).stream().map(conversionFunction).collect(Collectors.toList()), pageable,
365+
this::count);
366+
}
367+
368+
throw new UnsupportedOperationException("Pagination and Sorting is not supported");
323369
}
324370

325371
/*
@@ -329,7 +375,7 @@ public Page<R> page(Pageable pageable) {
329375
@Override
330376
public Stream<R> stream() {
331377

332-
Function<Object, R> conversionFunction = getConversionFunction(entityType, resultType);
378+
Function<Object, R> conversionFunction = getConversionFunction();
333379

334380
return search(null, QuerydslLdapQuery::list).stream().map(conversionFunction);
335381
}
@@ -390,6 +436,10 @@ private <P> Function<Object, P> getConversionFunction(Class<?> inputType, Class<
390436
return o -> (P) converter.convert(o);
391437
}
392438

439+
private Function<Object, R> getConversionFunction() {
440+
return getConversionFunction(entityType, resultType);
441+
}
442+
393443
private List<String> getProjection() {
394444

395445
if (projection.isEmpty()) {
@@ -410,5 +460,7 @@ private List<String> getProjection() {
410460

411461
return projection;
412462
}
463+
413464
}
465+
414466
}

src/test/java/org/springframework/data/ldap/repository/support/QuerydslLdapRepositoryUnitTests.java

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@
3535
import org.mockito.junit.jupiter.MockitoSettings;
3636

3737
import org.springframework.dao.IncorrectResultSizeDataAccessException;
38+
import org.springframework.data.domain.Page;
39+
import org.springframework.data.domain.PageRequest;
40+
import org.springframework.data.domain.Sort;
3841
import org.springframework.data.repository.query.FluentQuery;
3942
import org.springframework.ldap.core.ContextMapper;
4043
import org.springframework.ldap.core.LdapOperations;
@@ -166,6 +169,43 @@ void findByShouldReturnAllWithProjection() {
166169
assertThat(all).hasOnlyElementsOfType(PersonProjection.class);
167170
}
168171

172+
@Test // GH-269
173+
void findByShouldReturnFirstPage() {
174+
175+
when(ldapOperations.find(any(LdapQuery.class), eq(UnitTestPerson.class)))
176+
.thenReturn(Collections.singletonList(walter));
177+
when(ldapOperations.search(any(LdapQuery.class), any(ContextMapper.class))).thenReturn(Arrays.asList(true, true));
178+
179+
Page<PersonProjection> page = repository.findBy(QPerson.person.fullName.eq("Walter"),
180+
it -> it.as(PersonProjection.class).page(PageRequest.of(0, 1, Sort.unsorted())));
181+
assertThat(page.getContent().get(0).getLastName()).isEqualTo("White");
182+
assertThat(page.getTotalPages()).isEqualTo(2);
183+
184+
ArgumentCaptor<LdapQuery> captor = ArgumentCaptor.forClass(LdapQuery.class);
185+
186+
verify(ldapOperations).find(captor.capture(), any());
187+
188+
LdapQuery query = captor.getValue();
189+
190+
assertThat(query.countLimit()).isEqualTo(1);
191+
}
192+
193+
@Test // GH-269
194+
void shouldRejectNextPage() {
195+
196+
assertThatExceptionOfType(UnsupportedOperationException.class)
197+
.isThrownBy(() -> repository.findBy(QPerson.person.fullName.eq("Walter"),
198+
it -> it.as(PersonProjection.class).page(PageRequest.of(1, 1, Sort.unsorted()))));
199+
}
200+
201+
@Test // GH-269
202+
void shouldRejectSortedPage() {
203+
204+
assertThatExceptionOfType(UnsupportedOperationException.class)
205+
.isThrownBy(() -> repository.findBy(QPerson.person.fullName.eq("Walter"),
206+
it -> it.as(PersonProjection.class).page(PageRequest.of(0, 1, Sort.by(Sort.Direction.ASC, "foo")))));
207+
}
208+
169209
@Test // GH-269
170210
void findByShouldReturnStream() {
171211

@@ -191,7 +231,7 @@ void findByShouldReturnStreamWithProjection() {
191231
@Test // GH-269
192232
void findByShouldReturnCount() {
193233

194-
when(ldapOperations.find(any(LdapQuery.class), eq(UnitTestPerson.class))).thenReturn(Arrays.asList(walter, hank));
234+
when(ldapOperations.search(any(LdapQuery.class), any(ContextMapper.class))).thenReturn(Arrays.asList(true, true));
195235

196236
long count = repository.findBy(QPerson.person.fullName.eq("Walter"), FluentQuery.FetchableFluentQuery::count);
197237

0 commit comments

Comments
 (0)