Skip to content

Commit

Permalink
Support for binding values from a source Map
Browse files Browse the repository at this point in the history
  • Loading branch information
jhoeller committed Aug 16, 2023
1 parent b8b3e6d commit 0b99872
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ interface GenericExecuteSpec {
* Bind a non-{@code null} value to a parameter identified by its
* {@code index}. {@code value} can be either a scalar value or {@link io.r2dbc.spi.Parameter}.
* @param index zero based index to bind the parameter to
* @param value either a scalar value or {@link io.r2dbc.spi.Parameter}
* @param value either a scalar value or a {@link io.r2dbc.spi.Parameter}
*/
GenericExecuteSpec bind(int index, Object value);

Expand All @@ -181,7 +181,7 @@ interface GenericExecuteSpec {
/**
* Bind a non-{@code null} value to a parameter identified by its {@code name}.
* @param name the name of the parameter
* @param value the value to bind
* @param value either a scalar value or a {@link io.r2dbc.spi.Parameter}
*/
GenericExecuteSpec bind(String name, Object value);

Expand All @@ -192,11 +192,22 @@ interface GenericExecuteSpec {
*/
GenericExecuteSpec bindNull(String name, Class<?> type);

/**
* Bind the parameter values from the given source map,
* registering each as a parameter with the map key as name.
* @param source the source map of parameters, with keys as names and
* each value either a scalar value or a {@link io.r2dbc.spi.Parameter}
* @since 6.1
* @see #bindProperties
*/
GenericExecuteSpec bindValues(Map<String, ?> source);

/**
* Bind the bean properties or record components from the given
* source object, registering each as a named parameter.
* @param source the source object (a JavaBean or record)
* @since 6.1
* @see #mapProperties
*/
GenericExecuteSpec bindProperties(Object source);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,23 +249,27 @@ class DefaultGenericExecuteSpec implements GenericExecuteSpec {
}

@SuppressWarnings("deprecation")
private Parameter resolveParameter(Object value) {
if (value instanceof Parameter param) {
return param;
}
else if (value instanceof org.springframework.r2dbc.core.Parameter param) {
Object paramValue = param.getValue();
return (paramValue != null ? Parameters.in(paramValue) : Parameters.in(param.getType()));
}
else {
return Parameters.in(value);
}
}

@Override
public DefaultGenericExecuteSpec bind(int index, Object value) {
assertNotPreparedOperation();
Assert.notNull(value, () -> String.format(
"Value at index %d must not be null. Use bindNull(…) instead.", index));

Map<Integer, Parameter> byIndex = new LinkedHashMap<>(this.byIndex);
if (value instanceof Parameter param) {
byIndex.put(index, param);
}
else if (value instanceof org.springframework.r2dbc.core.Parameter param) {
Object pv = param.getValue();
byIndex.put(index, (pv != null ? Parameters.in(pv) : Parameters.in(param.getType())));
}
else {
byIndex.put(index, Parameters.in(value));
}
byIndex.put(index, resolveParameter(value));

return new DefaultGenericExecuteSpec(byIndex, this.byName, this.sqlSupplier, this.filterFunction);
}
Expand All @@ -290,15 +294,7 @@ public DefaultGenericExecuteSpec bind(String name, Object value) {
"Value for parameter %s must not be null. Use bindNull(…) instead.", name));

Map<String, Parameter> byName = new LinkedHashMap<>(this.byName);
if (value instanceof Parameter p) {
byName.put(name, p);
}
else if (value instanceof org.springframework.r2dbc.core.Parameter p) {
byName.put(name, p.hasValue() ? Parameters.in(p.getValue()) : Parameters.in(p.getType()));
}
else {
byName.put(name, Parameters.in(value));
}
byName.put(name, resolveParameter(value));

return new DefaultGenericExecuteSpec(this.byIndex, byName, this.sqlSupplier, this.filterFunction);
}
Expand All @@ -314,6 +310,17 @@ public DefaultGenericExecuteSpec bindNull(String name, Class<?> type) {
return new DefaultGenericExecuteSpec(this.byIndex, byName, this.sqlSupplier, this.filterFunction);
}

@Override
public GenericExecuteSpec bindValues(Map<String, ?> source) {
assertNotPreparedOperation();
Assert.notNull(source, "Parameter source must not be null");

Map<String, Parameter> target = new LinkedHashMap<>(this.byName);
source.forEach((name, value) -> target.put(name, resolveParameter(value)));

return new DefaultGenericExecuteSpec(this.byIndex, target, this.sqlSupplier, this.filterFunction);
}

@Override
public DefaultGenericExecuteSpec bindProperties(Object source) {
assertNotPreparedOperation();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@

package org.springframework.r2dbc.core;

import java.util.Map;

import io.r2dbc.spi.ConnectionFactory;
import io.r2dbc.spi.Parameters;
import io.r2dbc.spi.Result;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -93,6 +96,27 @@ public void executeInsert() {
.verifyComplete();
}

@Test
public void executeInsertWithMap() {
DatabaseClient databaseClient = DatabaseClient.create(connectionFactory);

databaseClient.sql("INSERT INTO legoset (id, name, manual) VALUES(:id, :name, :manual)")
.bindValues(Map.of("id", 42055,
"name", Parameter.from("SCHAUFELRADBAGGER"),
"manual", Parameters.in(Integer.class)))
.fetch().rowsUpdated()
.as(StepVerifier::create)
.expectNext(1L)
.verifyComplete();

databaseClient.sql("SELECT id FROM legoset")
.mapValue(Integer.class)
.first()
.as(StepVerifier::create)
.assertNext(actual -> assertThat(actual).isEqualTo(42055))
.verifyComplete();
}

@Test
public void executeInsertWithRecords() {
DatabaseClient databaseClient = DatabaseClient.create(connectionFactory);
Expand Down

0 comments on commit 0b99872

Please sign in to comment.