Add DatabaseClient bind variant for list of parameters
Prior to this commit, the `DatabaseClient` interface would allow batch operations for binding parameters by their names and values. Positional parameters did not have such equivalent. This commit adds a new `bindValues(List<?>)` method variant for adding multiple positional arguments in a single call and avoiding allocation overhead when the parameters count is large. Closes gh-33274
This commit is contained in:
parent
1f2c6c33ac
commit
38453910cd
|
@ -364,6 +364,28 @@ Or you may pass in a parameter object with bean properties or record components:
|
||||||
.bindProperties(new Person("joe", "Joe", 34);
|
.bindProperties(new Person("joe", "Joe", 34);
|
||||||
----
|
----
|
||||||
|
|
||||||
|
Alternatively, you can use positional parameters for binding values to statements.
|
||||||
|
Indices are zero based.
|
||||||
|
|
||||||
|
[source,java]
|
||||||
|
----
|
||||||
|
db.sql("INSERT INTO person (id, name, age) VALUES(:id, :name, :age)")
|
||||||
|
.bind(0, "joe")
|
||||||
|
.bind(1, "Joe")
|
||||||
|
.bind(2, 34);
|
||||||
|
----
|
||||||
|
|
||||||
|
In case your application is binding to many parameters, the same can be achieved with a single call:
|
||||||
|
|
||||||
|
[source,java]
|
||||||
|
----
|
||||||
|
List<?> values = List.of("joe", "Joe", 34);
|
||||||
|
db.sql("INSERT INTO person (id, name, age) VALUES(:id, :name, :age)")
|
||||||
|
.bindValues(values);
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.R2DBC Native Bind Markers
|
.R2DBC Native Bind Markers
|
||||||
****
|
****
|
||||||
R2DBC uses database-native bind markers that depend on the actual database vendor.
|
R2DBC uses database-native bind markers that depend on the actual database vendor.
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2023 the original author or authors.
|
* Copyright 2002-2024 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
package org.springframework.r2dbc.core;
|
package org.springframework.r2dbc.core;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
@ -56,6 +57,7 @@ import org.springframework.util.Assert;
|
||||||
*
|
*
|
||||||
* @author Mark Paluch
|
* @author Mark Paluch
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
|
* @author Brian Clozel
|
||||||
* @since 5.3
|
* @since 5.3
|
||||||
*/
|
*/
|
||||||
public interface DatabaseClient extends ConnectionAccessor {
|
public interface DatabaseClient extends ConnectionAccessor {
|
||||||
|
@ -191,6 +193,18 @@ public interface DatabaseClient extends ConnectionAccessor {
|
||||||
*/
|
*/
|
||||||
GenericExecuteSpec bindNull(String name, Class<?> type);
|
GenericExecuteSpec bindNull(String name, Class<?> type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bind the parameter values from the given source list,
|
||||||
|
* registering each as a positional parameter using their order
|
||||||
|
* in the given list as their index.
|
||||||
|
* @param source the source list of parameters, with their order
|
||||||
|
* as position and each value either a scalar value
|
||||||
|
* or a {@link io.r2dbc.spi.Parameter}
|
||||||
|
* @since 6.2
|
||||||
|
* @see #bind(int, Object)
|
||||||
|
*/
|
||||||
|
GenericExecuteSpec bindValues(List<?> source);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bind the parameter values from the given source map,
|
* Bind the parameter values from the given source map,
|
||||||
* registering each as a parameter with the map key as name.
|
* registering each as a parameter with the map key as name.
|
||||||
|
|
|
@ -24,6 +24,7 @@ import java.lang.reflect.Proxy;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.ListIterator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
|
@ -308,6 +309,18 @@ final class DefaultDatabaseClient implements DatabaseClient {
|
||||||
return new DefaultGenericExecuteSpec(this.byIndex, byName, this.sqlSupplier, this.filterFunction);
|
return new DefaultGenericExecuteSpec(this.byIndex, byName, this.sqlSupplier, this.filterFunction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GenericExecuteSpec bindValues(List<?> source) {
|
||||||
|
assertNotPreparedOperation();
|
||||||
|
Assert.notNull(source, "Source list must not be null");
|
||||||
|
Map<Integer, Parameter> byIndex = new LinkedHashMap<>(this.byIndex);
|
||||||
|
ListIterator<?> listIterator = source.listIterator();
|
||||||
|
while (listIterator.hasNext()) {
|
||||||
|
byIndex.put(listIterator.nextIndex(), resolveParameter(listIterator.next()));
|
||||||
|
}
|
||||||
|
return new DefaultGenericExecuteSpec(byIndex, this.byName, this.sqlSupplier, this.filterFunction);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GenericExecuteSpec bindValues(Map<String, ?> source) {
|
public GenericExecuteSpec bindValues(Map<String, ?> source) {
|
||||||
assertNotPreparedOperation();
|
assertNotPreparedOperation();
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
package org.springframework.r2dbc.core;
|
package org.springframework.r2dbc.core;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import io.r2dbc.spi.ConnectionFactory;
|
import io.r2dbc.spi.ConnectionFactory;
|
||||||
|
@ -96,6 +97,25 @@ abstract class AbstractDatabaseClientIntegrationTests {
|
||||||
.verifyComplete();
|
.verifyComplete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void executeInsertWithList() {
|
||||||
|
DatabaseClient databaseClient = DatabaseClient.create(connectionFactory);
|
||||||
|
|
||||||
|
databaseClient.sql("INSERT INTO legoset (id, name, manual) VALUES(:id, :name, :manual)")
|
||||||
|
.bindValues(List.of(42055, Parameters.in("SCHAUFELRADBAGGER"), 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
|
@Test
|
||||||
void executeInsertWithMap() {
|
void executeInsertWithMap() {
|
||||||
DatabaseClient databaseClient = DatabaseClient.create(connectionFactory);
|
DatabaseClient databaseClient = DatabaseClient.create(connectionFactory);
|
||||||
|
|
Loading…
Reference in New Issue