DATAJPA-1234 - Refactored Querydsl support to be based on fragments.

The base class for repositories is now fixed.

Additional features to support Querydsl come in via QuerydslJpaPredicateExecutor which is based on the QuerydslJpaRepository.

The QuerydslJpaRepository is deprecated and exists only for backward compatibility.
This commit is contained in:
Jens Schauder
2017-12-12 13:05:14 +01:00
committed by Oliver Gierke
parent 70d1012ac3
commit cbc2224ef1
7 changed files with 614 additions and 27 deletions

View File

@@ -23,6 +23,7 @@ import java.util.Optional;
import javax.persistence.EntityManager;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.jpa.projection.CollectionAwareProjectionFactory;
import org.springframework.data.jpa.provider.PersistenceProvider;
import org.springframework.data.jpa.provider.QueryExtractor;
@@ -30,9 +31,12 @@ import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy;
import org.springframework.data.projection.ProjectionFactory;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import org.springframework.data.querydsl.SimpleEntityPathResolver;
import org.springframework.data.repository.core.RepositoryInformation;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.core.support.RepositoryComposition;
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
import org.springframework.data.repository.core.support.RepositoryFragment;
import org.springframework.data.repository.query.EvaluationContextProvider;
import org.springframework.data.repository.query.QueryLookupStrategy;
import org.springframework.data.repository.query.QueryLookupStrategy.Key;
@@ -118,12 +122,7 @@ public class JpaRepositoryFactory extends RepositoryFactorySupport {
*/
@Override
protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
if (isQueryDslExecutor(metadata.getRepositoryInterface())) {
return QuerydslJpaRepository.class;
} else {
return SimpleJpaRepository.class;
}
return SimpleJpaRepository.class;
}
/*
@@ -171,4 +170,30 @@ public class JpaRepositoryFactory extends RepositoryFactorySupport {
return (JpaEntityInformation<T, ID>) JpaEntityInformationSupport.getEntityInformation(domainClass, entityManager);
}
@Override
protected RepositoryComposition.RepositoryFragments getRepositoryFragments(RepositoryMetadata metadata) {
RepositoryComposition.RepositoryFragments fragments = RepositoryComposition.RepositoryFragments.empty();
boolean isQueryDslRepository = QUERY_DSL_PRESENT
&& QuerydslPredicateExecutor.class.isAssignableFrom(metadata.getRepositoryInterface());
if (isQueryDslRepository) {
if (metadata.isReactiveRepository()) {
throw new InvalidDataAccessApiUsageException(
"Cannot combine Querydsl and reactive repository support in a single interface");
}
JpaEntityInformation<?, Serializable> entityInformation = getEntityInformation(metadata.getDomainType());
Object querydslFragment = getTargetRepositoryViaReflection(QuerydslJpaPredicateExecutor.class, entityInformation, entityManager,
SimpleEntityPathResolver.INSTANCE, crudMethodMetadataPostProcessor.getCrudMethodMetadata());
fragments = fragments.append(RepositoryFragment.implemented(querydslFragment));
}
return fragments;
}
}

View File

@@ -0,0 +1,255 @@
/*
* Copyright 2008-2017 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
*
* http://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.jpa.repository.support;
import java.util.List;
import java.util.Map.Entry;
import java.util.Optional;
import javax.persistence.EntityManager;
import javax.persistence.LockModeType;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.querydsl.EntityPathResolver;
import org.springframework.data.querydsl.QSort;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import org.springframework.data.querydsl.SimpleEntityPathResolver;
import org.springframework.data.repository.support.PageableExecutionUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import com.querydsl.core.NonUniqueResultException;
import com.querydsl.core.types.EntityPath;
import com.querydsl.core.types.OrderSpecifier;
import com.querydsl.core.types.Predicate;
import com.querydsl.core.types.dsl.PathBuilder;
import com.querydsl.jpa.JPQLQuery;
import com.querydsl.jpa.impl.AbstractJPAQuery;
/**
* QueryDsl specific fragment for extending {@link SimpleJpaRepository} with an implementation for implementation for
* {@link QuerydslPredicateExecutor}.
*
* @author Oliver Gierke
* @author Thomas Darimont
* @author Mark Paluch
* @author Jocelyn Ntakpe
* @author Christoph Strobl
* @author Jens Schauder
*/
public class QuerydslJpaPredicateExecutor<T> implements QuerydslPredicateExecutor<T> {
private static final EntityPathResolver DEFAULT_ENTITY_PATH_RESOLVER = SimpleEntityPathResolver.INSTANCE;
private final JpaEntityInformation<T, ?> entityInformation;
private final EntityPath<T> path;
private final Querydsl querydsl;
private final EntityManager entityManager;
private final CrudMethodMetadata metadata;
/**
* Creates a new {@link QuerydslJpaPredicateExecutor} from the given domain class and {@link EntityManager} and uses the
* given {@link EntityPathResolver} to translate the domain class into an {@link EntityPath}.
* @param entityInformation must not be {@literal null}.
* @param entityManager must not be {@literal null}.
* @param resolver must not be {@literal null}.
* @param metadata maybe {@literal null}.
*/
public QuerydslJpaPredicateExecutor(JpaEntityInformation<T, ?> entityInformation, EntityManager entityManager,
EntityPathResolver resolver, @Nullable CrudMethodMetadata metadata) {
this.entityInformation = entityInformation;
this.metadata = metadata;
this.path = resolver.createPath(entityInformation.getJavaType());
this.querydsl = new Querydsl(entityManager, new PathBuilder<T>(path.getType(), path.getMetadata()));
this.entityManager = entityManager;
}
/*
* (non-Javadoc)
* @see org.springframework.data.querydsl.QuerydslPredicateExecutor#findOne(com.mysema.query.types.Predicate)
*/
@Override
public Optional<T> findOne(Predicate predicate) {
try {
return Optional.ofNullable(createQuery(predicate).select(path).fetchOne());
} catch (NonUniqueResultException ex) {
throw new IncorrectResultSizeDataAccessException(ex.getMessage(), 1, ex);
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.querydsl.QuerydslPredicateExecutor#findAll(com.mysema.query.types.Predicate)
*/
@Override
public List<T> findAll(Predicate predicate) {
return createQuery(predicate).select(path).fetch();
}
/*
* (non-Javadoc)
* @see org.springframework.data.querydsl.QuerydslPredicateExecutor#findAll(com.mysema.query.types.Predicate, com.mysema.query.types.OrderSpecifier<?>[])
*/
@Override
public List<T> findAll(Predicate predicate, OrderSpecifier<?>... orders) {
return executeSorted(createQuery(predicate).select(path), orders);
}
/*
* (non-Javadoc)
* @see org.springframework.data.querydsl.QuerydslPredicateExecutor#findAll(com.mysema.query.types.Predicate, org.springframework.data.domain.Sort)
*/
@Override
public List<T> findAll(Predicate predicate, Sort sort) {
Assert.notNull(sort, "Sort must not be null!");
return executeSorted(createQuery(predicate).select(path), sort);
}
/*
* (non-Javadoc)
* @see org.springframework.data.querydsl.QuerydslPredicateExecutor#findAll(com.mysema.query.types.OrderSpecifier[])
*/
@Override
public List<T> findAll(OrderSpecifier<?>... orders) {
Assert.notNull(orders, "Order specifiers must not be null!");
return executeSorted(createQuery(new Predicate[0]).select(path), orders);
}
/*
* (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!");
final JPQLQuery<?> countQuery = createCountQuery(predicate);
JPQLQuery<T> query = querydsl.applyPagination(pageable, createQuery(predicate).select(path));
return PageableExecutionUtils.getPage(query.fetch(), pageable, countQuery::fetchCount);
}
/*
* (non-Javadoc)
* @see org.springframework.data.querydsl.QueryDslPredicateExecutor#count(com.mysema.query.types.Predicate)
*/
@Override
public long count(Predicate predicate) {
return createQuery(predicate).fetchCount();
}
/*
* (non-Javadoc)
* @see org.springframework.data.querydsl.QueryDslPredicateExecutor#exists(com.mysema.query.types.Predicate)
*/
@Override
public boolean exists(Predicate predicate) {
return createQuery(predicate).fetchCount() > 0;
}
/**
* Creates a new {@link JPQLQuery} for the given {@link Predicate}.
*
* @param predicate
* @return the Querydsl {@link JPQLQuery}.
*/
protected JPQLQuery<?> createQuery(Predicate... predicate) {
AbstractJPAQuery<?, ?> query = doCreateQuery(getQueryHints().withFetchGraphs(entityManager), predicate);
CrudMethodMetadata metadata = getRepositoryMethodMetadata();
if (metadata == null) {
return query;
}
LockModeType type = metadata.getLockModeType();
return type == null ? query : query.setLockMode(type);
}
/**
* Creates a new {@link JPQLQuery} count query for the given {@link Predicate}.
*
* @param predicate, can be {@literal null}.
* @return the Querydsl count {@link JPQLQuery}.
*/
protected JPQLQuery<?> createCountQuery(@Nullable Predicate... predicate) {
return doCreateQuery(getQueryHints(), predicate);
}
@Nullable
private CrudMethodMetadata getRepositoryMethodMetadata() {
return metadata;
}
/**
* Returns {@link QueryHints} with the query hints based on the current {@link CrudMethodMetadata} and potential
* {@link EntityGraph} information.
*
* @return
*/
private QueryHints getQueryHints() {
return metadata == null ? QueryHints.NoHints.INSTANCE : DefaultQueryHints.of(entityInformation, metadata);
}
private AbstractJPAQuery<?, ?> doCreateQuery(QueryHints hints, @Nullable Predicate... predicate) {
AbstractJPAQuery<?, ?> query = querydsl.createQuery(path);
if (predicate != null) {
query = query.where(predicate);
}
for (Entry<String, Object> hint : hints) {
query.setHint(hint.getKey(), hint.getValue());
}
return query;
}
/**
* Executes the given {@link JPQLQuery} after applying the given {@link OrderSpecifier}s.
*
* @param query must not be {@literal null}.
* @param orders must not be {@literal null}.
* @return
*/
private List<T> executeSorted(JPQLQuery<T> query, OrderSpecifier<?>... orders) {
return executeSorted(query, new QSort(orders));
}
/**
* Executes the given {@link JPQLQuery} after applying the given {@link Sort}.
*
* @param query must not be {@literal null}.
* @param sort must not be {@literal null}.
* @return
*/
private List<T> executeSorted(JPQLQuery<T> query, Sort sort) {
return querydsl.applySorting(sort, query).fetch();
}
}

View File

@@ -47,12 +47,16 @@ import com.querydsl.jpa.impl.AbstractJPAQuery;
* QueryDsl specific extension of {@link SimpleJpaRepository} which adds implementation for
* {@link QuerydslPredicateExecutor}.
*
* @deprecated Instead of this class use {@link QuerydslJpaPredicateExecutor}
*
* @author Oliver Gierke
* @author Thomas Darimont
* @author Mark Paluch
* @author Jocelyn Ntakpe
* @author Christoph Strobl
* @author Jens Schauder
*/
@Deprecated
public class QuerydslJpaRepository<T, ID extends Serializable> extends SimpleJpaRepository<T, ID>
implements QuerydslPredicateExecutor<T> {

View File

@@ -118,7 +118,7 @@ public class JpaRepositoryFactoryBeanUnitTests {
/*
* (non-Javadoc)
* @see org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean#doCreateRepositoryFactory()
* @see org.springframework.data.jpa.predicateExecutor.support.JpaRepositoryFactoryBean#doCreateRepositoryFactory()
*/
@Override
protected RepositoryFactorySupport doCreateRepositoryFactory() {

View File

@@ -50,6 +50,7 @@ import org.springframework.util.ClassUtils;
*
* @author Oliver Gierke
* @author Thomas Darimont
* @author Jens Schauder
*/
@RunWith(MockitoJUnitRunner.Silent.class)
public class JpaRepositoryFactoryUnitTests {
@@ -104,9 +105,9 @@ public class JpaRepositoryFactoryUnitTests {
}
/**
* Asserts that the factory recognized configured repository classes that contain custom method but no custom
* implementation could be found. Furthremore the exception has to contain the name of the repository interface as for
* a large repository configuration it's hard to find out where this error occured.
* Asserts that the factory recognized configured predicateExecutor classes that contain custom method but no custom
* implementation could be found. Furthremore the exception has to contain the name of the predicateExecutor interface as for
* a large predicateExecutor configuration it's hard to find out where this error occured.
*
* @throws Exception
*/
@@ -144,21 +145,6 @@ public class JpaRepositoryFactoryUnitTests {
repository.customMethod(1);
}
@Test
public void usesQueryDslRepositoryIfInterfaceImplementsExecutor() {
when(entityInformation.getJavaType()).thenReturn(User.class);
assertEquals(QuerydslJpaRepository.class,
factory.getRepositoryBaseClass(new DefaultRepositoryMetadata(QueryDslSampleRepository.class)));
try {
QueryDslSampleRepository repository = factory.getRepository(QueryDslSampleRepository.class);
assertEquals(QuerydslJpaRepository.class, ((Advised) repository).getTargetClass());
} catch (IllegalArgumentException e) {
assertThat(e.getStackTrace()[0].getClassName(), is("org.springframework.data.querydsl.SimpleEntityPathResolver"));
}
}
@Test // DATAJPA-710, DATACMNS-542
public void usesConfiguredRepositoryBaseClass() {
@@ -198,7 +184,7 @@ public class JpaRepositoryFactoryUnitTests {
}
/**
* Implementation of the custom repository interface.
* Implementation of the custom predicateExecutor interface.
*
* @author Oliver Gierke
*/

View File

@@ -0,0 +1,317 @@
/*
* Copyright 2008-2017 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
*
* http://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.jpa.repository.support;
import static org.assertj.core.api.Assertions.*;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.joda.time.LocalDate;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.domain.Sort.Order;
import org.springframework.data.jpa.domain.sample.Address;
import org.springframework.data.jpa.domain.sample.QUser;
import org.springframework.data.jpa.domain.sample.Role;
import org.springframework.data.jpa.domain.sample.User;
import org.springframework.data.querydsl.QPageRequest;
import org.springframework.data.querydsl.QSort;
import org.springframework.data.querydsl.SimpleEntityPathResolver;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
import com.querydsl.core.types.Predicate;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.core.types.dsl.PathBuilder;
import com.querydsl.core.types.dsl.PathBuilderFactory;
/**
* Integration test for {@link QuerydslJpaPredicateExecutor}.
*
* @author Jens Schauder
* @author Oliver Gierke
* @author Thomas Darimont
* @author Mark Paluch
* @author Christoph Strobl
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({ "classpath:infrastructure.xml" })
@Transactional
public class QuerydslJpaPredicateExecutorUnitTests {
@PersistenceContext EntityManager em;
QuerydslJpaPredicateExecutor<User> predicateExecutor;
QUser user = new QUser("user");
User dave, carter, oliver;
Role adminRole;
@Before
public void setUp() {
JpaEntityInformation<User, Integer> information = new JpaMetamodelEntityInformation<>(User.class,
em.getMetamodel());
SimpleJpaRepository<User, Integer> repository = new SimpleJpaRepository<>(information, em);
dave = repository.save(new User("Dave", "Matthews", "dave@matthews.com"));
carter = repository.save(new User("Carter", "Beauford", "carter@beauford.com"));
oliver = repository.save(new User("Oliver", "matthews", "oliver@matthews.com"));
adminRole = em.merge(new Role("admin"));
this.predicateExecutor = new QuerydslJpaPredicateExecutor<>(information, em, SimpleEntityPathResolver.INSTANCE, null);
}
@Test
public void executesPredicatesCorrectly() throws Exception {
BooleanExpression isCalledDave = user.firstname.eq("Dave");
BooleanExpression isBeauford = user.lastname.eq("Beauford");
List<User> result = predicateExecutor.findAll(isCalledDave.or(isBeauford));
assertThat(result).containsExactlyInAnyOrder(carter, dave);
}
@Test
public void executesStringBasedPredicatesCorrectly() throws Exception {
PathBuilder<User> builder = new PathBuilderFactory().create(User.class);
BooleanExpression isCalledDave = builder.getString("firstname").eq("Dave");
BooleanExpression isBeauford = builder.getString("lastname").eq("Beauford");
List<User> result = predicateExecutor.findAll(isCalledDave.or(isBeauford));
assertThat(result).containsExactlyInAnyOrder(carter, dave);
}
@Test // DATAJPA-243
public void considersSortingProvidedThroughPageable() {
Predicate lastnameContainsE = user.lastname.contains("e");
Page<User> result = predicateExecutor.findAll(lastnameContainsE, PageRequest.of(0, 1, Direction.ASC, "lastname"));
assertThat(result).containsExactly(carter);
result = predicateExecutor.findAll(lastnameContainsE, PageRequest.of(0, 2, Direction.DESC, "lastname"));
assertThat(result).containsExactly(oliver, dave);
}
@Test // DATAJPA-296
public void appliesIgnoreCaseOrdering() {
Sort sort = Sort.by(new Order(Direction.DESC, "lastname").ignoreCase(), new Order(Direction.ASC, "firstname"));
Page<User> result = predicateExecutor.findAll(user.lastname.contains("e"), PageRequest.of(0, 2, sort));
assertThat(result.getContent()).containsExactly(dave, oliver);
}
@Test // DATAJPA-427
public void findBySpecificationWithSortByPluralAssociationPropertyInPageableShouldUseSortNullValuesLast() {
oliver.getColleagues().add(dave);
dave.getColleagues().add(oliver);
QUser user = QUser.user;
Page<User> page = predicateExecutor.findAll(user.firstname.isNotNull(),
PageRequest.of(0, 10, Sort.by(Sort.Direction.ASC, "colleagues.firstname")));
assertThat(page.getContent()).hasSize(3).contains(oliver, dave, carter);
}
@Test // DATAJPA-427
public void findBySpecificationWithSortBySingularAssociationPropertyInPageableShouldUseSortNullValuesLast() {
oliver.setManager(dave);
dave.setManager(carter);
QUser user = QUser.user;
Page<User> page = predicateExecutor.findAll(user.firstname.isNotNull(),
PageRequest.of(0, 10, Sort.by(Sort.Direction.ASC, "manager.firstname")));
assertThat(page.getContent()).hasSize(3).contains(dave, oliver, carter);
}
@Test // DATAJPA-427
public void findBySpecificationWithSortBySingularPropertyInPageableShouldUseSortNullValuesFirst() {
QUser user = QUser.user;
Page<User> page = predicateExecutor.findAll(user.firstname.isNotNull(),
PageRequest.of(0, 10, Sort.by(Sort.Direction.ASC, "firstname")));
assertThat(page.getContent()).containsExactly(carter, dave, oliver);
}
@Test // DATAJPA-427
public void findBySpecificationWithSortByOrderIgnoreCaseBySingularPropertyInPageableShouldUseSortNullValuesFirst() {
QUser user = QUser.user;
Page<User> page = predicateExecutor.findAll(user.firstname.isNotNull(),
PageRequest.of(0, 10, Sort.by(new Order(Sort.Direction.ASC, "firstname").ignoreCase())));
assertThat(page.getContent()).containsExactly(carter, dave, oliver);
}
@Test // DATAJPA-427
public void findBySpecificationWithSortByNestedEmbeddedPropertyInPageableShouldUseSortNullValuesFirst() {
oliver.setAddress(new Address("Germany", "Saarbrücken", "HaveItYourWay", "123"));
QUser user = QUser.user;
Page<User> page = predicateExecutor.findAll(user.firstname.isNotNull(),
PageRequest.of(0, 10, Sort.by(Sort.Direction.ASC, "address.streetName")));
assertThat(page.getContent()).containsExactly(dave, carter, oliver);
}
@Test // DATAJPA-12
public void findBySpecificationWithSortByQueryDslOrderSpecifierWithQPageRequestAndQSort() {
QUser user = QUser.user;
Page<User> page = predicateExecutor.findAll(user.firstname.isNotNull(),
new QPageRequest(0, 10, new QSort(user.firstname.asc())));
assertThat(page.getContent()).containsExactly(carter, dave, oliver);
}
@Test // DATAJPA-12
public void findBySpecificationWithSortByQueryDslOrderSpecifierWithQPageRequest() {
QUser user = QUser.user;
Page<User> page = predicateExecutor.findAll(user.firstname.isNotNull(), new QPageRequest(0, 10, user.firstname.asc()));
assertThat(page.getContent()).containsExactly(carter, dave, oliver);
}
@Test // DATAJPA-12
public void findBySpecificationWithSortByQueryDslOrderSpecifierForAssociationShouldGenerateLeftJoinWithQPageRequest() {
oliver.setManager(dave);
dave.setManager(carter);
QUser user = QUser.user;
Page<User> page = predicateExecutor.findAll(user.firstname.isNotNull(),
new QPageRequest(0, 10, user.manager.firstname.asc()));
assertThat(page.getContent()).containsExactly(carter, dave, oliver);
}
@Test // DATAJPA-500, DATAJPA-635
public void sortByNestedEmbeddedAttribute() {
carter.setAddress(new Address("U", "Z", "Y", "41"));
dave.setAddress(new Address("U", "A", "Y", "41"));
oliver.setAddress(new Address("G", "D", "X", "42"));
List<User> users = predicateExecutor.findAll(QUser.user.address.streetName.asc());
assertThat(users).hasSize(3).contains(dave, oliver, carter);
}
@Test // DATAJPA-566, DATAJPA-635
public void shouldSupportSortByOperatorWithDateExpressions() {
carter.setDateOfBirth(new LocalDate(2000, 2, 1).toDate());
dave.setDateOfBirth(new LocalDate(2000, 1, 1).toDate());
oliver.setDateOfBirth(new LocalDate(2003, 5, 1).toDate());
List<User> users = predicateExecutor.findAll(QUser.user.dateOfBirth.yearMonth().asc());
assertThat(users).containsExactly(dave, carter, oliver);
}
@Test // DATAJPA-665
public void shouldSupportExistsWithPredicate() throws Exception {
assertThat(predicateExecutor.exists(user.firstname.eq("Dave"))).isEqualTo(true);
assertThat(predicateExecutor.exists(user.firstname.eq("Unknown"))).isEqualTo(false);
assertThat(predicateExecutor.exists((Predicate) null)).isEqualTo(true);
}
@Test // DATAJPA-679
public void shouldSupportFindAllWithPredicateAndSort() {
List<User> users = predicateExecutor.findAll(user.dateOfBirth.isNull(), Sort.by(Direction.ASC, "firstname"));
assertThat(users).contains(carter, dave, oliver);
}
@Test // DATAJPA-585
public void worksWithUnpagedPageable() {
assertThat(predicateExecutor.findAll(user.dateOfBirth.isNull(), Pageable.unpaged()).getContent()).hasSize(3);
}
@Test // DATAJPA-912
public void pageableQueryReportsTotalFromResult() {
Page<User> firstPage = predicateExecutor.findAll(user.dateOfBirth.isNull(), PageRequest.of(0, 10));
assertThat(firstPage.getContent()).hasSize(3);
assertThat(firstPage.getTotalElements()).isEqualTo(3L);
Page<User> secondPage = predicateExecutor.findAll(user.dateOfBirth.isNull(), PageRequest.of(1, 2));
assertThat(secondPage.getContent()).hasSize(1);
assertThat(secondPage.getTotalElements()).isEqualTo(3L);
}
@Test // DATAJPA-912
public void pageableQueryReportsTotalFromCount() {
Page<User> firstPage = predicateExecutor.findAll(user.dateOfBirth.isNull(), PageRequest.of(0, 3));
assertThat(firstPage.getContent()).hasSize(3);
assertThat(firstPage.getTotalElements()).isEqualTo(3L);
Page<User> secondPage = predicateExecutor.findAll(user.dateOfBirth.isNull(), PageRequest.of(10, 10));
assertThat(secondPage.getContent()).hasSize(0);
assertThat(secondPage.getTotalElements()).isEqualTo(3L);
}
@Test // DATAJPA-1115
public void findOneWithPredicateReturnsResultCorrectly() {
assertThat(predicateExecutor.findOne(user.eq(dave))).contains(dave);
}
@Test // DATAJPA-1115
public void findOneWithPredicateReturnsOptionalEmptyWhenNoDataFound() {
assertThat(predicateExecutor.findOne(user.firstname.eq("batman"))).isNotPresent();
}
@Test(expected = IncorrectResultSizeDataAccessException.class) // DATAJPA-1115
public void findOneWithPredicateThrowsExceptionForNonUniqueResults() {
predicateExecutor.findOne(user.emailAddress.contains("com"));
}
}

View File

@@ -32,7 +32,7 @@ import org.springframework.transaction.TransactionException;
import org.springframework.transaction.TransactionStatus;
/**
* Integration test for transactional behaviour of repository operations.
* Integration test for transactional behaviour of predicateExecutor operations.
*
* @author Oliver Gierke
*/