#73 - Polishing.

Original pull request: #82.
This commit is contained in:
Jens Schauder
2019-04-30 09:58:38 +02:00
committed by Mark Paluch
parent ef2d885b1e
commit 02e0cb5026
8 changed files with 505 additions and 111 deletions

View File

@@ -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;
}
}

View File

@@ -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;
}
}
}

View File

@@ -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) //

View File

@@ -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;
}
}
}

View File

@@ -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());
}
});
}
}

View File

@@ -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());
}
});
}
}

View File

@@ -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);
}

View File

@@ -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);
}
}