Add fluent API support for QuerydslKeyValueRepository.

Also reuse ProjectionFactory from repository factory.

Closes #397.
This commit is contained in:
Mark Paluch
2021-10-11 11:31:58 +02:00
parent b9b5aae60b
commit 10f63387df
5 changed files with 517 additions and 33 deletions

View File

@@ -17,9 +17,14 @@ package org.springframework.data.map;
import static org.assertj.core.api.Assertions.*;
import lombok.Data;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Stream;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
@@ -30,27 +35,30 @@ import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.keyvalue.Person;
import org.springframework.data.keyvalue.QPerson;
import org.springframework.data.keyvalue.repository.support.KeyValueRepositoryFactory;
import org.springframework.data.keyvalue.repository.support.QuerydslKeyValueRepository;
import org.springframework.data.map.QuerydslKeyValueRepositoryUnitTests.QPersonRepository;
import org.springframework.data.map.QuerydslKeyValuePredicateExecutorUnitTests.QPersonRepository;
import org.springframework.data.querydsl.QSort;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import org.springframework.data.repository.query.FluentQuery;
import org.springframework.data.util.Streamable;
/**
* Unit tests for {@link QuerydslKeyValueRepository}.
* Unit tests for {@link org.springframework.data.keyvalue.repository.support.QuerydslKeyValuePredicateExecutor}.
*
* @author Christoph Strobl
* @author Oliver Gierke
* @author Thomas Darimont
* @author Mark Paluch
*/
public class QuerydslKeyValueRepositoryUnitTests extends AbstractRepositoryUnitTests<QPersonRepository> {
class QuerydslKeyValuePredicateExecutorUnitTests extends AbstractRepositoryUnitTests<QPersonRepository> {
@BeforeEach
void setUp() {
repository.saveAll(LENNISTERS);
}
@Test // DATACMNS-525
void findOneIsExecutedCorrectly() {
repository.saveAll(LENNISTERS);
Optional<Person> result = repository.findOne(QPerson.person.firstname.eq(CERSEI.getFirstname()));
assertThat(result).hasValue(CERSEI);
}
@@ -58,8 +66,6 @@ public class QuerydslKeyValueRepositoryUnitTests extends AbstractRepositoryUnitT
@Test // DATACMNS-525
void findAllIsExecutedCorrectly() {
repository.saveAll(LENNISTERS);
Iterable<Person> result = repository.findAll(QPerson.person.age.eq(CERSEI.getAge()));
assertThat(result).contains(CERSEI, JAIME);
}
@@ -67,7 +73,6 @@ public class QuerydslKeyValueRepositoryUnitTests extends AbstractRepositoryUnitT
@Test // DATACMNS-525
void findWithPaginationWorksCorrectly() {
repository.saveAll(LENNISTERS);
Page<Person> page1 = repository.findAll(QPerson.person.age.eq(CERSEI.getAge()), PageRequest.of(0, 1));
assertThat(page1.getTotalElements()).isEqualTo(2L);
@@ -84,8 +89,6 @@ public class QuerydslKeyValueRepositoryUnitTests extends AbstractRepositoryUnitT
@Test // DATACMNS-525
void findAllUsingOrderSpecifierWorksCorrectly() {
repository.saveAll(LENNISTERS);
Iterable<Person> result = repository.findAll(QPerson.person.age.eq(CERSEI.getAge()),
QPerson.person.firstname.desc());
@@ -95,8 +98,6 @@ public class QuerydslKeyValueRepositoryUnitTests extends AbstractRepositoryUnitT
@Test // DATACMNS-525
void findAllUsingPageableWithSortWorksCorrectly() {
repository.saveAll(LENNISTERS);
Iterable<Person> result = repository.findAll(QPerson.person.age.eq(CERSEI.getAge()),
PageRequest.of(0, 10, Direction.DESC, "firstname"));
@@ -106,8 +107,6 @@ public class QuerydslKeyValueRepositoryUnitTests extends AbstractRepositoryUnitT
@Test // DATACMNS-525
void findAllUsingPagableWithQSortWorksCorrectly() {
repository.saveAll(LENNISTERS);
Iterable<Person> result = repository.findAll(QPerson.person.age.eq(CERSEI.getAge()),
PageRequest.of(0, 10, new QSort(QPerson.person.firstname.desc())));
@@ -117,8 +116,6 @@ public class QuerydslKeyValueRepositoryUnitTests extends AbstractRepositoryUnitT
@Test // DATAKV-90
void findAllWithOrderSpecifierWorksCorrectly() {
repository.saveAll(LENNISTERS);
Iterable<Person> result = repository.findAll(new QSort(QPerson.person.firstname.desc()));
assertThat(result).containsExactly(TYRION, JAIME, CERSEI);
@@ -132,8 +129,6 @@ public class QuerydslKeyValueRepositoryUnitTests extends AbstractRepositoryUnitT
@Test // DATAKV-90, DATAKV-197
void findAllShouldAllowUnsortedFindAll() {
repository.saveAll(LENNISTERS);
Iterable<Person> result = repository.findAll(Sort.unsorted());
assertThat(result).contains(TYRION, JAIME, CERSEI);
@@ -141,17 +136,12 @@ public class QuerydslKeyValueRepositoryUnitTests extends AbstractRepositoryUnitT
@Test // DATAKV-95
void executesExistsCorrectly() {
repository.saveAll(LENNISTERS);
assertThat(repository.exists(QPerson.person.age.eq(CERSEI.getAge()))).isTrue();
}
@Test // DATAKV-96
void shouldSupportFindAllWithPredicateAndSort() {
repository.saveAll(LENNISTERS);
List<Person> users = Streamable.of(repository.findAll(person.age.gt(0), Sort.by(Direction.ASC, "firstname")))
.toList();
@@ -164,12 +154,148 @@ public class QuerydslKeyValueRepositoryUnitTests extends AbstractRepositoryUnitT
@Test // DATAKV-179
void throwsExceptionIfMoreThanOneResultIsFound() {
repository.saveAll(LENNISTERS);
assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class) //
.isThrownBy(() -> repository.findOne(person.firstname.contains("e")));
}
@Test // GH-397
void findByShouldReturnFirst() {
Person first = repository.findBy(QPerson.person.firstname.eq("tyrion"),
FluentQuery.FetchableFluentQuery::firstValue);
assertThat(first).isEqualTo(TYRION);
first = repository.findBy(QPerson.person.firstname.eq("foo"), Function.identity()).firstValue();
assertThat(first).isNull();
}
@Test // GH-397
void findByShouldReturnOne() {
assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class)
.isThrownBy(() -> repository.findBy(QPerson.person.firstname.ne("foo"), FluentQuery.FetchableFluentQuery::one));
Person one = repository.findBy(QPerson.person.firstname.eq("tyrion"), FluentQuery.FetchableFluentQuery::oneValue);
assertThat(one).isEqualTo(TYRION);
}
@Test // GH-397
void findByShouldReturnFirstWithProjection() {
PersonProjection interfaceProjection = repository.findBy(QPerson.person.firstname.eq("tyrion"),
it -> it.as(PersonProjection.class).firstValue());
assertThat(interfaceProjection.getFirstname()).isEqualTo("tyrion");
PersonDto dto = repository.findBy(QPerson.person.firstname.eq("tyrion"), it -> it.as(PersonDto.class).firstValue());
assertThat(dto.getFirstname()).isEqualTo("tyrion");
}
@Test // GH-397
void findByShouldReturnOneWithProjection() {
PersonProjection interfaceProjection = repository.findBy(QPerson.person.firstname.eq("tyrion"),
it -> it.as(PersonProjection.class).oneValue());
assertThat(interfaceProjection.getFirstname()).isEqualTo("tyrion");
PersonDto dto = repository.findBy(QPerson.person.firstname.eq("tyrion"), it -> it.as(PersonDto.class).oneValue());
assertThat(dto.getFirstname()).isEqualTo("tyrion");
}
@Test // GH-397
void findByShouldReturnAll() {
List<Person> all = repository.findBy(QPerson.person.firstname.eq("tyrion"), FluentQuery.FetchableFluentQuery::all);
assertThat(all).contains(TYRION);
}
@Test // GH-397
void findByShouldReturnAllSorted() {
List<Person> all = repository.findBy(QPerson.person.firstname.ne("foo"),
q -> q.sortBy(Sort.by(Direction.ASC, "firstname")).all());
assertThat(all).containsSequence(CERSEI, JAIME, TYRION);
all = repository.findBy(QPerson.person.firstname.ne("foo"),
q -> q.sortBy(Sort.by(Direction.DESC, "firstname")).all());
assertThat(all).containsSequence(TYRION, JAIME, CERSEI);
}
@Test // GH-397
void findByShouldReturnAllWithProjection() {
Stream<PersonProjection> all = repository.findBy(QPerson.person.firstname.eq("tyrion"),
q -> q.as(PersonProjection.class).stream());
assertThat(all).hasOnlyElementsOfType(PersonProjection.class);
}
@Test // GH-397
void findByShouldReturnPage() {
Page<PersonProjection> page = repository.findBy(QPerson.person.firstname.ne("foo"),
it -> it.as(PersonProjection.class).page(PageRequest.of(0, 1, Sort.by("firstname"))));
assertThat(page.getContent().get(0).getFirstname()).isEqualTo("cersei");
assertThat(page.getTotalPages()).isEqualTo(3);
Page<PersonProjection> nextPage = repository.findBy(QPerson.person.firstname.ne("foo"),
it -> it.as(PersonProjection.class).page(page.nextPageable()));
assertThat(nextPage.getContent().get(0).getFirstname()).isEqualTo("jaime");
assertThat(nextPage.getTotalPages()).isEqualTo(3);
}
@Test // GH-397
void findByShouldReturnStream() {
List<Person> all = repository.findBy(QPerson.person.firstname.eq("tyrion"), FluentQuery.FetchableFluentQuery::all);
assertThat(all).contains(TYRION);
}
@Test // GH-397
void findByShouldReturnStreamWithProjection() {
Stream<PersonProjection> all = repository.findBy(QPerson.person.firstname.eq("tyrion"),
q -> q.as(PersonProjection.class).stream());
assertThat(all).hasOnlyElementsOfType(PersonProjection.class);
}
@Test // GH-397
void findByShouldReturnCount() {
long count = repository.findBy(QPerson.person.firstname.ne("foo"), FluentQuery.FetchableFluentQuery::count);
assertThat(count).isEqualTo(3);
}
@Test // GH-397
void findByShouldReturnExists() {
boolean exists = repository.findBy(QPerson.person.firstname.eq("tyrion"), FluentQuery.FetchableFluentQuery::exists);
assertThat(exists).isTrue();
exists = repository.findBy(QPerson.person.firstname.eq("foo"), FluentQuery.FetchableFluentQuery::exists);
assertThat(exists).isFalse();
}
interface PersonProjection {
String getFirstname();
}
@Data
static class PersonDto {
String firstname;
}
/*
* (non-Javadoc)
* @see org.springframework.data.map.SimpleKeyValueRepositoryUnitTests#getRepository(org.springframework.data.keyvalue.repository.support.KeyValueRepositoryFactory)