Skip to content

Commit aa8c61a

Browse files
committed
#64 - Add Criteria API.
We now support Criteria creation and mapping to express where conditions with a fluent API. databaseClient.select().from("legoset") .where(Criteria.of("name").like("John%").and("id").lessThanOrEquals(42055));
1 parent 3db82a2 commit aa8c61a

15 files changed

+2027
-89
lines changed

src/main/java/org/springframework/data/r2dbc/function/DatabaseClient.java

+8
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929

3030
import org.springframework.data.domain.Pageable;
3131
import org.springframework.data.domain.Sort;
32+
import org.springframework.data.r2dbc.function.query.Criteria;
3233
import org.springframework.data.r2dbc.support.R2dbcExceptionTranslator;
3334

3435
/**
@@ -353,6 +354,13 @@ interface SelectSpec<S extends SelectSpec<S>> {
353354
*/
354355
S project(String... selectedFields);
355356

357+
/**
358+
* Configure a filter {@link Criteria}.
359+
*
360+
* @param criteria must not be {@literal null}.
361+
*/
362+
S where(Criteria criteria);
363+
356364
/**
357365
* Configure {@link Sort}.
358366
*

src/main/java/org/springframework/data/r2dbc/function/DefaultDatabaseClient.java

+39-19
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
import org.springframework.data.r2dbc.domain.SettableValue;
5757
import org.springframework.data.r2dbc.function.connectionfactory.ConnectionProxy;
5858
import org.springframework.data.r2dbc.function.convert.ColumnMapRowMapper;
59+
import org.springframework.data.r2dbc.function.query.Criteria;
5960
import org.springframework.data.r2dbc.support.R2dbcExceptionTranslator;
6061
import org.springframework.data.relational.core.sql.Insert;
6162
import org.springframework.lang.Nullable;
@@ -619,6 +620,7 @@ private abstract class DefaultSelectSpecSupport {
619620

620621
final String table;
621622
final List<String> projectedFields;
623+
final @Nullable Criteria criteria;
622624
final Sort sort;
623625
final Pageable page;
624626

@@ -628,6 +630,7 @@ private abstract class DefaultSelectSpecSupport {
628630

629631
this.table = table;
630632
this.projectedFields = Collections.emptyList();
633+
this.criteria = null;
631634
this.sort = Sort.unsorted();
632635
this.page = Pageable.unpaged();
633636
}
@@ -639,21 +642,28 @@ public DefaultSelectSpecSupport project(String... selectedFields) {
639642
projectedFields.addAll(this.projectedFields);
640643
projectedFields.addAll(Arrays.asList(selectedFields));
641644

642-
return createInstance(table, projectedFields, sort, page);
645+
return createInstance(table, projectedFields, criteria, sort, page);
646+
}
647+
648+
public DefaultSelectSpecSupport where(Criteria whereCriteria) {
649+
650+
Assert.notNull(whereCriteria, "Criteria must not be null!");
651+
652+
return createInstance(table, projectedFields, whereCriteria, sort, page);
643653
}
644654

645655
public DefaultSelectSpecSupport orderBy(Sort sort) {
646656

647657
Assert.notNull(sort, "Sort must not be null!");
648658

649-
return createInstance(table, projectedFields, sort, page);
659+
return createInstance(table, projectedFields, criteria, sort, page);
650660
}
651661

652662
public DefaultSelectSpecSupport page(Pageable page) {
653663

654664
Assert.notNull(page, "Pageable must not be null!");
655665

656-
return createInstance(table, projectedFields, sort, page);
666+
return createInstance(table, projectedFields, criteria, sort, page);
657667
}
658668

659669
<R> FetchSpec<R> execute(String sql, BiFunction<Row, RowMetadata, R> mappingFunction) {
@@ -676,14 +686,14 @@ <R> FetchSpec<R> execute(String sql, BiFunction<Row, RowMetadata, R> mappingFunc
676686
mappingFunction);
677687
}
678688

679-
protected abstract DefaultSelectSpecSupport createInstance(String table, List<String> projectedFields, Sort sort,
680-
Pageable page);
689+
protected abstract DefaultSelectSpecSupport createInstance(String table, List<String> projectedFields,
690+
Criteria criteria, Sort sort, Pageable page);
681691
}
682692

683693
private class DefaultGenericSelectSpec extends DefaultSelectSpecSupport implements GenericSelectSpec {
684694

685-
DefaultGenericSelectSpec(String table, List<String> projectedFields, Sort sort, Pageable page) {
686-
super(table, projectedFields, sort, page);
695+
DefaultGenericSelectSpec(String table, List<String> projectedFields, Criteria criteria, Sort sort, Pageable page) {
696+
super(table, projectedFields, criteria, sort, page);
687697
}
688698

689699
DefaultGenericSelectSpec(String table) {
@@ -695,7 +705,7 @@ public <R> TypedSelectSpec<R> as(Class<R> resultType) {
695705

696706
Assert.notNull(resultType, "Result type must not be null!");
697707

698-
return new DefaultTypedSelectSpec<>(table, projectedFields, sort, page, resultType,
708+
return new DefaultTypedSelectSpec<>(table, projectedFields, criteria, sort, page, resultType,
699709
dataAccessStrategy.getRowMapper(resultType));
700710
}
701711

@@ -712,6 +722,11 @@ public DefaultGenericSelectSpec project(String... selectedFields) {
712722
return (DefaultGenericSelectSpec) super.project(selectedFields);
713723
}
714724

725+
@Override
726+
public DefaultGenericSelectSpec where(Criteria criteria) {
727+
return (DefaultGenericSelectSpec) super.where(criteria);
728+
}
729+
715730
@Override
716731
public DefaultGenericSelectSpec orderBy(Sort sort) {
717732
return (DefaultGenericSelectSpec) super.orderBy(sort);
@@ -735,9 +750,9 @@ private <R> FetchSpec<R> exchange(BiFunction<Row, RowMetadata, R> mappingFunctio
735750
}
736751

737752
@Override
738-
protected DefaultGenericSelectSpec createInstance(String table, List<String> projectedFields, Sort sort,
739-
Pageable page) {
740-
return new DefaultGenericSelectSpec(table, projectedFields, sort, page);
753+
protected DefaultGenericSelectSpec createInstance(String table, List<String> projectedFields, Criteria criteria,
754+
Sort sort, Pageable page) {
755+
return new DefaultGenericSelectSpec(table, projectedFields, criteria, sort, page);
741756
}
742757
}
743758

@@ -758,14 +773,14 @@ private class DefaultTypedSelectSpec<T> extends DefaultSelectSpecSupport impleme
758773
this.mappingFunction = dataAccessStrategy.getRowMapper(typeToRead);
759774
}
760775

761-
DefaultTypedSelectSpec(String table, List<String> projectedFields, Sort sort, Pageable page,
776+
DefaultTypedSelectSpec(String table, List<String> projectedFields, Criteria criteria, Sort sort, Pageable page,
762777
BiFunction<Row, RowMetadata, T> mappingFunction) {
763-
this(table, projectedFields, sort, page, null, mappingFunction);
778+
this(table, projectedFields, criteria, sort, page, null, mappingFunction);
764779
}
765780

766-
DefaultTypedSelectSpec(String table, List<String> projectedFields, Sort sort, Pageable page, Class<T> typeToRead,
767-
BiFunction<Row, RowMetadata, T> mappingFunction) {
768-
super(table, projectedFields, sort, page);
781+
DefaultTypedSelectSpec(String table, List<String> projectedFields, Criteria criteria, Sort sort, Pageable page,
782+
Class<T> typeToRead, BiFunction<Row, RowMetadata, T> mappingFunction) {
783+
super(table, projectedFields, criteria, sort, page);
769784
this.typeToRead = typeToRead;
770785
this.mappingFunction = mappingFunction;
771786
}
@@ -791,6 +806,11 @@ public DefaultTypedSelectSpec<T> project(String... selectedFields) {
791806
return (DefaultTypedSelectSpec<T>) super.project(selectedFields);
792807
}
793808

809+
@Override
810+
public DefaultTypedSelectSpec<T> where(Criteria criteria) {
811+
return (DefaultTypedSelectSpec<T>) super.where(criteria);
812+
}
813+
794814
@Override
795815
public DefaultTypedSelectSpec<T> orderBy(Sort sort) {
796816
return (DefaultTypedSelectSpec<T>) super.orderBy(sort);
@@ -822,9 +842,9 @@ private <R> FetchSpec<R> exchange(BiFunction<Row, RowMetadata, R> mappingFunctio
822842
}
823843

824844
@Override
825-
protected DefaultTypedSelectSpec<T> createInstance(String table, List<String> projectedFields, Sort sort,
826-
Pageable page) {
827-
return new DefaultTypedSelectSpec<>(table, projectedFields, sort, page, typeToRead, mappingFunction);
845+
protected DefaultTypedSelectSpec<T> createInstance(String table, List<String> projectedFields, Criteria criteria,
846+
Sort sort, Pageable page) {
847+
return new DefaultTypedSelectSpec<>(table, projectedFields, criteria, sort, page, typeToRead, mappingFunction);
828848
}
829849
}
830850

src/main/java/org/springframework/data/r2dbc/function/DefaultReactiveDataAccessStrategy.java

+109-53
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import io.r2dbc.spi.Row;
1919
import io.r2dbc.spi.RowMetadata;
20+
import io.r2dbc.spi.Statement;
2021

2122
import java.util.ArrayList;
2223
import java.util.Collection;
@@ -34,6 +35,7 @@
3435
import org.springframework.data.domain.Sort.Order;
3536
import org.springframework.data.mapping.context.MappingContext;
3637
import org.springframework.data.r2dbc.dialect.ArrayColumns;
38+
import org.springframework.data.r2dbc.dialect.BindMarkers;
3739
import org.springframework.data.r2dbc.dialect.BindMarkersFactory;
3840
import org.springframework.data.r2dbc.dialect.Dialect;
3941
import org.springframework.data.r2dbc.domain.OutboundRow;
@@ -42,14 +44,17 @@
4244
import org.springframework.data.r2dbc.function.convert.MappingR2dbcConverter;
4345
import org.springframework.data.r2dbc.function.convert.R2dbcConverter;
4446
import org.springframework.data.r2dbc.function.convert.R2dbcCustomConversions;
47+
import org.springframework.data.r2dbc.function.query.ConditionBindings;
48+
import org.springframework.data.r2dbc.function.query.Criteria;
49+
import org.springframework.data.r2dbc.function.query.CriteriaMapper;
4550
import org.springframework.data.r2dbc.support.StatementRenderUtil;
4651
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
4752
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
4853
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
4954
import org.springframework.data.relational.core.sql.Expression;
5055
import org.springframework.data.relational.core.sql.OrderByField;
5156
import org.springframework.data.relational.core.sql.Select;
52-
import org.springframework.data.relational.core.sql.SelectBuilder.SelectFromAndOrderBy;
57+
import org.springframework.data.relational.core.sql.SelectBuilder;
5358
import org.springframework.data.relational.core.sql.StatementBuilder;
5459
import org.springframework.data.relational.core.sql.Table;
5560
import org.springframework.data.relational.core.sql.render.NamingStrategies;
@@ -69,6 +74,7 @@ public class DefaultReactiveDataAccessStrategy implements ReactiveDataAccessStra
6974

7075
private final Dialect dialect;
7176
private final R2dbcConverter converter;
77+
private final CriteriaMapper criteriaMapper;
7278
private final MappingContext<RelationalPersistentEntity<?>, ? extends RelationalPersistentProperty> mappingContext;
7379
private final StatementFactory statements;
7480

@@ -115,6 +121,7 @@ public DefaultReactiveDataAccessStrategy(Dialect dialect, R2dbcConverter convert
115121
Assert.notNull(converter, "RelationalConverter must not be null");
116122

117123
this.converter = converter;
124+
this.criteriaMapper = new CriteriaMapper(converter);
118125
this.mappingContext = (MappingContext<RelationalPersistentEntity<?>, ? extends RelationalPersistentProperty>) this.converter
119126
.getMappingContext();
120127
this.dialect = dialect;
@@ -238,6 +245,107 @@ public Sort getMappedSort(Class<?> typeToRead, Sort sort) {
238245
return Sort.by(mappedOrder);
239246
}
240247

248+
@Override
249+
public PreparedOperation<Select> select(String tableName, @Nullable Class<?> entityType, Set<String> columns,
250+
Sort sort, Pageable page, Criteria criteria) {
251+
252+
Table table = Table.create(tableName);
253+
254+
Collection<? extends Expression> selectList;
255+
256+
if (columns.isEmpty()) {
257+
selectList = Collections.singletonList(table.asterisk());
258+
} else {
259+
selectList = table.columns(columns);
260+
}
261+
262+
SelectBuilder.SelectFromAndJoin selectBuilder = StatementBuilder //
263+
.select(selectList) //
264+
.from(table);//
265+
266+
ConditionBindings conditionBindings;
267+
268+
if (criteria != null) {
269+
270+
BindMarkers bindMarkers = dialect.getBindMarkersFactory().create();
271+
conditionBindings = criteriaMapper.getMappedObject(bindMarkers, criteria, table,
272+
entityType != null ? getRequiredPersistentEntity(entityType) : null);
273+
274+
selectBuilder.where(conditionBindings.getCondition());
275+
} else {
276+
conditionBindings = null;
277+
}
278+
279+
selectBuilder.orderBy(createOrderByFields(table, sort));
280+
281+
OptionalLong limit = OptionalLong.empty();
282+
OptionalLong offset = OptionalLong.empty();
283+
284+
if (page.isPaged()) {
285+
limit = OptionalLong.of(page.getPageSize());
286+
offset = OptionalLong.of(page.getOffset());
287+
}
288+
289+
Select select = selectBuilder.build();
290+
String sql = StatementRenderUtil.render(select, limit, offset, this.dialect);
291+
292+
if (conditionBindings == null) {
293+
294+
return new PreparedOperation<Select>() {
295+
@Override
296+
public Select getSource() {
297+
return select;
298+
}
299+
300+
@Override
301+
public Statement bind(Statement to) {
302+
return to;
303+
}
304+
305+
@Override
306+
public String toQuery() {
307+
return sql;
308+
}
309+
};
310+
}
311+
312+
return new PreparedOperation<Select>() {
313+
@Override
314+
public Select getSource() {
315+
return select;
316+
}
317+
318+
@Override
319+
public Statement bind(Statement to) {
320+
conditionBindings.getBindings().apply(to);
321+
return to;
322+
}
323+
324+
@Override
325+
public String toQuery() {
326+
return sql;
327+
}
328+
};
329+
}
330+
331+
private Collection<? extends OrderByField> createOrderByFields(Table table, Sort sortToUse) {
332+
333+
List<OrderByField> fields = new ArrayList<>();
334+
335+
for (Sort.Order order : sortToUse) {
336+
337+
OrderByField orderByField = OrderByField.from(table.column(order.getProperty()));
338+
339+
if (order.getDirection() != null) {
340+
fields.add(order.isAscending() ? orderByField.asc() : orderByField.desc());
341+
} else {
342+
fields.add(orderByField);
343+
}
344+
}
345+
346+
return fields;
347+
}
348+
241349
/*
242350
* (non-Javadoc)
243351
* @see org.springframework.data.r2dbc.function.ReactiveDataAccessStrategy#getRowMapper(java.lang.Class)
@@ -282,56 +390,4 @@ private RelationalPersistentEntity<?> getRequiredPersistentEntity(Class<?> typeT
282390
private RelationalPersistentEntity<?> getPersistentEntity(Class<?> typeToRead) {
283391
return mappingContext.getPersistentEntity(typeToRead);
284392
}
285-
286-
/*
287-
* (non-Javadoc)
288-
* @see org.springframework.data.r2dbc.function.ReactiveDataAccessStrategy#select(java.lang.String, java.util.Set, org.springframework.data.domain.Sort, org.springframework.data.domain.Pageable)
289-
*/
290-
@Override
291-
public String select(String tableName, Set<String> columns, Sort sort, Pageable page) {
292-
293-
Table table = Table.create(tableName);
294-
295-
Collection<? extends Expression> selectList;
296-
297-
if (columns.isEmpty()) {
298-
selectList = Collections.singletonList(table.asterisk());
299-
} else {
300-
selectList = table.columns(columns);
301-
}
302-
303-
SelectFromAndOrderBy selectBuilder = StatementBuilder //
304-
.select(selectList) //
305-
.from(tableName) //
306-
.orderBy(createOrderByFields(table, sort));
307-
308-
OptionalLong limit = OptionalLong.empty();
309-
OptionalLong offset = OptionalLong.empty();
310-
311-
if (page.isPaged()) {
312-
limit = OptionalLong.of(page.getPageSize());
313-
offset = OptionalLong.of(page.getOffset());
314-
}
315-
316-
// See https://github.com/spring-projects/spring-data-r2dbc/issues/55
317-
return StatementRenderUtil.render(selectBuilder.build(), limit, offset, this.dialect);
318-
}
319-
320-
private Collection<? extends OrderByField> createOrderByFields(Table table, Sort sortToUse) {
321-
322-
List<OrderByField> fields = new ArrayList<>();
323-
324-
for (Order order : sortToUse) {
325-
326-
OrderByField orderByField = OrderByField.from(table.column(order.getProperty()));
327-
328-
if (order.getDirection() != null) {
329-
fields.add(order.isAscending() ? orderByField.asc() : orderByField.desc());
330-
} else {
331-
fields.add(orderByField);
332-
}
333-
}
334-
335-
return fields;
336-
}
337393
}

0 commit comments

Comments
 (0)