Skip to content

Commit

Permalink
#344 - Add support for support distinct derived query methods.
Browse files Browse the repository at this point in the history
We now support distinct derived queries that are useful with projections to retrieve distinct results.

interface UserRepository extends Repository<User, Long> {

  Mono<UserProjection> findDistinctByFirstName(String firstName);

}

interface UserProjection {

  String getFirstName();

}

Original pull request: #346.
  • Loading branch information
m1ngyuan authored and mp911de committed Apr 21, 2020
1 parent ea464b2 commit dd7c713
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 15 deletions.
Expand Up @@ -41,6 +41,7 @@
*
* @author Mark Paluch
* @author Roman Chigvintsev
* @author Mingyuan Wu
*/
class DefaultStatementMapper implements StatementMapper {

Expand Down Expand Up @@ -84,8 +85,11 @@ private PreparedOperation<Select> getMappedObject(SelectSpec selectSpec,
@Nullable RelationalPersistentEntity<?> entity) {

Table table = selectSpec.getTable();
SelectBuilder.SelectFromAndJoin selectBuilder = StatementBuilder.select(getSelectList(selectSpec, entity))
.from(table);
SelectBuilder.SelectAndFrom selectAndFrom = StatementBuilder.select(getSelectList(selectSpec, entity));
if(selectSpec.isDistinct()){
selectAndFrom = selectAndFrom.distinct();
}
SelectBuilder.SelectFromAndJoin selectBuilder = selectAndFrom.from(table);

BindMarkers bindMarkers = this.dialect.getBindMarkersFactory().create();
Bindings bindings = Bindings.empty();
Expand Down
Expand Up @@ -202,16 +202,18 @@ class SelectSpec {
private final Sort sort;
private final long offset;
private final int limit;
private boolean distinct = false;

protected SelectSpec(Table table, List<String> projectedFields, List<Expression> selectList,
@Nullable CriteriaDefinition criteria, Sort sort, int limit, long offset) {
@Nullable CriteriaDefinition criteria, Sort sort, int limit, long offset, boolean distinct) {
this.table = table;
this.projectedFields = projectedFields;
this.selectList = selectList;
this.criteria = criteria;
this.sort = sort;
this.offset = offset;
this.limit = limit;
this.distinct = distinct;
}

/**
Expand All @@ -236,7 +238,7 @@ public static SelectSpec create(SqlIdentifier table) {
List<String> projectedFields = Collections.emptyList();
List<Expression> selectList = Collections.emptyList();
return new SelectSpec(Table.create(table), projectedFields, selectList, Criteria.empty(), Sort.unsorted(), -1,
-1);
-1, false);
}

public SelectSpec doWithTable(BiFunction<Table, SelectSpec, SelectSpec> function) {
Expand Down Expand Up @@ -277,7 +279,7 @@ public SelectSpec withProjection(Expression... expressions) {
List<Expression> selectList = new ArrayList<>(this.selectList);
selectList.addAll(Arrays.asList(expressions));

return new SelectSpec(this.table, projectedFields, selectList, this.criteria, this.sort, this.limit, this.offset);
return new SelectSpec(this.table, projectedFields, selectList, this.criteria, this.sort, this.limit, this.offset, this.distinct);
}

/**
Expand All @@ -293,7 +295,7 @@ public SelectSpec withProjection(Collection<Expression> projectedFields) {
selectList.addAll(projectedFields);

return new SelectSpec(this.table, this.projectedFields, selectList, this.criteria, this.sort, this.limit,
this.offset);
this.offset, this.distinct);
}

/**
Expand All @@ -304,7 +306,7 @@ public SelectSpec withProjection(Collection<Expression> projectedFields) {
*/
public SelectSpec withCriteria(CriteriaDefinition criteria) {
return new SelectSpec(this.table, this.projectedFields, this.selectList, criteria, this.sort, this.limit,
this.offset);
this.offset, this.distinct);
}

/**
Expand All @@ -317,11 +319,11 @@ public SelectSpec withSort(Sort sort) {

if (sort.isSorted()) {
return new SelectSpec(this.table, this.projectedFields, this.selectList, this.criteria, sort, this.limit,
this.offset);
this.offset, this.distinct);
}

return new SelectSpec(this.table, this.projectedFields, this.selectList, this.criteria, this.sort, this.limit,
this.offset);
this.offset, this.distinct);
}

/**
Expand All @@ -337,33 +339,44 @@ public SelectSpec withPage(Pageable page) {
Sort sort = page.getSort();

return new SelectSpec(this.table, this.projectedFields, this.selectList, this.criteria,
sort.isSorted() ? sort : this.sort, page.getPageSize(), page.getOffset());
sort.isSorted() ? sort : this.sort, page.getPageSize(), page.getOffset(), this.distinct);
}

return new SelectSpec(this.table, this.projectedFields, this.selectList, this.criteria, this.sort, this.limit,
this.offset);
this.offset, this.distinct);
}

/**
* Associate a result offset with the select and create a new {@link SelectSpec}.
*
* @param page
* @param offset
* @return the {@link SelectSpec}.
*/
public SelectSpec offset(long offset) {
return new SelectSpec(this.table, this.projectedFields, this.selectList, this.criteria, this.sort, this.limit,
offset);
offset, this.distinct);
}

/**
* Associate a result limit with the select and create a new {@link SelectSpec}.
*
* @param page
* @param limit
* @return the {@link SelectSpec}.
*/
public SelectSpec limit(int limit) {
return new SelectSpec(this.table, this.projectedFields, this.selectList, this.criteria, this.sort, limit,
this.offset);
this.offset, this.distinct);
}

