Skip to content

Commit 01eccbb

Browse files
committed
#220 - Refactor StatementMapper.
Use limit/offset instead of Page and accept Expression objects to declare a select list. Use SqlIdentifier in Update, Query, Criteria and fluent API. Original pull request: #287.
1 parent 64387d1 commit 01eccbb

23 files changed

+425
-235
lines changed

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

+6-6
Original file line numberDiff line numberDiff line change
@@ -818,8 +818,8 @@ private <R> FetchSpec<R> exchange(BiFunction<Row, RowMetadata, R> mappingFunctio
818818

819819
StatementMapper mapper = dataAccessStrategy.getStatementMapper();
820820

821-
StatementMapper.SelectSpec selectSpec = mapper.createSelect(this.table).withProjection(this.projectedFields)
822-
.withSort(this.sort).withPage(this.page);
821+
StatementMapper.SelectSpec selectSpec = mapper.createSelect(this.table)
822+
.withProjection(this.projectedFields.toArray(new SqlIdentifier[0])).withSort(this.sort).withPage(this.page);
823823

824824
if (this.criteria != null) {
825825
selectSpec = selectSpec.withCriteria(this.criteria);
@@ -931,8 +931,8 @@ private <R> FetchSpec<R> exchange(BiFunction<Row, RowMetadata, R> mappingFunctio
931931
columns = this.projectedFields;
932932
}
933933

934-
StatementMapper.SelectSpec selectSpec = mapper.createSelect(this.table).withProjection(columns)
935-
.withPage(this.page).withSort(this.sort);
934+
StatementMapper.SelectSpec selectSpec = mapper.createSelect(this.table)
935+
.withProjection(columns.toArray(new SqlIdentifier[0])).withPage(this.page).withSort(this.sort);
936936

937937
if (this.criteria != null) {
938938
selectSpec = selectSpec.withCriteria(this.criteria);
@@ -1038,7 +1038,7 @@ private <R> FetchSpec<R> exchange(BiFunction<Row, RowMetadata, R> mappingFunctio
10381038
StatementMapper.InsertSpec insert = mapper.createInsert(this.table);
10391039

10401040
for (SqlIdentifier column : this.byName.keySet()) {
1041-
insert = insert.withColumn(dataAccessStrategy.toSql(column), this.byName.get(column));
1041+
insert = insert.withColumn(column, this.byName.get(column));
10421042
}
10431043

10441044
PreparedOperation<?> operation = mapper.getMappedObject(insert);
@@ -1161,7 +1161,7 @@ private <MR> FetchSpec<MR> exchange(Object toInsert, BiFunction<Row, RowMetadata
11611161
for (SqlIdentifier column : outboundRow.keySet()) {
11621162
SettableValue settableValue = outboundRow.get(column);
11631163
if (settableValue.hasValue()) {
1164-
insert = insert.withColumn(dataAccessStrategy.toSql(column), settableValue);
1164+
insert = insert.withColumn(column, settableValue);
11651165
}
11661166
}
11671167

src/main/java/org/springframework/data/r2dbc/core/DefaultStatementMapper.java

+19-23
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,8 @@
1616
package org.springframework.data.r2dbc.core;
1717

1818
import java.util.ArrayList;
19-
import java.util.Collection;
2019
import java.util.List;
2120

22-
import org.springframework.data.domain.Pageable;
23-
import org.springframework.data.domain.Sort;
2421
import org.springframework.data.mapping.context.MappingContext;
2522
import org.springframework.data.r2dbc.dialect.BindMarkers;
2623
import org.springframework.data.r2dbc.dialect.BindTarget;
@@ -84,9 +81,9 @@ public PreparedOperation<?> getMappedObject(SelectSpec selectSpec) {
8481
private PreparedOperation<Select> getMappedObject(SelectSpec selectSpec,
8582
@Nullable RelationalPersistentEntity<?> entity) {
8683

87-
Table table = Table.create(toSql(selectSpec.getTable()));
88-
List<Column> columns = table.columns(toSql(selectSpec.getProjectedFields()));
89-
SelectBuilder.SelectFromAndJoin selectBuilder = StatementBuilder.select(columns).from(table);
84+
Table table = selectSpec.getTable();
85+
SelectBuilder.SelectFromAndJoin selectBuilder = StatementBuilder.select(getSelectList(selectSpec, entity))
86+
.from(table);
9087

9188
BindMarkers bindMarkers = this.dialect.getBindMarkersFactory().create();
9289
Bindings bindings = Bindings.empty();
@@ -102,37 +99,36 @@ private PreparedOperation<Select> getMappedObject(SelectSpec selectSpec,
10299

103100
if (selectSpec.getSort().isSorted()) {
104101

105-
Sort mappedSort = this.updateMapper.getMappedObject(selectSpec.getSort(), entity);
106-
selectBuilder.orderBy(createOrderByFields(table, mappedSort));
102+
List<OrderByField> sort = this.updateMapper.getMappedSort(table, selectSpec.getSort(), entity);
103+
selectBuilder.orderBy(sort);
107104
}
108105

109-
if (selectSpec.getPage().isPaged()) {
110-
111-
Pageable page = selectSpec.getPage();
106+
if (selectSpec.getLimit() > 0) {
107+
selectBuilder.limit(selectSpec.getLimit());
108+
}
112109

113-
selectBuilder.limitOffset(page.getPageSize(), page.getOffset());
110+
if (selectSpec.getOffset() > 0) {
111+
selectBuilder.offset(selectSpec.getOffset());
114112
}
115113

116114
Select select = selectBuilder.build();
117115
return new DefaultPreparedOperation<>(select, this.renderContext, bindings);
118116
}
119117

120-
private Collection<? extends OrderByField> createOrderByFields(Table table, Sort sortToUse) {
121-
122-
List<OrderByField> fields = new ArrayList<>();
118+
protected List<Expression> getSelectList(SelectSpec selectSpec, @Nullable RelationalPersistentEntity<?> entity) {
123119

124-
for (Sort.Order order : sortToUse) {
120+
if (entity == null) {
121+
return selectSpec.getSelectList();
122+
}
125123

126-
OrderByField orderByField = OrderByField.from(table.column(order.getProperty()));
124+
List<Expression> selectList = selectSpec.getSelectList();
125+
List<Expression> mapped = new ArrayList<>(selectList.size());
127126

128-
if (order.getDirection() != null) {
129-
fields.add(order.isAscending() ? orderByField.asc() : orderByField.desc());
130-
} else {
131-
fields.add(orderByField);
132-
}
127+
for (Expression expression : selectList) {
128+
mapped.add(updateMapper.getMappedObject(expression, entity));
133129
}
134130

135-
return fields;
131+
return mapped;
136132
}
137133

138134
/*

src/main/java/org/springframework/data/r2dbc/core/FluentR2dbcOperations.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2018-2020 the original author or authors.
2+
* Copyright 2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.

src/main/java/org/springframework/data/r2dbc/core/R2dbcEntityTemplate.java

+22-33
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,15 @@
3737
import org.springframework.data.mapping.context.MappingContext;
3838
import org.springframework.data.projection.ProjectionInformation;
3939
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
40-
import org.springframework.data.r2dbc.mapping.R2dbcMappingContext;
4140
import org.springframework.data.r2dbc.query.Criteria;
4241
import org.springframework.data.r2dbc.query.Query;
4342
import org.springframework.data.r2dbc.query.Update;
4443
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
4544
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
45+
import org.springframework.data.relational.core.sql.Expression;
4646
import org.springframework.data.relational.core.sql.Functions;
47+
import org.springframework.data.relational.core.sql.SqlIdentifier;
48+
import org.springframework.data.relational.core.sql.Table;
4749
import org.springframework.data.util.ProxyUtils;
4850
import org.springframework.util.Assert;
4951

@@ -74,13 +76,7 @@ public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAw
7476
* @param databaseClient
7577
*/
7678
public R2dbcEntityTemplate(DatabaseClient databaseClient) {
77-
78-
Assert.notNull(databaseClient, "DatabaseClient must not be null");
79-
80-
this.databaseClient = databaseClient;
81-
this.dataAccessStrategy = getDataAccessStrategy(databaseClient);
82-
this.mappingContext = getMappingContext(this.dataAccessStrategy);
83-
this.projectionFactory = new SpelAwareProxyProjectionFactory();
79+
this(databaseClient, getDataAccessStrategy(databaseClient));
8480
}
8581

8682
/**
@@ -174,7 +170,7 @@ public Mono<Long> count(Query query, Class<?> entityClass) throws DataAccessExce
174170
return doCount(query, entityClass, getTableName(entityClass));
175171
}
176172

177-
Mono<Long> doCount(Query query, Class<?> entityClass, String tableName) {
173+
Mono<Long> doCount(Query query, Class<?> entityClass, SqlIdentifier tableName) {
178174

179175
RelationalPersistentEntity<?> entity = getRequiredEntity(entityClass);
180176
StatementMapper statementMapper = dataAccessStrategy.getStatementMapper().forType(entityClass);
@@ -211,16 +207,18 @@ public Mono<Boolean> exists(Query query, Class<?> entityClass) throws DataAccess
211207
return doExists(query, entityClass, getTableName(entityClass));
212208
}
213209

214-
Mono<Boolean> doExists(Query query, Class<?> entityClass, String tableName) {
210+
Mono<Boolean> doExists(Query query, Class<?> entityClass, SqlIdentifier tableName) {
215211

216212
RelationalPersistentEntity<?> entity = getRequiredEntity(entityClass);
217213
StatementMapper statementMapper = dataAccessStrategy.getStatementMapper().forType(entityClass);
218214

219-
String columnName = entity.hasIdProperty() ? entity.getRequiredIdProperty().getColumnName() : "*";
215+
SqlIdentifier columnName = entity.hasIdProperty() ? entity.getRequiredIdProperty().getColumnName()
216+
: SqlIdentifier.unquoted("*");
220217

221218
StatementMapper.SelectSpec selectSpec = statementMapper //
222219
.createSelect(tableName) //
223-
.withProjection(columnName);
220+
.withProjection(columnName) //
221+
.limit(1);
224222

225223
Optional<Criteria> criteria = query.getCriteria();
226224
if (criteria.isPresent()) {
@@ -248,14 +246,13 @@ public <T> Flux<T> select(Query query, Class<T> entityClass) throws DataAccessEx
248246
return doSelect(query, entityClass, getTableName(entityClass), entityClass).all();
249247
}
250248

251-
<T> RowsFetchSpec<T> doSelect(Query query, Class<?> entityClass, String tableName, Class<T> returnType) {
249+
<T> RowsFetchSpec<T> doSelect(Query query, Class<?> entityClass, SqlIdentifier tableName, Class<T> returnType) {
252250

253-
RelationalPersistentEntity<?> entity = getRequiredEntity(entityClass);
254251
StatementMapper statementMapper = dataAccessStrategy.getStatementMapper().forType(entityClass);
255252

256253
StatementMapper.SelectSpec selectSpec = statementMapper //
257254
.createSelect(tableName) //
258-
.withProjection(getSelectProjection(query, returnType));
255+
.doWithTable((table, spec) -> spec.withProjection(getSelectProjection(table, query, returnType)));
259256

260257
if (query.getLimit() > 0) {
261258
selectSpec = selectSpec.limit(query.getLimit());
@@ -310,7 +307,7 @@ public Mono<Integer> update(Query query, Update update, Class<?> entityClass) th
310307
return doUpdate(query, update, entityClass, getTableName(entityClass));
311308
}
312309

313-
Mono<Integer> doUpdate(Query query, Update update, Class<?> entityClass, String tableName) {
310+
Mono<Integer> doUpdate(Query query, Update update, Class<?> entityClass, SqlIdentifier tableName) {
314311

315312
StatementMapper statementMapper = dataAccessStrategy.getStatementMapper().forType(entityClass);
316313

@@ -339,7 +336,7 @@ public Mono<Integer> delete(Query query, Class<?> entityClass) throws DataAccess
339336
return doDelete(query, entityClass, getTableName(entityClass));
340337
}
341338

342-
Mono<Integer> doDelete(Query query, Class<?> entityClass, String tableName) {
339+
Mono<Integer> doDelete(Query query, Class<?> entityClass, SqlIdentifier tableName) {
343340

344341
StatementMapper statementMapper = dataAccessStrategy.getStatementMapper().forType(entityClass);
345342

@@ -371,7 +368,7 @@ public <T> Mono<T> insert(T entity) throws DataAccessException {
371368
return doInsert(entity, getRequiredEntity(entity).getTableName());
372369
}
373370

374-
<T> Mono<T> doInsert(T entity, String tableName) {
371+
<T> Mono<T> doInsert(T entity, SqlIdentifier tableName) {
375372

376373
RelationalPersistentEntity<T> persistentEntity = getRequiredEntity(entity);
377374

@@ -434,7 +431,7 @@ private <T> Query getByIdQuery(T entity, RelationalPersistentEntity<?> persisten
434431
return Query.query(Criteria.where(persistentEntity.getRequiredIdProperty().getName()).is(id));
435432
}
436433

437-
String getTableName(Class<?> entityClass) {
434+
SqlIdentifier getTableName(Class<?> entityClass) {
438435
return getRequiredEntity(entityClass).getTableName();
439436
}
440437

@@ -447,7 +444,7 @@ private <T> RelationalPersistentEntity<T> getRequiredEntity(T entity) {
447444
return (RelationalPersistentEntity) getRequiredEntity(entityType);
448445
}
449446

450-
private <T> List<String> getSelectProjection(Query query, Class<T> returnType) {
447+
private <T> List<Expression> getSelectProjection(Table table, Query query, Class<T> returnType) {
451448

452449
if (query.getColumns().isEmpty()) {
453450

@@ -456,19 +453,21 @@ private <T> List<String> getSelectProjection(Query query, Class<T> returnType) {
456453
ProjectionInformation projectionInformation = projectionFactory.getProjectionInformation(returnType);
457454

458455
if (projectionInformation.isClosed()) {
459-
return projectionInformation.getInputProperties().stream().map(FeatureDescriptor::getName)
456+
return projectionInformation.getInputProperties().stream().map(FeatureDescriptor::getName).map(table::column)
460457
.collect(Collectors.toList());
461458
}
462459
}
463460

464-
return Collections.singletonList("*");
461+
return Collections.singletonList(table.asterisk());
465462
}
466463

467-
return query.getColumns();
464+
return query.getColumns().stream().map(table::column).collect(Collectors.toList());
468465
}
469466

470467
private static ReactiveDataAccessStrategy getDataAccessStrategy(DatabaseClient databaseClient) {
471468

469+
Assert.notNull(databaseClient, "DatabaseClient must not be null");
470+
472471
if (databaseClient instanceof DefaultDatabaseClient) {
473472

474473
DefaultDatabaseClient client = (DefaultDatabaseClient) databaseClient;
@@ -478,14 +477,4 @@ private static ReactiveDataAccessStrategy getDataAccessStrategy(DatabaseClient d
478477
throw new IllegalStateException("Cannot obtain ReactiveDataAccessStrategy");
479478
}
480479

481-
private static MappingContext<? extends RelationalPersistentEntity<?>, ? extends RelationalPersistentProperty> getMappingContext(
482-
ReactiveDataAccessStrategy strategy) {
483-
484-
if (strategy instanceof DefaultReactiveDataAccessStrategy) {
485-
DefaultReactiveDataAccessStrategy strategy1 = (DefaultReactiveDataAccessStrategy) strategy;
486-
return strategy1.getMappingContext();
487-
}
488-
return new R2dbcMappingContext();
489-
}
490-
491480
}

src/main/java/org/springframework/data/r2dbc/core/ReactiveDeleteOperation.java

+17-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2018-2020 the original author or authors.
2+
* Copyright 2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@
1818
import reactor.core.publisher.Mono;
1919

2020
import org.springframework.data.r2dbc.query.Query;
21+
import org.springframework.data.relational.core.sql.SqlIdentifier;
2122

2223
/**
2324
* The {@link ReactiveDeleteOperation} interface allows creation and execution of {@code DELETE} operations in a fluent
@@ -67,7 +68,21 @@ interface DeleteWithTable {
6768
* @throws IllegalArgumentException if {@link String table} is {@literal null} or empty.
6869
* @see DeleteWithQuery
6970
*/
70-
DeleteWithQuery from(String table);
71+
default DeleteWithQuery from(String table) {
72+
return from(SqlIdentifier.unquoted(table));
73+
}
74+
75+
/**
76+
* Explicitly set the {@link SqlIdentifier name} of the table on which to perform the delete.
77+
* <p>
78+
* Skip this step to use the default table derived from the {@link Class domain type}.
79+
*
80+
* @param table {@link SqlIdentifier name} of the table; must not be {@literal null}.
81+
* @return new instance of {@link DeleteWithQuery}.
82+
* @throws IllegalArgumentException if {@link SqlIdentifier table} is {@literal null}.
83+
* @see DeleteWithQuery
84+
*/
85+
DeleteWithQuery from(SqlIdentifier table);
7186
}
7287

7388
/**

0 commit comments

Comments
 (0)