Skip to content

Commit 941ca39

Browse files
committed
#64 - Migrate Insert to StatementMapper.
1 parent 9f343b0 commit 941ca39

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.lang.Nullable;
6665
import org.springframework.util.Assert;
@@ -918,18 +917,22 @@ public GenericInsertSpec value(String field, Object value) {
918917
() -> String.format("Value for field %s must not be null. Use nullValue(…) instead.", field));
919918

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

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

926929
@Override
927-
public GenericInsertSpec nullValue(String field, Class<?> type) {
930+
public GenericInsertSpec nullValue(String field) {
928931

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

931934
Map<String, SettableValue> byName = new LinkedHashMap<>(this.byName);
932-
byName.put(field, SettableValue.empty(type));
935+
byName.put(field, null);
933936

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

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

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

969977
return new DefaultSqlResult<>(DefaultDatabaseClient.this, //
@@ -1066,17 +1074,19 @@ private <MR> FetchSpec<MR> exchange(Object toInsert, BiFunction<Row, RowMetadata
10661074

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

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

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

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

10821092
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)