/**
* Associate a result statement distinct with the select and create a new {@link SelectSpec}.
*
* @param distinct
* @return the {@link SelectSpec}.
*/
public SelectSpec distinct(boolean distinct) {
return new SelectSpec(this.table, this.projectedFields, this.selectList, this.criteria, this.sort, limit,
this.offset, distinct);
}

public Table getTable() {
Expand Down Expand Up @@ -399,6 +412,10 @@ public long getOffset() {
public int getLimit() {
return this.limit;
}

public boolean isDistinct() {
return this.distinct;
}
}

/**
Expand Down
Expand Up @@ -123,6 +123,10 @@ private PreparedOperation<?> select(Criteria criteria, Sort sort, StatementMappe
selectSpec = selectSpec.withSort(getSort(sort));
}

if(tree.isDistinct()){
selectSpec = selectSpec.distinct(true);
}

return statementMapper.getMappedObject(selectSpec);
}

Expand Down
Expand Up @@ -63,6 +63,7 @@ public class PartTreeR2dbcQueryUnitTests {
private static final String TABLE = "users";
private static final String ALL_FIELDS = TABLE + ".id, " + TABLE + ".first_name, " + TABLE + ".last_name, " + TABLE
+ ".date_of_birth, " + TABLE + ".age, " + TABLE + ".active";
private static final String DISTINCT = "DISTINCT";

@Mock ConnectionFactory connectionFactory;
@Mock R2dbcConverter r2dbcConverter;
Expand Down Expand Up @@ -605,6 +606,19 @@ public void createsQueryToDeleteByFirstName() throws Exception {
assertThat(bindableQuery.get()).isEqualTo("DELETE FROM " + TABLE + " WHERE " + TABLE + ".first_name = $1");
}

@Test // gh-344
public void createsQueryToFindAllEntitiesByStringAttributeWithDistinct() throws Exception {

R2dbcQueryMethod queryMethod = getQueryMethod("findDistinctByFirstName", String.class);
PartTreeR2dbcQuery r2dbcQuery = new PartTreeR2dbcQuery(queryMethod, databaseClient, r2dbcConverter,
dataAccessStrategy);
BindableQuery bindableQuery = r2dbcQuery.createQuery(getAccessor(queryMethod, new Object[] { "John" }));

assertThat(bindableQuery.get())
.isEqualTo("SELECT " + DISTINCT + " " + ALL_FIELDS + " FROM " + TABLE + " WHERE " + TABLE + ".first_name = $1");
}


private R2dbcQueryMethod getQueryMethod(String methodName, Class<?>... parameterTypes) throws Exception {
Method method = UserRepository.class.getMethod(methodName, parameterTypes);
return new R2dbcQueryMethod(method, new DefaultRepositoryMetadata(UserRepository.class),
Expand Down Expand Up @@ -682,6 +696,8 @@ interface UserRepository extends Repository<User, Long> {
Flux<User> findTop3ByFirstName(String firstName);

Mono<User> findFirstByFirstName(String firstName);

Mono<User> findDistinctByFirstName(String firstName);

Mono<Integer> deleteByFirstName(String firstName);
}
Expand Down

0 comments on commit dd7c713

Please sign in to comment.