committed by
Mark Paluch
parent
ef2d885b1e
commit
02e0cb5026
@@ -20,11 +20,11 @@ import io.r2dbc.spi.Statement;
|
||||
/**
|
||||
* A single indexed bind marker.
|
||||
*/
|
||||
class IndexedBindMarker implements BindMarker {
|
||||
public class IndexedBindMarker implements BindMarker {
|
||||
|
||||
private final String placeholder;
|
||||
|
||||
private int index;
|
||||
private final int index;
|
||||
|
||||
IndexedBindMarker(String placeholder, int index) {
|
||||
this.placeholder = placeholder;
|
||||
@@ -57,4 +57,10 @@ class IndexedBindMarker implements BindMarker {
|
||||
public void bindNull(Statement statement, Class<?> valueType) {
|
||||
statement.bindNull(this.index, valueType);
|
||||
}
|
||||
|
||||
|
||||
public int getIndex() {
|
||||
return index;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright 2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.r2dbc.function;
|
||||
|
||||
import io.r2dbc.spi.Statement;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.r2dbc.domain.SettableValue;
|
||||
|
||||
/**
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
public class Bindings {
|
||||
|
||||
private final List<SingleBinding> bindings;
|
||||
|
||||
public Bindings(List<SingleBinding> bindings) {
|
||||
this.bindings = bindings;
|
||||
}
|
||||
|
||||
public void apply(Statement statement) {
|
||||
bindings.forEach(sb -> sb.bindTo(statement));
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public static abstract class SingleBinding<T> {
|
||||
|
||||
final T identifier;
|
||||
final SettableValue value;
|
||||
|
||||
public abstract void bindTo(Statement statement);
|
||||
|
||||
public abstract boolean isIndexed();
|
||||
|
||||
public final boolean isNamed() {
|
||||
return !isIndexed();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class IndexedSingleBinding extends SingleBinding<Integer> {
|
||||
|
||||
public IndexedSingleBinding(Integer identifier, SettableValue value) {
|
||||
super(identifier, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindTo(Statement statement) {
|
||||
|
||||
if (value.isEmpty()) {
|
||||
statement.bindNull((int) identifier, value.getType());
|
||||
} else {
|
||||
statement.bind((int) identifier, value.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIndexed() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static class NamedExpandedSingleBinding extends SingleBinding<String> {
|
||||
|
||||
private final BindableOperation operation;
|
||||
|
||||
public NamedExpandedSingleBinding(String identifier, SettableValue value, BindableOperation operation) {
|
||||
|
||||
super(identifier, value);
|
||||
|
||||
this.operation = operation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindTo(Statement statement) {
|
||||
|
||||
if (value != null) {
|
||||
operation.bind(statement, identifier, value);
|
||||
} else {
|
||||
operation.bindNull(statement, identifier, value.getType());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIndexed() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -331,40 +331,15 @@ class DefaultDatabaseClient implements DatabaseClient, ConnectionAccessor {
|
||||
|
||||
<T> FetchSpec<T> exchange(String sql, BiFunction<Row, RowMetadata, T> mappingFunction) {
|
||||
|
||||
Function<Connection, Statement> executeFunction = it -> {
|
||||
PreparedOperation pop;
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Executing SQL statement [" + sql + "]");
|
||||
}
|
||||
if (sqlSupplier instanceof PreparedOperation<?>) {
|
||||
pop = ((PreparedOperation<?>) sqlSupplier);
|
||||
} else {
|
||||
pop = new ExpandedPreparedOperation(sql, namedParameters, dataAccessStrategy, byName, byIndex);
|
||||
}
|
||||
|
||||
if (sqlSupplier instanceof PreparedOperation<?>) {
|
||||
return ((PreparedOperation<?>) sqlSupplier).bind(it.createStatement(sql));
|
||||
}
|
||||
|
||||
BindableOperation operation = namedParameters.expand(sql, dataAccessStrategy.getBindMarkersFactory(),
|
||||
new MapBindParameterSource(byName));
|
||||
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Expanded SQL [" + operation.toQuery() + "]");
|
||||
}
|
||||
|
||||
Statement statement = it.createStatement(operation.toQuery());
|
||||
|
||||
byName.forEach((name, o) -> {
|
||||
|
||||
if (o.getValue() != null) {
|
||||
operation.bind(statement, name, o.getValue());
|
||||
} else {
|
||||
operation.bindNull(statement, name, o.getType());
|
||||
}
|
||||
});
|
||||
|
||||
bindByIndex(statement, byIndex);
|
||||
|
||||
return statement;
|
||||
};
|
||||
|
||||
Function<Connection, Flux<Result>> resultFunction = it -> Flux.from(executeFunction.apply(it).execute());
|
||||
Function<Connection, Flux<Result>> resultFunction = it -> Flux.from(pop.createBoundStatement(it).execute());
|
||||
|
||||
return new DefaultSqlResult<>(DefaultDatabaseClient.this, //
|
||||
sql, //
|
||||
@@ -907,20 +882,10 @@ class DefaultDatabaseClient implements DatabaseClient, ConnectionAccessor {
|
||||
byName.forEach(it::bind);
|
||||
});
|
||||
|
||||
String sql = operation.toQuery();
|
||||
Function<Connection, Statement> insertFunction = it -> {
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Executing SQL statement [" + sql + "]");
|
||||
}
|
||||
|
||||
return operation.bind(it.createStatement(sql));
|
||||
};
|
||||
|
||||
Function<Connection, Flux<Result>> resultFunction = it -> Flux.from(insertFunction.apply(it).execute());
|
||||
Function<Connection, Flux<Result>> resultFunction = it -> Flux.from(operation.createBoundStatement(it).execute());
|
||||
|
||||
return new DefaultSqlResult<>(DefaultDatabaseClient.this, //
|
||||
sql, //
|
||||
operation.toQuery(), //
|
||||
resultFunction, //
|
||||
it -> resultFunction.apply(it).flatMap(Result::getRowsUpdated).next(), //
|
||||
mappingFunction);
|
||||
@@ -1028,21 +993,10 @@ class DefaultDatabaseClient implements DatabaseClient, ConnectionAccessor {
|
||||
});
|
||||
});
|
||||
|
||||
String sql = operation.toQuery();
|
||||
|
||||
Function<Connection, Statement> insertFunction = it -> {
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Executing SQL statement [" + sql + "]");
|
||||
}
|
||||
|
||||
return operation.bind(it.createStatement(sql));
|
||||
};
|
||||
|
||||
Function<Connection, Flux<Result>> resultFunction = it -> Flux.from(insertFunction.apply(it).execute());
|
||||
Function<Connection, Flux<Result>> resultFunction = it -> Flux.from(operation.createBoundStatement(it).execute());
|
||||
|
||||
return new DefaultSqlResult<>(DefaultDatabaseClient.this, //
|
||||
sql, //
|
||||
operation.toQuery(), //
|
||||
resultFunction, //
|
||||
it -> resultFunction //
|
||||
.apply(it) //
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.data.r2dbc.function;
|
||||
|
||||
import io.r2dbc.spi.Connection;
|
||||
import io.r2dbc.spi.Statement;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@@ -28,27 +29,16 @@ import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.r2dbc.dialect.BindMarker;
|
||||
import org.springframework.data.r2dbc.dialect.BindMarkers;
|
||||
import org.springframework.data.r2dbc.dialect.Dialect;
|
||||
import org.springframework.data.r2dbc.dialect.IndexedBindMarker;
|
||||
import org.springframework.data.r2dbc.domain.SettableValue;
|
||||
import org.springframework.data.relational.core.sql.AssignValue;
|
||||
import org.springframework.data.relational.core.sql.Assignment;
|
||||
import org.springframework.data.relational.core.sql.Column;
|
||||
import org.springframework.data.relational.core.sql.Condition;
|
||||
import org.springframework.data.relational.core.sql.Delete;
|
||||
import org.springframework.data.relational.core.sql.DeleteBuilder;
|
||||
import org.springframework.data.relational.core.sql.Expression;
|
||||
import org.springframework.data.relational.core.sql.Insert;
|
||||
import org.springframework.data.relational.core.sql.SQL;
|
||||
import org.springframework.data.relational.core.sql.Select;
|
||||
import org.springframework.data.relational.core.sql.SelectBuilder;
|
||||
import org.springframework.data.relational.core.sql.StatementBuilder;
|
||||
import org.springframework.data.relational.core.sql.Table;
|
||||
import org.springframework.data.relational.core.sql.Update;
|
||||
import org.springframework.data.relational.core.sql.UpdateBuilder;
|
||||
import org.springframework.data.relational.core.sql.*;
|
||||
import org.springframework.data.relational.core.sql.render.RenderContext;
|
||||
import org.springframework.data.relational.core.sql.render.SqlRenderer;
|
||||
import org.springframework.lang.Nullable;
|
||||
@@ -101,10 +91,45 @@ class DefaultStatementFactory implements StatementFactory {
|
||||
select = selectBuilder.build();
|
||||
}
|
||||
|
||||
return new DefaultPreparedOperation<>(select, renderContext, binding);
|
||||
return new DefaultPreparedOperation<>( //
|
||||
select, //
|
||||
renderContext, //
|
||||
createBindings(binding) //
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static Bindings createBindings(Binding binding) {
|
||||
|
||||
List<Bindings.SingleBinding> singleBindings = new ArrayList<>();
|
||||
|
||||
binding.getNulls().forEach( //
|
||||
(bindMarker, settableValue) -> {
|
||||
|
||||
if (bindMarker instanceof IndexedBindMarker) {
|
||||
singleBindings //
|
||||
.add(new Bindings.IndexedSingleBinding( //
|
||||
((IndexedBindMarker) bindMarker).getIndex(), //
|
||||
settableValue) //
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
binding.getValues().forEach( //
|
||||
(bindMarker, value) -> {
|
||||
if (bindMarker instanceof IndexedBindMarker) {
|
||||
singleBindings //
|
||||
.add(new Bindings.IndexedSingleBinding( //
|
||||
((IndexedBindMarker) bindMarker).getIndex(), //
|
||||
value instanceof SettableValue ? (SettableValue) value : SettableValue.from(value)) //
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return new Bindings(singleBindings);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.r2dbc.function.StatementFactory#insert(java.lang.String, java.util.Collection, java.util.function.Consumer)
|
||||
@@ -149,7 +174,7 @@ class DefaultStatementFactory implements StatementFactory {
|
||||
Insert insert = StatementBuilder.insert().into(table).columns(table.columns(binderBuilder.bindings.keySet()))
|
||||
.values(expressions).build();
|
||||
|
||||
return new DefaultPreparedOperation<Insert>(insert, renderContext, binding) {
|
||||
return new DefaultPreparedOperation<Insert>(insert, renderContext, createBindings(binding)) {
|
||||
@Override
|
||||
public Statement bind(Statement to) {
|
||||
return super.bind(to).returnGeneratedValues(generatedKeysNames.toArray(new String[0]));
|
||||
@@ -203,7 +228,7 @@ class DefaultStatementFactory implements StatementFactory {
|
||||
update = updateBuilder.build();
|
||||
}
|
||||
|
||||
return new DefaultPreparedOperation<>(update, renderContext, binding);
|
||||
return new DefaultPreparedOperation<>(update, renderContext, createBindings(binding));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -241,7 +266,7 @@ class DefaultStatementFactory implements StatementFactory {
|
||||
delete = deleteBuilder.build();
|
||||
}
|
||||
|
||||
return new DefaultPreparedOperation<>(delete, renderContext, binding);
|
||||
return new DefaultPreparedOperation<>(delete, renderContext, createBindings(binding));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -411,17 +436,90 @@ class DefaultStatementFactory implements StatementFactory {
|
||||
}
|
||||
}
|
||||
|
||||
static abstract class PreparedOperationSupport<T> implements PreparedOperation<T> {
|
||||
|
||||
private Function<String, String> sqlFilter = s -> s;
|
||||
private Function<Bindings, Bindings> bindingFilter = b -> b;
|
||||
private final Bindings bindings;
|
||||
|
||||
protected PreparedOperationSupport(Bindings bindings) {
|
||||
|
||||
this.bindings = bindings;
|
||||
}
|
||||
|
||||
abstract protected String createBaseSql();
|
||||
|
||||
Bindings getBaseBinding() {
|
||||
return bindings;
|
||||
};
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.r2dbc.function.QueryOperation#toQuery()
|
||||
*/
|
||||
@Override
|
||||
public String toQuery() {
|
||||
|
||||
return sqlFilter.apply(createBaseSql());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.r2dbc.function.PreparedOperation#bind(io.r2dbc.spi.Statement)
|
||||
*/
|
||||
protected Statement bind(Statement to) {
|
||||
|
||||
bindingFilter.apply(getBaseBinding()).apply(to);
|
||||
return to;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Statement createBoundStatement(Connection connection) {
|
||||
|
||||
// TODO add back logging
|
||||
// if (logger.isDebugEnabled()) {
|
||||
// logger.debug("Executing SQL statement [" + sql + "]");
|
||||
// }
|
||||
|
||||
return bind(connection.createStatement(toQuery()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSqlFilter(Function<String, String> filter) {
|
||||
|
||||
Assert.notNull(filter, "Filter must not be null.");
|
||||
|
||||
sqlFilter = filter;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindingFilter(Function<Bindings, Bindings> filter) {
|
||||
|
||||
Assert.notNull(filter, "Filter must not be null.");
|
||||
|
||||
bindingFilter = filter;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Default implementation of {@link PreparedOperation}.
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
static class DefaultPreparedOperation<T> implements PreparedOperation<T> {
|
||||
static class DefaultPreparedOperation<T> extends PreparedOperationSupport<T> {
|
||||
|
||||
private final T source;
|
||||
private final RenderContext renderContext;
|
||||
private final Binding binding;
|
||||
|
||||
DefaultPreparedOperation(T source, RenderContext renderContext, Bindings bindings) {
|
||||
|
||||
super(bindings);
|
||||
|
||||
this.source = source;
|
||||
this.renderContext = renderContext;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
@@ -432,12 +530,8 @@ class DefaultStatementFactory implements StatementFactory {
|
||||
return this.source;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.r2dbc.function.QueryOperation#toQuery()
|
||||
*/
|
||||
@Override
|
||||
public String toQuery() {
|
||||
protected String createBaseSql() {
|
||||
|
||||
SqlRenderer sqlRenderer = SqlRenderer.create(renderContext);
|
||||
|
||||
@@ -460,15 +554,5 @@ class DefaultStatementFactory implements StatementFactory {
|
||||
throw new IllegalStateException("Cannot render " + this.getSource());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.r2dbc.function.PreparedOperation#bind(io.r2dbc.spi.Statement)
|
||||
*/
|
||||
@Override
|
||||
public Statement bind(Statement to) {
|
||||
|
||||
binding.apply(to);
|
||||
return to;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright 2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.r2dbc.function;
|
||||
|
||||
import io.r2dbc.spi.Connection;
|
||||
import io.r2dbc.spi.Statement;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.data.r2dbc.domain.SettableValue;
|
||||
|
||||
/**
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
public class ExpandedPreparedOperation
|
||||
extends DefaultStatementFactory.PreparedOperationSupport<BindableOperation> {
|
||||
|
||||
private final BindableOperation operation;
|
||||
private final Map<String, SettableValue> byName;
|
||||
private final Map<Integer, SettableValue> byIndex;
|
||||
|
||||
private ExpandedPreparedOperation(BindableOperation operation, Map<String, SettableValue> byName,
|
||||
Map<Integer, SettableValue> byIndex) {
|
||||
|
||||
super(createBindings(operation, byName, byIndex));
|
||||
|
||||
this.operation = operation;
|
||||
this.byName = byName;
|
||||
this.byIndex = byIndex;
|
||||
}
|
||||
|
||||
private static Bindings createBindings(BindableOperation operation, Map<String, SettableValue> byName,
|
||||
Map<Integer, SettableValue> byIndex) {
|
||||
|
||||
List<Bindings.SingleBinding> bindings = new ArrayList<>();
|
||||
|
||||
byName.forEach(
|
||||
(identifier, settableValue) -> bindings.add(new Bindings.NamedExpandedSingleBinding(identifier, settableValue, operation)));
|
||||
|
||||
byIndex.forEach((identifier, settableValue) -> bindings.add(new Bindings.IndexedSingleBinding(identifier, settableValue)));
|
||||
|
||||
return new Bindings(bindings);
|
||||
}
|
||||
|
||||
ExpandedPreparedOperation(String sql, NamedParameterExpander namedParameters,
|
||||
ReactiveDataAccessStrategy dataAccessStrategy, Map<String, SettableValue> byName,
|
||||
Map<Integer, SettableValue> byIndex) {
|
||||
|
||||
this( //
|
||||
namedParameters.expand(sql, dataAccessStrategy.getBindMarkersFactory(), new MapBindParameterSource(byName)), //
|
||||
byName, //
|
||||
byIndex //
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String createBaseSql() {
|
||||
return toQuery();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BindableOperation getSource() {
|
||||
return operation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Statement createBoundStatement(Connection connection) {
|
||||
|
||||
Statement statement = connection.createStatement(operation.toQuery());
|
||||
|
||||
bindByName(statement, byName);
|
||||
bindByIndex(statement, byIndex);
|
||||
|
||||
return statement;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toQuery() {
|
||||
return operation.toQuery();
|
||||
}
|
||||
|
||||
// todo that is a weird assymmetry between bindByName and bindByIndex
|
||||
private void bindByName(Statement statement, Map<String, SettableValue> byName) {
|
||||
|
||||
byName.forEach((name, o) -> {
|
||||
|
||||
if (o.getValue() != null) {
|
||||
operation.bind(statement, name, o.getValue());
|
||||
} else {
|
||||
operation.bindNull(statement, name, o.getType());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void bindByIndex(Statement statement, Map<Integer, SettableValue> byIndex) {
|
||||
|
||||
byIndex.forEach((i, o) -> {
|
||||
|
||||
if (o.getValue() != null) {
|
||||
statement.bind(i.intValue(), o.getValue());
|
||||
} else {
|
||||
statement.bindNull(i.intValue(), o.getType());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright 2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.r2dbc.function;
|
||||
|
||||
import io.r2dbc.spi.Connection;
|
||||
import io.r2dbc.spi.Statement;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.data.r2dbc.domain.SettableValue;
|
||||
|
||||
/**
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
public class OldParameterbindingPreparedOperation implements PreparedOperation<BindableOperation> {
|
||||
|
||||
private final BindableOperation operation;
|
||||
private final Map<String, SettableValue> byName;
|
||||
private final Map<Integer, SettableValue> byIndex;
|
||||
|
||||
private OldParameterbindingPreparedOperation(BindableOperation operation, Map<String, SettableValue> byName,
|
||||
Map<Integer, SettableValue> byIndex) {
|
||||
|
||||
this.operation = operation;
|
||||
this.byName = byName;
|
||||
this.byIndex = byIndex;
|
||||
}
|
||||
|
||||
OldParameterbindingPreparedOperation(String sql, NamedParameterExpander namedParameters,
|
||||
ReactiveDataAccessStrategy dataAccessStrategy, Map<String, SettableValue> byName,
|
||||
Map<Integer, SettableValue> byIndex) {
|
||||
|
||||
this( //
|
||||
namedParameters.expand(sql, dataAccessStrategy.getBindMarkersFactory(), new MapBindParameterSource(byName)), //
|
||||
byName, //
|
||||
byIndex //
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BindableOperation getSource() {
|
||||
return operation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Statement createBoundStatement(Connection connection) {
|
||||
|
||||
Statement statement = connection.createStatement(operation.toQuery());
|
||||
|
||||
bindByName(statement, byName);
|
||||
bindByIndex(statement, byIndex);
|
||||
|
||||
return statement;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSqlFilter(Function<String, String> filter) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindingFilter(Function<Bindings, Bindings> filter) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toQuery() {
|
||||
return operation.toQuery();
|
||||
}
|
||||
|
||||
// todo that is a weird assymmetry between bindByName and bindByIndex
|
||||
private void bindByName(Statement statement, Map<String, SettableValue> byName) {
|
||||
|
||||
byName.forEach((name, o) -> {
|
||||
|
||||
if (o.getValue() != null) {
|
||||
operation.bind(statement,name, o.getValue());
|
||||
} else {
|
||||
operation.bindNull(statement, name, o.getType());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void bindByIndex(Statement statement, Map<Integer, SettableValue> byIndex) {
|
||||
|
||||
byIndex.forEach((i, o) -> {
|
||||
|
||||
if (o.getValue() != null) {
|
||||
statement.bind(i.intValue(), o.getValue());
|
||||
} else {
|
||||
statement.bindNull(i.intValue(), o.getType());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -15,8 +15,10 @@
|
||||
*/
|
||||
package org.springframework.data.r2dbc.function;
|
||||
|
||||
import io.r2dbc.spi.Connection;
|
||||
import io.r2dbc.spi.Statement;
|
||||
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
@@ -39,10 +41,15 @@ public interface PreparedOperation<T> extends QueryOperation {
|
||||
T getSource();
|
||||
|
||||
/**
|
||||
* Bind query parameters to a {@link Statement}.
|
||||
* create a {@link Statement} from the generated SQL after applying the SQL filter and then applying the
|
||||
* {@link org.springframework.data.r2dbc.function.DefaultStatementFactory.Binding} after filtering those as well.
|
||||
*
|
||||
* @param to the target statement to bind parameters to.
|
||||
* @param connection the {@link Connection} used for constructing a statement
|
||||
* @return the bound statement.
|
||||
*/
|
||||
Statement bind(Statement to);
|
||||
Statement createBoundStatement(Connection connection);
|
||||
|
||||
void addSqlFilter(Function<String, String> filter);
|
||||
|
||||
void addBindingFilter(Function<Bindings, Bindings> filter);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.data.r2dbc.function;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import io.r2dbc.spi.Connection;
|
||||
import io.r2dbc.spi.Statement;
|
||||
|
||||
import java.util.Arrays;
|
||||
@@ -46,6 +47,11 @@ public class StatementFactoryUnitTests {
|
||||
.createRenderContext());
|
||||
|
||||
Statement statementMock = mock(Statement.class);
|
||||
Connection connectionMock = mock(Connection.class);
|
||||
|
||||
{
|
||||
when(connectionMock.createStatement(anyString())).thenReturn(statementMock);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldToQuerySimpleSelectWithoutBindings() {
|
||||
@@ -55,7 +61,8 @@ public class StatementFactoryUnitTests {
|
||||
assertThat(select.getSource()).isInstanceOf(Select.class);
|
||||
assertThat(select.toQuery()).isEqualTo("SELECT foo.bar, foo.baz FROM foo");
|
||||
|
||||
select.bind(statementMock);
|
||||
select.createBoundStatement(connectionMock);
|
||||
|
||||
verifyZeroInteractions(statementMock);
|
||||
}
|
||||
|
||||
@@ -69,7 +76,8 @@ public class StatementFactoryUnitTests {
|
||||
assertThat(select.getSource()).isInstanceOf(Select.class);
|
||||
assertThat(select.toQuery()).isEqualTo("SELECT foo.bar, foo.baz FROM foo WHERE foo.doe = $1");
|
||||
|
||||
select.bind(statementMock);
|
||||
select.createBoundStatement(connectionMock);
|
||||
|
||||
verify(statementMock).bind(0, "John");
|
||||
verifyNoMoreInteractions(statementMock);
|
||||
}
|
||||
@@ -85,7 +93,8 @@ public class StatementFactoryUnitTests {
|
||||
assertThat(select.getSource()).isInstanceOf(Select.class);
|
||||
assertThat(select.toQuery()).isEqualTo("SELECT foo.bar, foo.baz FROM foo WHERE foo.doe = $1 AND foo.baz = $2");
|
||||
|
||||
select.bind(statementMock);
|
||||
select.createBoundStatement(connectionMock);
|
||||
|
||||
verify(statementMock).bind(0, "John");
|
||||
verify(statementMock).bind(1, "Jake");
|
||||
verifyNoMoreInteractions(statementMock);
|
||||
@@ -101,7 +110,7 @@ public class StatementFactoryUnitTests {
|
||||
assertThat(select.getSource()).isInstanceOf(Select.class);
|
||||
assertThat(select.toQuery()).isEqualTo("SELECT foo.bar, foo.baz FROM foo WHERE foo.doe IS NULL");
|
||||
|
||||
select.bind(statementMock);
|
||||
select.createBoundStatement(connectionMock);
|
||||
verifyZeroInteractions(statementMock);
|
||||
}
|
||||
|
||||
@@ -115,7 +124,7 @@ public class StatementFactoryUnitTests {
|
||||
assertThat(select.getSource()).isInstanceOf(Select.class);
|
||||
assertThat(select.toQuery()).isEqualTo("SELECT foo.bar, foo.baz FROM foo WHERE foo.doe IN ($1, $2)");
|
||||
|
||||
select.bind(statementMock);
|
||||
select.createBoundStatement(connectionMock);
|
||||
verify(statementMock).bind(0, "John");
|
||||
verify(statementMock).bind(1, "Jake");
|
||||
verifyNoMoreInteractions(statementMock);
|
||||
@@ -138,7 +147,7 @@ public class StatementFactoryUnitTests {
|
||||
assertThat(insert.getSource()).isInstanceOf(Insert.class);
|
||||
assertThat(insert.toQuery()).isEqualTo("INSERT INTO foo (bar) VALUES ($1)");
|
||||
|
||||
insert.bind(statementMock);
|
||||
insert.createBoundStatement(connectionMock);
|
||||
verify(statementMock).bind(0, "Foo");
|
||||
verify(statementMock).returnGeneratedValues(any(String[].class));
|
||||
verifyNoMoreInteractions(statementMock);
|
||||
@@ -161,7 +170,7 @@ public class StatementFactoryUnitTests {
|
||||
assertThat(update.getSource()).isInstanceOf(Update.class);
|
||||
assertThat(update.toQuery()).isEqualTo("UPDATE foo SET bar = $1");
|
||||
|
||||
update.bind(statementMock);
|
||||
update.createBoundStatement(connectionMock);
|
||||
verify(statementMock).bind(0, "Foo");
|
||||
verifyNoMoreInteractions(statementMock);
|
||||
}
|
||||
@@ -176,7 +185,7 @@ public class StatementFactoryUnitTests {
|
||||
assertThat(update.getSource()).isInstanceOf(Update.class);
|
||||
assertThat(update.toQuery()).isEqualTo("UPDATE foo SET bar = $1");
|
||||
|
||||
update.bind(statementMock);
|
||||
update.createBoundStatement(connectionMock);
|
||||
verify(statementMock).bindNull(0, String.class);
|
||||
|
||||
verifyNoMoreInteractions(statementMock);
|
||||
@@ -193,7 +202,7 @@ public class StatementFactoryUnitTests {
|
||||
assertThat(update.getSource()).isInstanceOf(Update.class);
|
||||
assertThat(update.toQuery()).isEqualTo("UPDATE foo SET bar = $1 WHERE foo.baz = $2");
|
||||
|
||||
update.bind(statementMock);
|
||||
update.createBoundStatement(connectionMock);
|
||||
verify(statementMock).bind(0, "Foo");
|
||||
verify(statementMock).bind(1, "Baz");
|
||||
verifyNoMoreInteractions(statementMock);
|
||||
@@ -209,7 +218,7 @@ public class StatementFactoryUnitTests {
|
||||
assertThat(delete.getSource()).isInstanceOf(Delete.class);
|
||||
assertThat(delete.toQuery()).isEqualTo("DELETE FROM foo WHERE foo.doe = $1");
|
||||
|
||||
delete.bind(statementMock);
|
||||
delete.createBoundStatement(connectionMock);
|
||||
verify(statementMock).bind(0, "John");
|
||||
verifyNoMoreInteractions(statementMock);
|
||||
}
|
||||
@@ -225,7 +234,7 @@ public class StatementFactoryUnitTests {
|
||||
assertThat(delete.getSource()).isInstanceOf(Delete.class);
|
||||
assertThat(delete.toQuery()).isEqualTo("DELETE FROM foo WHERE foo.doe = $1 AND foo.baz = $2");
|
||||
|
||||
delete.bind(statementMock);
|
||||
delete.createBoundStatement(connectionMock);
|
||||
verify(statementMock).bind(0, "John");
|
||||
verify(statementMock).bind(1, "Jake");
|
||||
verifyNoMoreInteractions(statementMock);
|
||||
@@ -241,7 +250,7 @@ public class StatementFactoryUnitTests {
|
||||
assertThat(delete.getSource()).isInstanceOf(Delete.class);
|
||||
assertThat(delete.toQuery()).isEqualTo("DELETE FROM foo WHERE foo.doe IS NULL");
|
||||
|
||||
delete.bind(statementMock);
|
||||
delete.createBoundStatement(connectionMock);
|
||||
verifyZeroInteractions(statementMock);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user