Skip to content

Commit 21e35b2

Browse files
committed
spring-projects#282 - Implement query parameter binding
1 parent b222607 commit 21e35b2

File tree

3 files changed

+72
-30
lines changed

3 files changed

+72
-30
lines changed

Diff for: src/main/java/org/springframework/data/r2dbc/repository/query/ParameterMetadataProvider.java

+13-8
Original file line numberDiff line numberDiff line change
@@ -78,14 +78,19 @@ private ParameterMetadataProvider(Parameters<?, ?> parameters,
7878
* Creates new instance of {@link ParameterMetadata} for the given {@link Part} and next {@link Parameter}.
7979
*/
8080
public ParameterMetadata next(Part part) {
81-
Assert.isTrue(bindableParameterIterator.hasNext(),
82-
() -> String.format("No parameter available for part %s.", part));
81+
Assert.isTrue(bindableParameterIterator.hasNext(), () -> String.format("No parameter available for part %s.", part));
8382
Parameter parameter = bindableParameterIterator.next();
84-
String parameterName = getParameterName(parameter);
85-
Class<?> parameterType = parameter.getType();
86-
Object parameterValue = getParameterValue();
87-
boolean isNullProperty = parameterValue == null && Part.Type.SIMPLE_PROPERTY.equals(part.getType());
88-
return new ParameterMetadata(parameterName, parameterType, isNullProperty);
83+
String name = getParameterName(parameter);
84+
Class<?> type = parameter.getType();
85+
Object value = getParameterValue();
86+
boolean isNullProperty = value == null && Part.Type.SIMPLE_PROPERTY.equals(part.getType());
87+
ParameterMetadata metadata = new ParameterMetadata(name, type, isNullProperty);
88+
parameterMetadata.add(metadata);
89+
return metadata;
90+
}
91+
92+
public ParameterMetadata getParameterMetadata(int index) {
93+
return parameterMetadata.get(index);
8994
}
9095

9196
@Nullable
@@ -102,7 +107,7 @@ private Object getParameterValue() {
102107
}
103108

104109
/**
105-
* Helper class for holding information about query parameter like parameter name and type.
110+
* Helper class to hold information about query parameter.
106111
*/
107112
static class ParameterMetadata {
108113
@Nullable

Diff for: src/main/java/org/springframework/data/r2dbc/repository/query/PartTreeR2dbcQuery.java

+52-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@
1818
import org.springframework.data.domain.Sort;
1919
import org.springframework.data.r2dbc.convert.R2dbcConverter;
2020
import org.springframework.data.r2dbc.core.DatabaseClient;
21+
import org.springframework.data.r2dbc.core.DatabaseClient.BindSpec;
2122
import org.springframework.data.r2dbc.core.ReactiveDataAccessStrategy;
23+
import org.springframework.data.r2dbc.repository.query.ParameterMetadataProvider.ParameterMetadata;
2224
import org.springframework.data.relational.repository.query.RelationalEntityMetadata;
2325
import org.springframework.data.relational.repository.query.RelationalParameterAccessor;
2426
import org.springframework.data.relational.repository.query.RelationalParameters;
@@ -68,8 +70,56 @@ public PartTreeR2dbcQuery(R2dbcQueryMethod method,
6870
protected BindableQuery createQuery(RelationalParameterAccessor accessor) {
6971
RelationalEntityMetadata<?> entityMetadata = getQueryMethod().getEntityInformation();
7072
ParameterMetadataProvider parameterMetadataProvider = new ParameterMetadataProvider(accessor);
71-
return new R2dbcQueryCreator(tree, dataAccessStrategy, entityMetadata, parameterMetadataProvider)
72-
.createQuery(getDynamicSort(accessor));
73+
R2dbcQueryCreator queryCreator = new R2dbcQueryCreator(tree, dataAccessStrategy, entityMetadata,
74+
parameterMetadataProvider);
75+
String sql = queryCreator.createQuery(getDynamicSort(accessor));
76+
return new BindableQuery() {
77+
@Override
78+
public <T extends BindSpec<T>> T bind(T bindSpec) {
79+
T bindSpecToUse = bindSpec;
80+
81+
int index = 0;
82+
int bindingIndex = 0;
83+
84+
for (Object value : accessor.getValues()) {
85+
ParameterMetadata metadata = parameterMetadataProvider.getParameterMetadata(index++);
86+
String parameterName = metadata.getName();
87+
Class<?> parameterType = metadata.getType();
88+
89+
if (parameterName != null) {
90+
if (value == null) {
91+
checkNullIsAllowed(metadata, "Value of parameter with name %s must not be null!",
92+
parameterName);
93+
bindSpecToUse = bindSpecToUse.bindNull(parameterName, parameterType);
94+
} else {
95+
bindSpecToUse = bindSpecToUse.bind(parameterName, value);
96+
}
97+
} else {
98+
if (value == null) {
99+
checkNullIsAllowed(metadata, "Value of parameter with index %d must not be null!",
100+
bindingIndex);
101+
bindSpecToUse = bindSpecToUse.bindNull(bindingIndex++, parameterType);
102+
} else {
103+
bindSpecToUse = bindSpecToUse.bind(bindingIndex++, value);
104+
}
105+
}
106+
}
107+
108+
return bindSpecToUse;
109+
}
110+
111+
@Override
112+
public String get() {
113+
return sql;
114+
}
115+
116+
private void checkNullIsAllowed(ParameterMetadata metadata, String errorMessage, Object messageArgument) {
117+
if (!metadata.isIsNullParameter()) {
118+
String message = String.format(errorMessage, messageArgument);
119+
throw new IllegalArgumentException(message);
120+
}
121+
}
122+
};
73123
}
74124

75125
private Sort getDynamicSort(RelationalParameterAccessor accessor) {

Diff for: src/main/java/org/springframework/data/r2dbc/repository/query/R2dbcQueryCreator.java

+7-20
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717

1818
import org.springframework.data.domain.Sort;
1919
import org.springframework.data.r2dbc.convert.R2dbcConverter;
20-
import org.springframework.data.r2dbc.core.DatabaseClient;
2120
import org.springframework.data.r2dbc.core.ReactiveDataAccessStrategy;
2221
import org.springframework.data.relational.core.sql.Condition;
2322
import org.springframework.data.relational.core.sql.Expression;
@@ -32,18 +31,18 @@
3231
import org.springframework.data.repository.query.parser.PartTree;
3332
import org.springframework.util.Assert;
3433

34+
import java.util.Collection;
3535
import java.util.Iterator;
36-
import java.util.List;
3736

3837
/**
3938
* Implementation of {@link AbstractQueryCreator} that creates {@link BindableQuery} from a {@link PartTree}.
4039
*
4140
* @author Roman Chigvintsev
4241
*/
43-
public class R2dbcQueryCreator extends AbstractQueryCreator<BindableQuery, Condition> {
42+
public class R2dbcQueryCreator extends AbstractQueryCreator<String, Condition> {
4443
private final PartTree tree;
45-
private final RelationalEntityMetadata<?> entityMetadata;
4644
private final ReactiveDataAccessStrategy dataAccessStrategy;
45+
private final RelationalEntityMetadata<?> entityMetadata;
4746
private final ConditionFactory conditionFactory;
4847

4948
/**
@@ -96,9 +95,9 @@ protected Condition or(Condition base, Condition condition) {
9695
* @return new instance of {@link BindableQuery}
9796
*/
9897
@Override
99-
protected BindableQuery complete(Condition condition, Sort sort) {
98+
protected String complete(Condition condition, Sort sort) {
10099
Table fromTable = Table.create(entityMetadata.getTableName());
101-
List<? extends Expression> selectExpressions = getSelectionExpressions(fromTable);
100+
Collection<? extends Expression> selectExpressions = getSelectionExpressions(fromTable);
102101
SelectFromAndJoin selectBuilder = StatementBuilder.select(selectExpressions).from(fromTable);
103102

104103
if (tree.isExistsProjection()) {
@@ -111,22 +110,10 @@ protected BindableQuery complete(Condition condition, Sort sort) {
111110

112111
RenderContext renderContext = dataAccessStrategy.getStatementMapper().getRenderContext();
113112
SqlRenderer sqlRenderer = renderContext == null ? SqlRenderer.create() : SqlRenderer.create(renderContext);
114-
String sql = sqlRenderer.render(selectBuilder.build());
115-
116-
return new BindableQuery() {
117-
@Override
118-
public <T extends DatabaseClient.BindSpec<T>> T bind(T bindSpec) {
119-
return bindSpec;
120-
}
121-
122-
@Override
123-
public String get() {
124-
return sql;
125-
}
126-
};
113+
return sqlRenderer.render(selectBuilder.build());
127114
}
128115

129-
private List<? extends Expression> getSelectionExpressions(Table fromTable) {
116+
private Collection<? extends Expression> getSelectionExpressions(Table fromTable) {
130117
if (tree.isExistsProjection()) {
131118
return fromTable.columns(dataAccessStrategy.getIdentifierColumns(entityMetadata.getJavaType()));
132119
}

0 commit comments

Comments
 (0)