Skip to content

Commit 2e6af4a

Browse files
committed
#64 - Migrate Insert to StatementMapper.
1 parent 3cedd75 commit 2e6af4a

File tree

11 files changed

+176
-515
lines changed

11 files changed

+176
-515
lines changed

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

+13-1
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.domain.SettableValue;
3233
import org.springframework.data.r2dbc.function.operation.PreparedOperation;
3334
import org.springframework.data.r2dbc.function.query.Criteria;
3435
import org.springframework.data.r2dbc.function.query.Update;
@@ -464,8 +465,19 @@ interface GenericInsertSpec<T> extends InsertSpec<T> {
464465
*
465466
* @param field must not be {@literal null} or empty.
466467
* @param type must not be {@literal null}.
468+
* @deprecated will be removed soon. Use {@link #nullValue(String)}.
467469
*/
468-
GenericInsertSpec<T> nullValue(String field, Class<?> type);
470+
@Deprecated
471+
default GenericInsertSpec<T> nullValue(String field, Class<?> type) {
472+
return value(field, SettableValue.empty(type));
473+
}
474+
475+
/**
476+
* Specify a {@literal null} value to insert.
477+
*
478+
* @param field must not be {@literal null} or empty.
479+
*/
480+
GenericInsertSpec<T> nullValue(String field);
469481
}
470482

471483
/**

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

+28-18
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@
6060
import org.springframework.data.r2dbc.function.query.Criteria;
6161
import org.springframework.data.r2dbc.function.query.Update;
6262
import org.springframework.data.r2dbc.support.R2dbcExceptionTranslator;
63-
import org.springframework.data.relational.core.sql.Insert;
6463
import org.springframework.data.relational.core.sql.Select;
6564
import org.springframework.jdbc.core.SqlProvider;
6665
import org.springframework.lang.Nullable;
@@ -919,18 +918,22 @@ public GenericInsertSpec value(String field, Object value) {
919918
() -> String.format("Value for field %s must not be null. Use nullValue(…) instead.", field));
920919

921920
Map<String, SettableValue> byName = new LinkedHashMap<>(this.byName);
922-
byName.put(field, SettableValue.fromOrEmpty(value, value.getClass()));
921+
if (value instanceof SettableValue) {
922+
byName.put(field, (SettableValue) value);
923+
} else {
924+
byName.put(field, SettableValue.fromOrEmpty(value, value.getClass()));
925+
}
923926

924927
return new DefaultGenericInsertSpec<>(this.table, byName, this.mappingFunction);
925928
}
926929

927930
@Override
928-
public GenericInsertSpec nullValue(String field, Class<?> type) {
931+
public GenericInsertSpec nullValue(String field) {
929932

930933
Assert.notNull(field, "Field must not be null!");
931934

932935
Map<String, SettableValue> byName = new LinkedHashMap<>(this.byName);
933-
byName.put(field, SettableValue.empty(type));
936+
byName.put(field, null);
934937

935938
return new DefaultGenericInsertSpec<>(this.table, byName, this.mappingFunction);
936939
}
@@ -959,12 +962,17 @@ private <R> FetchSpec<R> exchange(BiFunction<Row, RowMetadata, R> mappingFunctio
959962
throw new IllegalStateException("Insert fields is empty!");
960963
}
961964

962-
PreparedOperation<Insert> operation = dataAccessStrategy.getStatements().insert(this.table,
963-
Collections.emptyList(), it -> {
964-
this.byName.forEach(it::bind);
965-
});
965+
StatementMapper mapper = dataAccessStrategy.getStatementMapper();
966+
StatementMapper.InsertSpec insert = mapper.createInsert(this.table);
967+
968+
for (String column : this.byName.keySet()) {
969+
insert = insert.withColumn(column, this.byName.get(column));
970+
}
966971

967-
Function<Connection, Statement> insertFunction = wrapPreparedOperation(operation);
972+
PreparedOperation<?> operation = mapper.getMappedObject(insert);
973+
974+
Function<Connection, Statement> insertFunction = wrapPreparedOperation(operation)
975+
.andThen(statement -> statement.returnGeneratedValues());
968976
Function<Connection, Flux<Result>> resultFunction = it -> Flux.from(insertFunction.apply(it).execute());
969977

970978
return new DefaultSqlResult<>(DefaultDatabaseClient.this, //
@@ -1067,17 +1075,19 @@ private <MR> FetchSpec<MR> exchange(Object toInsert, BiFunction<Row, RowMetadata
10671075

10681076
OutboundRow outboundRow = dataAccessStrategy.getOutboundRow(toInsert);
10691077

1070-
PreparedOperation<Insert> operation = dataAccessStrategy.getStatements().insert(this.table,
1071-
Collections.emptyList(), it -> {
1072-
outboundRow.forEach((k, v) -> {
1078+
StatementMapper mapper = dataAccessStrategy.getStatementMapper();
1079+
StatementMapper.InsertSpec insert = mapper.createInsert(this.table);
10731080

1074-
if (v.hasValue()) {
1075-
it.bind(k, v);
1076-
}
1077-
});
1078-
});
1081+
for (String column : outboundRow.keySet()) {
1082+
SettableValue settableValue = outboundRow.get(column);
1083+
if (settableValue.hasValue()) {
1084+
insert = insert.withColumn(column, settableValue);
1085+
}
1086+
}
10791087

1080-
Function<Connection, Statement> insertFunction = wrapPreparedOperation(operation);
1088+
PreparedOperation<?> operation = mapper.getMappedObject(insert);
1089+
Function<Connection, Statement> insertFunction = wrapPreparedOperation(operation)
1090+
.andThen(statement -> statement.returnGeneratedValues());
10811091
Function<Connection, Flux<Result>> resultFunction = it -> Flux.from(insertFunction.apply(it).execute());
10821092

10831093
return new DefaultSqlResult<>(DefaultDatabaseClient.this, //

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

-228
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,9 @@
3939
import org.springframework.data.r2dbc.function.operation.Bindings;
4040
import org.springframework.data.r2dbc.function.operation.PreparedOperation;
4141
import org.springframework.data.r2dbc.support.StatementRenderUtil;
42-
import org.springframework.data.relational.core.sql.AssignValue;
43-
import org.springframework.data.relational.core.sql.Assignment;
4442
import org.springframework.data.relational.core.sql.Column;
4543
import org.springframework.data.relational.core.sql.Condition;
4644
import org.springframework.data.relational.core.sql.Delete;
47-
import org.springframework.data.relational.core.sql.DeleteBuilder;
4845
import org.springframework.data.relational.core.sql.Expression;
4946
import org.springframework.data.relational.core.sql.Insert;
5047
import org.springframework.data.relational.core.sql.OrderByField;
@@ -54,7 +51,6 @@
5451
import org.springframework.data.relational.core.sql.StatementBuilder;
5552
import org.springframework.data.relational.core.sql.Table;
5653
import org.springframework.data.relational.core.sql.Update;
57-
import org.springframework.data.relational.core.sql.UpdateBuilder;
5854
import org.springframework.data.relational.core.sql.render.RenderContext;
5955
import org.springframework.data.relational.core.sql.render.SqlRenderer;
6056
import org.springframework.lang.Nullable;
@@ -169,197 +165,6 @@ private Collection<? extends OrderByField> createOrderByFields(Table table, Sort
169165
return fields;
170166
}
171167

172-
/*
173-
* (non-Javadoc)
174-
* @see org.springframework.data.r2dbc.function.StatementFactory#insert(java.lang.String, java.util.Collection, java.util.function.Consumer)
175-
*/
176-
@Override
177-
public PreparedOperation<Insert> insert(String tableName, Collection<String> generatedKeysNames,
178-
Consumer<StatementBinderBuilder> binderConsumer) {
179-
180-
Assert.hasText(tableName, "Table must not be empty");
181-
Assert.notNull(generatedKeysNames, "Generated key names must not be null");
182-
Assert.notNull(binderConsumer, "Binder Consumer must not be null");
183-
184-
DefaultBinderBuilder binderBuilder = new DefaultBinderBuilder() {
185-
@Override
186-
public void filterBy(String identifier, SettableValue settable) {
187-
throw new InvalidDataAccessApiUsageException("Filter-Binding for INSERT not supported. Use bind(…)");
188-
}
189-
};
190-
191-
binderConsumer.accept(binderBuilder);
192-
193-
return withDialect((dialect, renderContext) -> {
194-
195-
BindMarkers bindMarkers = dialect.getBindMarkersFactory().create();
196-
Table table = Table.create(tableName);
197-
198-
Map<BindMarker, SettableValue> expressionBindings = new LinkedHashMap<>();
199-
List<Expression> expressions = new ArrayList<>();
200-
binderBuilder.forEachBinding((column, settableValue) -> {
201-
202-
BindMarker bindMarker = bindMarkers.next(column);
203-
204-
expressions.add(SQL.bindMarker(bindMarker.getPlaceholder()));
205-
expressionBindings.put(bindMarker, settableValue);
206-
});
207-
208-
if (expressions.isEmpty()) {
209-
throw new IllegalStateException("INSERT contains no value expressions");
210-
}
211-
212-
Binding binding = binderBuilder.build(table, bindMarkers).withBindings(expressionBindings);
213-
Insert insert = StatementBuilder.insert().into(table).columns(table.columns(binderBuilder.bindings.keySet()))
214-
.values(expressions).build();
215-
216-
return new DefaultPreparedOperation<Insert>(insert, renderContext, binding.toBindings()) {
217-
@Override
218-
public Statement bind(Statement to) {
219-
return super.bind(to).returnGeneratedValues(generatedKeysNames.toArray(new String[0]));
220-
}
221-
};
222-
});
223-
}
224-
225-
/*
226-
* (non-Javadoc)
227-
* @see org.springframework.data.r2dbc.function.StatementFactory#update(java.lang.String, java.util.function.Consumer)
228-
*/
229-
@Override
230-
public PreparedOperation<Update> update(String tableName, Consumer<StatementBinderBuilder> binderConsumer) {
231-
232-
Assert.hasText(tableName, "Table must not be empty");
233-
Assert.notNull(binderConsumer, "Binder Consumer must not be null");
234-
235-
DefaultBinderBuilder binderBuilder = new DefaultBinderBuilder();
236-
237-
binderConsumer.accept(binderBuilder);
238-
239-
return withDialect((dialect, renderContext) -> {
240-
241-
BindMarkers bindMarkers = dialect.getBindMarkersFactory().create();
242-
Table table = Table.create(tableName);
243-
244-
Map<BindMarker, SettableValue> assignmentBindings = new LinkedHashMap<>();
245-
List<Assignment> assignments = new ArrayList<>();
246-
binderBuilder.forEachBinding((column, settableValue) -> {
247-
248-
BindMarker bindMarker = bindMarkers.next(column);
249-
AssignValue assignment = table.column(column).set(SQL.bindMarker(bindMarker.getPlaceholder()));
250-
251-
assignments.add(assignment);
252-
assignmentBindings.put(bindMarker, settableValue);
253-
});
254-
255-
if (assignments.isEmpty()) {
256-
throw new IllegalStateException("UPDATE contains no assignments");
257-
}
258-
259-
UpdateBuilder.UpdateWhere updateBuilder = StatementBuilder.update(table).set(assignments);
260-
261-
Binding binding = binderBuilder.build(table, bindMarkers).withBindings(assignmentBindings);
262-
Update update;
263-
264-
if (binding.hasCondition()) {
265-
update = updateBuilder.where(binding.getCondition()).build();
266-
} else {
267-
update = updateBuilder.build();
268-
}
269-
270-
return new DefaultPreparedOperation<>(update, renderContext, binding.toBindings());
271-
});
272-
}
273-
274-
@Override
275-
public PreparedOperation<Update> update(String tableName, BiConsumer<Table, UpdateConfigurer> configurerConsumer) {
276-
277-
Assert.hasText(tableName, "Table must not be empty");
278-
Assert.notNull(configurerConsumer, "Configurer Consumer must not be null");
279-
280-
return withDialect((dialect, renderContext) -> {
281-
282-
DefaultUpdateConfigurer configurer = new DefaultUpdateConfigurer(dialect.getBindMarkersFactory().create());
283-
Table table = Table.create(tableName);
284-
configurerConsumer.accept(table, configurer);
285-
286-
UpdateBuilder.UpdateWhere updateBuilder = StatementBuilder.update(table).set(configurer.assignments);
287-
288-
if (configurer.condition != null) {
289-
updateBuilder.where(configurer.condition);
290-
}
291-
292-
Update update = updateBuilder.build();
293-
return new DefaultPreparedOperation<>(update, renderContext, configurer.bindings);
294-
});
295-
}
296-
297-
/*
298-
* (non-Javadoc)
299-
* @see org.springframework.data.r2dbc.function.StatementFactory#delete(java.lang.String, java.util.function.Consumer)
300-
*/
301-
@Override
302-
public PreparedOperation<Delete> delete(String tableName, Consumer<StatementBinderBuilder> binderConsumer) {
303-
304-
Assert.hasText(tableName, "Table must not be empty");
305-
Assert.notNull(binderConsumer, "Binder Consumer must not be null");
306-
307-
DefaultBinderBuilder binderBuilder = new DefaultBinderBuilder() {
308-
@Override
309-
public void bind(String identifier, SettableValue settable) {
310-
throw new InvalidDataAccessApiUsageException("Binding for DELETE not supported. Use filterBy(…)");
311-
}
312-
};
313-
314-
binderConsumer.accept(binderBuilder);
315-
316-
return withDialect((dialect, renderContext) -> {
317-
318-
Table table = Table.create(tableName);
319-
DeleteBuilder.DeleteWhere deleteBuilder = StatementBuilder.delete().from(table);
320-
321-
BindMarkers bindMarkers = dialect.getBindMarkersFactory().create();
322-
Binding binding = binderBuilder.build(table, bindMarkers);
323-
Delete delete;
324-
325-
if (binding.hasCondition()) {
326-
delete = deleteBuilder.where(binding.getCondition()).build();
327-
} else {
328-
delete = deleteBuilder.build();
329-
}
330-
331-
return new DefaultPreparedOperation<>(delete, renderContext, binding.toBindings());
332-
});
333-
}
334-
335-
@Override
336-
public PreparedOperation<Delete> delete(String tableName, BiConsumer<Table, BindConfigurer> configurerConsumer) {
337-
338-
Assert.hasText(tableName, "Table must not be empty");
339-
Assert.notNull(configurerConsumer, "Configurer Consumer must not be null");
340-
341-
return withDialect((dialect, renderContext) -> {
342-
343-
Table table = Table.create(tableName);
344-
DeleteBuilder.DeleteWhere deleteBuilder = StatementBuilder.delete().from(table);
345-
346-
BindMarkers bindMarkers = dialect.getBindMarkersFactory().create();
347-
DefaultBindConfigurer configurer = new DefaultBindConfigurer(bindMarkers);
348-
349-
configurerConsumer.accept(table, configurer);
350-
351-
Delete delete;
352-
353-
if (configurer.condition != null) {
354-
delete = deleteBuilder.where(configurer.condition).build();
355-
} else {
356-
delete = deleteBuilder.build();
357-
}
358-
359-
return new DefaultPreparedOperation<>(delete, renderContext, configurer.bindings);
360-
});
361-
}
362-
363168
private <T> T withDialect(BiFunction<Dialect, RenderContext, T> action) {
364169

365170
Assert.notNull(action, "Action must not be null");
@@ -668,39 +473,6 @@ public SelectConfigurer withSort(Sort sort) {
668473
}
669474
}
670475

671-
/**
672-
* Default {@link UpdateConfigurer} implementation.
673-
*/
674-
static class DefaultUpdateConfigurer extends DefaultBindConfigurer implements UpdateConfigurer {
675-
676-
Collection<? extends Assignment> assignments;
677-
678-
DefaultUpdateConfigurer(BindMarkers bindMarkers) {
679-
super(bindMarkers);
680-
}
681-
682-
@Override
683-
public UpdateConfigurer withBindings(Bindings bindings) {
684-
super.withBindings(bindings);
685-
return this;
686-
}
687-
688-
@Override
689-
public UpdateConfigurer withAssignments(Collection<? extends Assignment> assignments) {
690-
691-
Assert.notNull(assignments, "Assignments must not be null");
692-
693-
this.assignments = assignments;
694-
return this;
695-
}
696-
697-
@Override
698-
public UpdateConfigurer withWhere(Condition condition) {
699-
super.withWhere(condition);
700-
return this;
701-
}
702-
}
703-
704476
/**
705477
* Default {@link SelectConfigurer} implementation.
706478
*/

0 commit comments

Comments
 (0)