@@ -120,4 +120,4 @@ The annotated query uses native bind markers, which are Postgres bind markers in
|
||||
|
||||
NOTE: R2DBC repositories do not support query derivation.
|
||||
|
||||
NOTE: R2DBC repositories bind parameters to placeholders by index.
|
||||
NOTE: R2DBC repositories bind internally parameters to placeholders via `Statement.bind(…)` by index.
|
||||
|
||||
@@ -459,13 +459,16 @@ db.execute()
|
||||
|
||||
.R2DBC Native Bind Markers
|
||||
****
|
||||
R2DBC uses database-native bind markers that depend on the actual database.
|
||||
If you are familiar with JDBC, then you're also familiar with `?` (question mark) bind markers.
|
||||
JDBC drivers translate question mark bind markers to database-native markers as part of statement execution.
|
||||
R2DBC uses database-native bind markers that depend on the actual database vendor.
|
||||
As an example, Postgres uses indexed markers such as `$1`, `$2`, `$n`.
|
||||
Another example is SQL Server that uses named bind markers prefixed with `@` (at).
|
||||
|
||||
Postgres uses indexed markers (`$1`, `$2`), SQL Server uses named bind markers prefixed with `@` as its native bind marker syntax.
|
||||
Spring Data R2DBC leverages `Dialect` implementations to expand named parameters to native bind markers at the time of query execution which gives you a certain degree of query portability across various database vendors.
|
||||
You can still use native bind markers if you prefer to do so.
|
||||
This is different from JDBC which requires `?` (question mark) as bind markers.
|
||||
In JDBC, the actual drivers translate question mark bind markers to database-native markers as part of their statement execution.
|
||||
|
||||
Spring Data R2DBC allows you to use native bind markers or named bind markers with the `:name` syntax.
|
||||
|
||||
Named parameter support leverages ``Dialect``s to expand named parameters to native bind markers at the time of query execution which gives you a certain degree of query portability across various database vendors.
|
||||
****
|
||||
|
||||
The query-preprocessor unrolls named `Collection` parameters into a series of bind markers to remove the need of dynamic query creation based on the number of arguments.
|
||||
|
||||
@@ -112,12 +112,12 @@ public interface DatabaseClient {
|
||||
/**
|
||||
* Configures {@link NamedParameterExpander}.
|
||||
*
|
||||
* @param namedParameters must not be {@literal null}.
|
||||
* @param expander must not be {@literal null}.
|
||||
* @return {@code this} {@link Builder}.
|
||||
* @see NamedParameterExpander#enabled()
|
||||
* @see NamedParameterExpander#disabled()
|
||||
*/
|
||||
Builder namedParameters(NamedParameterExpander namedParameters);
|
||||
Builder namedParameters(NamedParameterExpander expander);
|
||||
|
||||
/**
|
||||
* Configures a {@link Consumer} to configure this builder.
|
||||
|
||||
@@ -254,14 +254,11 @@ class DefaultDatabaseClient implements DatabaseClient, ConnectionAccessor {
|
||||
private static void doBind(Statement<?> statement, Map<String, SettableValue> byName,
|
||||
Map<Integer, SettableValue> byIndex) {
|
||||
|
||||
byIndex.forEach((i, o) -> {
|
||||
bindByIndex(statement, byIndex);
|
||||
bindByName(statement, byName);
|
||||
}
|
||||
|
||||
if (o.getValue() != null) {
|
||||
statement.bind(i.intValue(), o.getValue());
|
||||
} else {
|
||||
statement.bindNull(i.intValue(), o.getType());
|
||||
}
|
||||
});
|
||||
private static void bindByName(Statement<?> statement, Map<String, SettableValue> byName) {
|
||||
|
||||
byName.forEach((name, o) -> {
|
||||
|
||||
@@ -273,6 +270,18 @@ class DefaultDatabaseClient implements DatabaseClient, ConnectionAccessor {
|
||||
});
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Default {@link DatabaseClient.SqlSpec} implementation.
|
||||
*/
|
||||
@@ -340,7 +349,7 @@ class DefaultDatabaseClient implements DatabaseClient, ConnectionAccessor {
|
||||
}
|
||||
});
|
||||
|
||||
doBind(statement, Collections.emptyMap(), byIndex);
|
||||
bindByIndex(statement, byIndex);
|
||||
|
||||
return statement;
|
||||
};
|
||||
|
||||
@@ -96,11 +96,11 @@ class DefaultDatabaseClientBuilder implements DatabaseClient.Builder {
|
||||
* @see org.springframework.data.r2dbc.function.DatabaseClient.Builder#namedParameters(org.springframework.data.r2dbc.function.NamedParameterExpander)
|
||||
*/
|
||||
@Override
|
||||
public Builder namedParameters(NamedParameterExpander namedParameters) {
|
||||
public Builder namedParameters(NamedParameterExpander expander) {
|
||||
|
||||
Assert.notNull(namedParameters, "NamedParameterExpander must not be null!");
|
||||
Assert.notNull(expander, "NamedParameterExpander must not be null!");
|
||||
|
||||
this.namedParameters = namedParameters;
|
||||
this.namedParameters = expander;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -73,8 +73,8 @@ class DefaultTransactionalDatabaseClientBuilder extends DefaultDatabaseClientBui
|
||||
* @see org.springframework.data.r2dbc.function.DefaultDatabaseClientBuilder#dataAccessStrategy(org.springframework.data.r2dbc.function.NamedParameterSupport)
|
||||
*/
|
||||
@Override
|
||||
public TransactionalDatabaseClient.Builder namedParameters(NamedParameterExpander namedParameters) {
|
||||
super.namedParameters(namedParameters);
|
||||
public TransactionalDatabaseClient.Builder namedParameters(NamedParameterExpander expander) {
|
||||
super.namedParameters(expander);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -162,16 +162,6 @@ abstract class NamedParameterUtils {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (c == '?') {
|
||||
int j = i + 1;
|
||||
if (j < statement.length && (statement[j] == '?' || statement[j] == '|' || statement[j] == '&')) {
|
||||
// Postgres-style "??", "?|", "?&" operator should be skipped
|
||||
i = i + 2;
|
||||
continue;
|
||||
}
|
||||
unnamedParameterCount++;
|
||||
totalParameterCount++;
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
@@ -192,12 +192,12 @@ public interface TransactionalDatabaseClient extends DatabaseClient {
|
||||
/**
|
||||
* Configures {@link NamedParameterExpander}.
|
||||
*
|
||||
* @param namedParameters must not be {@literal null}.
|
||||
* @param expander must not be {@literal null}.
|
||||
* @return {@code this} {@link Builder}.
|
||||
* @see NamedParameterExpander#enabled()
|
||||
* @see NamedParameterExpander#disabled()
|
||||
*/
|
||||
Builder namedParameters(NamedParameterExpander namedParameters);
|
||||
Builder namedParameters(NamedParameterExpander expander);
|
||||
|
||||
/**
|
||||
* Configures a {@link Consumer} to configure this builder.
|
||||
|
||||
@@ -50,7 +50,7 @@ public class NamedParameterUtilsUnitTests {
|
||||
String sql2 = "xxx &a yyyy ? zzzzz";
|
||||
ParsedSql psql2 = NamedParameterUtils.parseSqlStatement(sql2);
|
||||
assertThat(psql2.getParameterNames()).containsExactly("a");
|
||||
assertThat(psql2.getTotalParameterCount()).isEqualTo(2);
|
||||
assertThat(psql2.getTotalParameterCount()).isEqualTo(1);
|
||||
assertThat(psql2.getNamedParameterCount()).isEqualTo(1);
|
||||
|
||||
String sql3 = "xxx &ä+:ö" + '\t' + ":ü%10 yyyy ? zzzzz";
|
||||
|
||||
Reference in New Issue
Block a user