Browse Source

Polish Javadoc and internals of R2DBC support

pull/35405/head
Sam Brannen 7 months ago
parent
commit
de97e35189
  1. 75
      spring-r2dbc/src/main/java/org/springframework/r2dbc/core/NamedParameterUtils.java
  2. 9
      spring-r2dbc/src/main/java/org/springframework/r2dbc/core/binding/AnonymousBindMarkers.java
  3. 11
      spring-r2dbc/src/main/java/org/springframework/r2dbc/core/binding/BindMarker.java
  4. 8
      spring-r2dbc/src/main/java/org/springframework/r2dbc/core/binding/BindMarkers.java
  5. 32
      spring-r2dbc/src/main/java/org/springframework/r2dbc/core/binding/BindMarkersFactoryResolver.java
  6. 15
      spring-r2dbc/src/main/java/org/springframework/r2dbc/core/binding/IndexedBindMarkers.java
  7. 180
      spring-r2dbc/src/test/java/org/springframework/r2dbc/core/NamedParameterUtilsTests.java

75
spring-r2dbc/src/main/java/org/springframework/r2dbc/core/NamedParameterUtils.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2025 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.
@ -513,14 +513,50 @@ abstract class NamedParameterUtils { @@ -513,14 +513,50 @@ abstract class NamedParameterUtils {
private final BindParameterSource parameterSource;
ExpandedQuery(String expandedSql, NamedParameters parameters, BindParameterSource parameterSource) {
this.expandedSql = expandedSql;
this.parameters = parameters;
this.parameterSource = parameterSource;
}
@Override
public String toQuery() {
return this.expandedSql;
}
@Override
public String getSource() {
return this.expandedSql;
}
@Override
public void bindTo(BindTarget target) {
for (String namedParameter : this.parameterSource.getParameterNames()) {
Parameter parameter = this.parameterSource.getValue(namedParameter);
if (parameter.getValue() == null) {
bindNull(target, namedParameter, parameter);
}
else {
bind(target, namedParameter, parameter);
}
}
}
private void bindNull(BindTarget target, String identifier, Parameter parameter) {
List<BindMarker> bindMarkers = getBindMarkers(identifier);
if (bindMarkers == null) {
target.bind(identifier, parameter);
return;
}
for (BindMarker bindMarker : bindMarkers) {
bindMarker.bind(target, parameter);
}
}
@SuppressWarnings({"rawtypes", "unchecked"})
public void bind(BindTarget target, String identifier, Parameter parameter) {
private void bind(BindTarget target, String identifier, Parameter parameter) {
List<BindMarker> bindMarkers = getBindMarkers(identifier);
if (bindMarkers == null) {
target.bind(identifier, parameter);
@ -555,19 +591,8 @@ abstract class NamedParameterUtils { @@ -555,19 +591,8 @@ abstract class NamedParameterUtils {
markers.next().bind(target, valueToBind);
}
public void bindNull(BindTarget target, String identifier, Parameter parameter) {
List<BindMarker> bindMarkers = getBindMarkers(identifier);
if (bindMarkers == null) {
target.bind(identifier, parameter);
return;
}
for (BindMarker bindMarker : bindMarkers) {
bindMarker.bind(target, parameter);
}
}
@Nullable
List<BindMarker> getBindMarkers(String identifier) {
private List<BindMarker> getBindMarkers(String identifier) {
List<NamedParameters.NamedParameter> parameters = this.parameters.getMarker(identifier);
if (parameters == null) {
return null;
@ -579,28 +604,6 @@ abstract class NamedParameterUtils { @@ -579,28 +604,6 @@ abstract class NamedParameterUtils {
return markers;
}
@Override
public String getSource() {
return this.expandedSql;
}
@Override
public void bindTo(BindTarget target) {
for (String namedParameter : this.parameterSource.getParameterNames()) {
Parameter parameter = this.parameterSource.getValue(namedParameter);
if (parameter.getValue() == null) {
bindNull(target, namedParameter, parameter);
}
else {
bind(target, namedParameter, parameter);
}
}
}
@Override
public String toQuery() {
return this.expandedSql;
}
}
}

9
spring-r2dbc/src/main/java/org/springframework/r2dbc/core/binding/AnonymousBindMarkers.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2025 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.
@ -19,8 +19,9 @@ package org.springframework.r2dbc.core.binding; @@ -19,8 +19,9 @@ package org.springframework.r2dbc.core.binding;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
/**
* Anonymous, index-based bind marker using a static placeholder.
* Instances are bound by the ordinal position ordered by the appearance of
* Anonymous, index-based bind markers that use a static placeholder.
*
* <p>Instances are bound by the ordinal position ordered by the appearance of
* the placeholder. This implementation creates indexed bind markers using
* an anonymous placeholder that correlates with an index.
*
@ -46,7 +47,7 @@ class AnonymousBindMarkers implements BindMarkers { @@ -46,7 +47,7 @@ class AnonymousBindMarkers implements BindMarkers {
/**
* Create a new {@link AnonymousBindMarkers} instance given {@code placeholder}.
* Create a new {@link AnonymousBindMarkers} instance for the given {@code placeholder}.
* @param placeholder parameter bind marker
*/
AnonymousBindMarkers(String placeholder) {

11
spring-r2dbc/src/main/java/org/springframework/r2dbc/core/binding/BindMarker.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2025 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.
@ -20,7 +20,8 @@ import io.r2dbc.spi.Statement; @@ -20,7 +20,8 @@ import io.r2dbc.spi.Statement;
/**
* A bind marker represents a single bindable parameter within a query.
* Bind markers are dialect-specific and provide a
*
* <p>Bind markers are dialect-specific and provide a
* {@link #getPlaceholder() placeholder} that is used in the actual query.
*
* @author Mark Paluch
@ -37,7 +38,8 @@ public interface BindMarker { @@ -37,7 +38,8 @@ public interface BindMarker {
String getPlaceholder();
/**
* Bind the given {@code value} to the {@link Statement} using the underlying binding strategy.
* Bind the given {@code value} to the {@link Statement} using the underlying
* binding strategy.
* @param bindTarget the target to bind the value to
* @param value the actual value (must not be {@code null};
* use {@link #bindNull(BindTarget, Class)} for {@code null} values)
@ -46,7 +48,8 @@ public interface BindMarker { @@ -46,7 +48,8 @@ public interface BindMarker {
void bind(BindTarget bindTarget, Object value);
/**
* Bind a {@code null} value to the {@link Statement} using the underlying binding strategy.
* Bind a {@code null} value to the {@link Statement} using the underlying
* binding strategy.
* @param bindTarget the target to bind the value to
* @param valueType the value type (must not be {@code null})
* @see Statement#bindNull

8
spring-r2dbc/src/main/java/org/springframework/r2dbc/core/binding/BindMarkers.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2025 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.
@ -20,10 +20,10 @@ package org.springframework.r2dbc.core.binding; @@ -20,10 +20,10 @@ package org.springframework.r2dbc.core.binding;
* Bind markers represent placeholders in SQL queries for substitution
* for an actual parameter. Using bind markers allows creating safe queries
* so query strings are not required to contain escaped values but rather
* the driver encodes parameter in the appropriate representation.
* the driver encodes the parameter in the appropriate representation.
*
* <p>{@link BindMarkers} is stateful and can be only used for a single binding
* pass of one or more parameters. It maintains bind indexes/bind parameter names.
* pass of one or more parameters. It maintains bind indexes or bind parameter names.
*
* @author Mark Paluch
* @since 5.3
@ -41,7 +41,7 @@ public interface BindMarkers { @@ -41,7 +41,7 @@ public interface BindMarkers {
/**
* Create a new {@link BindMarker} that accepts a {@code hint}.
* Implementations are allowed to consider/ignore/filter
* <p>Implementations are allowed to consider/ignore/filter
* the name hint to create more expressive bind markers.
* @param hint an optional name hint that can be used as part of the bind marker
* @return a new {@link BindMarker}

32
spring-r2dbc/src/main/java/org/springframework/r2dbc/core/binding/BindMarkersFactoryResolver.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2025 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.
@ -30,8 +30,8 @@ import org.springframework.util.LinkedCaseInsensitiveMap; @@ -30,8 +30,8 @@ import org.springframework.util.LinkedCaseInsensitiveMap;
/**
* Resolves a {@link BindMarkersFactory} from a {@link ConnectionFactory} using
* {@link BindMarkerFactoryProvider}. Dialect resolution uses Spring's
* {@link SpringFactoriesLoader spring.factories} to determine available extensions.
* a {@link BindMarkerFactoryProvider}. Dialect resolution uses Spring's
* {@link SpringFactoriesLoader spring.factories} file to determine available extensions.
*
* @author Mark Paluch
* @since 5.3
@ -45,8 +45,8 @@ public final class BindMarkersFactoryResolver { @@ -45,8 +45,8 @@ public final class BindMarkersFactoryResolver {
/**
* Retrieve a {@link BindMarkersFactory} by inspecting {@link ConnectionFactory}
* and its metadata.
* Retrieve a {@link BindMarkersFactory} by inspecting the supplied
* {@link ConnectionFactory} and its metadata.
* @param connectionFactory the connection factory to inspect
* @return the resolved {@link BindMarkersFactory}
* @throws NoBindMarkersFactoryException if no {@link BindMarkersFactory} can be resolved
@ -69,18 +69,21 @@ public final class BindMarkersFactoryResolver { @@ -69,18 +69,21 @@ public final class BindMarkersFactoryResolver {
/**
* SPI to extend Spring's default R2DBC BindMarkersFactory discovery mechanism.
* Implementations of this interface are discovered through Spring's
* SPI to extend Spring's default R2DBC {@link BindMarkersFactory} discovery
* mechanism.
*
* <p>Implementations of this interface are discovered through Spring's
* {@link SpringFactoriesLoader} mechanism.
*
* @see SpringFactoriesLoader
*/
@FunctionalInterface
public interface BindMarkerFactoryProvider {
/**
* Return a {@link BindMarkersFactory} for a {@link ConnectionFactory}.
* @param connectionFactory the connection factory to be used with the {@link BindMarkersFactory}
* @return the {@link BindMarkersFactory} if the {@link BindMarkerFactoryProvider}
* Return a {@link BindMarkersFactory} for the given {@link ConnectionFactory}.
* @param connectionFactory the connection factory to be used with the {@code BindMarkersFactory}
* @return the {@code BindMarkersFactory} if this {@code BindMarkerFactoryProvider}
* can provide a bind marker factory object, otherwise {@code null}
*/
@Nullable
@ -89,7 +92,7 @@ public final class BindMarkersFactoryResolver { @@ -89,7 +92,7 @@ public final class BindMarkersFactoryResolver {
/**
* Exception thrown when {@link BindMarkersFactoryResolver} cannot resolve a
* Exception thrown when a {@link BindMarkersFactoryResolver} cannot resolve a
* {@link BindMarkersFactory}.
*/
@SuppressWarnings("serial")
@ -106,8 +109,11 @@ public final class BindMarkersFactoryResolver { @@ -106,8 +109,11 @@ public final class BindMarkersFactoryResolver {
/**
* Built-in bind maker factories. Used typically as last {@link BindMarkerFactoryProvider}
* when other providers register with a higher precedence.
* Built-in bind marker factories.
*
* <p>Typically used as the last {@link BindMarkerFactoryProvider} when other
* providers are registered with a higher precedence.
*
* @see org.springframework.core.Ordered
* @see org.springframework.core.annotation.AnnotationAwareOrderComparator
*/

15
spring-r2dbc/src/main/java/org/springframework/r2dbc/core/binding/IndexedBindMarkers.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2025 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.
@ -19,7 +19,7 @@ package org.springframework.r2dbc.core.binding; @@ -19,7 +19,7 @@ package org.springframework.r2dbc.core.binding;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
/**
* Index-based bind marker. This implementation creates indexed bind
* Index-based bind markers. This implementation creates indexed bind
* markers using a numeric index and an optional prefix for bind markers
* to be represented within the query string.
*
@ -43,14 +43,15 @@ class IndexedBindMarkers implements BindMarkers { @@ -43,14 +43,15 @@ class IndexedBindMarkers implements BindMarkers {
/**
* Create a new {@link IndexedBindMarker} instance given {@code prefix} and {@code beginWith}.
* @param prefix bind parameter prefix
* @param beginWith the first index to use
* Create a new {@link IndexedBindMarker} instance for the given {@code prefix}
* and {@code beginWith} value.
* @param prefix the bind parameter prefix
* @param beginIndex the first index to use
*/
IndexedBindMarkers(String prefix, int beginWith) {
IndexedBindMarkers(String prefix, int beginIndex) {
this.counter = 0;
this.prefix = prefix;
this.offset = beginWith;
this.offset = beginIndex;
}

180
spring-r2dbc/src/test/java/org/springframework/r2dbc/core/NamedParameterUtilsTests.java

@ -28,8 +28,6 @@ import org.junit.jupiter.params.provider.ValueSource; @@ -28,8 +28,6 @@ import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.r2dbc.core.binding.BindMarkersFactory;
import org.springframework.r2dbc.core.binding.BindTarget;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
@ -320,34 +318,17 @@ class NamedParameterUtilsTests { @@ -320,34 +318,17 @@ class NamedParameterUtilsTests {
assertThat(operation.toQuery())
.isEqualTo("SELECT * FROM person where name = $1 or lastname = $1");
Map<Integer, Object> bindings = new HashMap<>();
operation.bindTo(new BindTarget() {
@Override
public void bind(String identifier, Object value) {
throw new UnsupportedOperationException();
}
@Override
public void bind(int index, Object value) {
bindings.put(index, value);
}
@Override
public void bindNull(String identifier, Class<?> type) {
throw new UnsupportedOperationException();
}
@Override
public void bindNull(int index, Class<?> type) {
throw new UnsupportedOperationException();
}
});
assertThat(bindings)
TrackingBindTarget trackingBindTarget = new TrackingBindTarget();
operation.bindTo(trackingBindTarget);
assertThat(trackingBindTarget.bindings)
.hasSize(1)
.containsEntry(0, Parameters.in("foo"));
}
@Test
void multipleEqualCollectionParameterReferencesForIndexedMarkersBindsValueOnce() {
void multipleEqualCollectionParameterReferencesForIndexedMarkersBindsValuesOnce() {
String sql = "SELECT * FROM person where name IN (:ids) or lastname IN (:ids)";
MapBindParameterSource source = new MapBindParameterSource(Map.of("ids",
@ -357,71 +338,40 @@ class NamedParameterUtilsTests { @@ -357,71 +338,40 @@ class NamedParameterUtilsTests {
assertThat(operation.toQuery())
.isEqualTo("SELECT * FROM person where name IN ($1, $2, $3) or lastname IN ($1, $2, $3)");
MultiValueMap<Integer, Object> bindings = new LinkedMultiValueMap<>();
operation.bindTo(new BindTarget() {
@Override
public void bind(String identifier, Object value) {
throw new UnsupportedOperationException();
}
@Override
public void bind(int index, Object value) {
bindings.add(index, value);
}
@Override
public void bindNull(String identifier, Class<?> type) {
throw new UnsupportedOperationException();
}
@Override
public void bindNull(int index, Class<?> type) {
throw new UnsupportedOperationException();
}
});
assertThat(bindings)
TrackingBindTarget trackingBindTarget = new TrackingBindTarget();
operation.bindTo(trackingBindTarget);
assertThat(trackingBindTarget.bindings)
.hasSize(3)
.containsEntry(0, List.of("foo"))
.containsEntry(1, List.of("bar"))
.containsEntry(2, List.of("baz"));
.containsEntry(0, "foo")
.containsEntry(1, "bar")
.containsEntry(2, "baz");
}
@Test // gh-34768
@Disabled("Disabled until gh-34768 is addressed")
void multipleEqualCollectionParameterReferencesForAnonymousMarkersBindsValueTwice() {
void multipleEqualCollectionParameterReferencesForAnonymousMarkersBindsValuesTwice() {
String sql = "SELECT * FROM fund_info WHERE fund_code IN (:fundCodes) OR fund_code IN (:fundCodes)";
MapBindParameterSource source = new MapBindParameterSource(Map.of("fundCodes", Parameters.in(List.of("foo"))));
MapBindParameterSource source = new MapBindParameterSource(Map.of("fundCodes", Parameters.in(List.of("foo", "bar", "baz"))));
PreparedOperation<String> operation = NamedParameterUtils.substituteNamedParameters(sql, ANONYMOUS_MARKERS, source);
assertThat(operation.toQuery())
.isEqualTo("SELECT * FROM fund_info WHERE fund_code IN (?) OR fund_code IN (?)");
Map<Integer, Object> bindings = new HashMap<>();
.isEqualTo("SELECT * FROM fund_info WHERE fund_code IN (?, ?, ?) OR fund_code IN (?, ?, ?)");
operation.bindTo(new BindTarget() {
@Override
public void bind(String identifier, Object value) {}
TrackingBindTarget trackingBindTarget = new TrackingBindTarget();
@Override
public void bind(int index, Object value) {
bindings.put(index, value);
}
operation.bindTo(trackingBindTarget);
@Override
public void bindNull(String identifier, Class<?> type) {
throw new UnsupportedOperationException();
}
@Override
public void bindNull(int index, Class<?> type) {
throw new UnsupportedOperationException();
}
});
assertThat(bindings)
.hasSize(2)
assertThat(trackingBindTarget.bindings)
.hasSize(6)
.containsEntry(0, "foo")
.containsEntry(1, "foo");
.containsEntry(1, "bar")
.containsEntry(2, "baz")
.containsEntry(3, "foo")
.containsEntry(4, "bar")
.containsEntry(5, "baz");
}
@Test
@ -434,28 +384,11 @@ class NamedParameterUtilsTests { @@ -434,28 +384,11 @@ class NamedParameterUtilsTests {
assertThat(operation.toQuery())
.isEqualTo("SELECT * FROM person where name = ? or lastname = ?");
Map<Integer, Object> bindings = new HashMap<>();
operation.bindTo(new BindTarget() {
@Override
public void bind(String identifier, Object value) {
throw new UnsupportedOperationException();
}
@Override
public void bind(int index, Object value) {
bindings.put(index, value);
}
@Override
public void bindNull(String identifier, Class<?> type) {
throw new UnsupportedOperationException();
}
@Override
public void bindNull(int index, Class<?> type) {
throw new UnsupportedOperationException();
}
});
assertThat(bindings)
TrackingBindTarget trackingBindTarget = new TrackingBindTarget();
operation.bindTo(trackingBindTarget);
assertThat(trackingBindTarget.bindings)
.hasSize(2)
.containsEntry(0, Parameters.in("foo"))
.containsEntry(1, Parameters.in("foo"));
@ -471,28 +404,11 @@ class NamedParameterUtilsTests { @@ -471,28 +404,11 @@ class NamedParameterUtilsTests {
assertThat(operation.toQuery())
.isEqualTo("SELECT * FROM person where name = $1 or lastname = $1");
Map<Integer, Object> bindings = new HashMap<>();
operation.bindTo(new BindTarget() {
@Override
public void bind(String identifier, Object value) {
throw new UnsupportedOperationException();
}
@Override
public void bind(int index, Object value) {
bindings.put(index, value);
}
@Override
public void bindNull(String identifier, Class<?> type) {
throw new UnsupportedOperationException();
}
@Override
public void bindNull(int index, Class<?> type) {
throw new UnsupportedOperationException();
}
});
assertThat(bindings)
TrackingBindTarget trackingBindTarget = new TrackingBindTarget();
operation.bindTo(trackingBindTarget);
assertThat(trackingBindTarget.bindings)
.hasSize(1)
.containsEntry(0, Parameters.in(String.class));
}
@ -508,4 +424,28 @@ class NamedParameterUtilsTests { @@ -508,4 +424,28 @@ class NamedParameterUtilsTests {
new MapBindParameterSource()).toQuery();
}
private static class TrackingBindTarget implements BindTarget {
final Map<Integer, Object> bindings = new HashMap<>();
@Override
public void bind(String identifier, Object value) {}
@Override
public void bind(int index, Object value) {
this.bindings.put(index, value);
}
@Override
public void bindNull(String identifier, Class<?> type) {
throw new UnsupportedOperationException();
}
@Override
public void bindNull(int index, Class<?> type) {
throw new UnsupportedOperationException();
}
}
}

Loading…
Cancel
Save