Skip to content

Commit a58003b

Browse files
committed
spring-projects#282 - Create standalone implementation of 'BindableQuery' for 'PartTreeR2dbcQuery'
1 parent d3017d1 commit a58003b

File tree

4 files changed

+242
-48
lines changed

4 files changed

+242
-48
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/*
2+
* Copyright 2018-2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.r2dbc.repository.query;
17+
18+
import org.springframework.data.r2dbc.core.DatabaseClient;
19+
import org.springframework.data.relational.repository.query.RelationalParameterAccessor;
20+
import org.springframework.data.repository.query.parser.PartTree;
21+
import org.springframework.util.Assert;
22+
23+
/**
24+
* A {@link BindableQuery} implementation based on a {@link PartTree}.
25+
*
26+
* @author Roman Chigvintsev
27+
* @see PartTreeR2dbcQuery
28+
*/
29+
class PartTreeBindableQuery implements BindableQuery {
30+
private final String sql;
31+
private final RelationalParameterAccessor accessor;
32+
private final ParameterMetadataProvider parameterMetadataProvider;
33+
34+
/**
35+
* Creates new instance of this class with the given SQL query, {@link RelationalParameterAccessor} and
36+
* {@link ParameterMetadataProvider}.
37+
*
38+
* @param sql SQL query (must not be {@literal null} or blank)
39+
* @param accessor query parameter accessor (must not be {@literal null})
40+
* @param parameterMetadataProvider parameter metadata provider (must not be {@literal null})
41+
*/
42+
PartTreeBindableQuery(String sql,
43+
RelationalParameterAccessor accessor,
44+
ParameterMetadataProvider parameterMetadataProvider) {
45+
Assert.hasText(sql, "SQL query must not be null or blank!");
46+
Assert.notNull(accessor, "Query parameter accessor must not be null!");
47+
Assert.notNull(parameterMetadataProvider, "Parameter metadata provider must not be null!");
48+
49+
this.sql = sql;
50+
this.accessor = accessor;
51+
this.parameterMetadataProvider = parameterMetadataProvider;
52+
}
53+
54+
@Override
55+
public <T extends DatabaseClient.BindSpec<T>> T bind(T bindSpec) {
56+
T bindSpecToUse = bindSpec;
57+
58+
int index = 0;
59+
int bindingIndex = 0;
60+
61+
for (Object value : accessor.getValues()) {
62+
ParameterMetadata metadata = parameterMetadataProvider.getParameterMetadata(index++);
63+
String parameterName = metadata.getName();
64+
Class<?> parameterType = metadata.getType();
65+
66+
if (parameterName != null) {
67+
if (value == null) {
68+
checkNullIsAllowed(metadata, "Value of parameter with name %s must not be null!",
69+
parameterName);
70+
bindSpecToUse = bindSpecToUse.bindNull(parameterName, parameterType);
71+
} else {
72+
bindSpecToUse = bindSpecToUse.bind(parameterName, metadata.prepare(value));
73+
}
74+
} else {
75+
if (value == null) {
76+
checkNullIsAllowed(metadata, "Value of parameter with index %d must not be null!",
77+
bindingIndex);
78+
bindSpecToUse = bindSpecToUse.bindNull(bindingIndex++, parameterType);
79+
} else {
80+
bindSpecToUse = bindSpecToUse.bind(bindingIndex++, metadata.prepare(value));
81+
}
82+
}
83+
}
84+
85+
return bindSpecToUse;
86+
}
87+
88+
@Override
89+
public String get() {
90+
return sql;
91+
}
92+
93+
private void checkNullIsAllowed(ParameterMetadata metadata, String errorMessage, Object messageArgument) {
94+
if (!metadata.isIsNullParameter()) {
95+
String message = String.format(errorMessage, messageArgument);
96+
throw new IllegalArgumentException(message);
97+
}
98+
}
99+
}

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

+1-48
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import org.springframework.data.domain.Sort;
2020
import org.springframework.data.r2dbc.convert.R2dbcConverter;
2121
import org.springframework.data.r2dbc.core.DatabaseClient;
22-
import org.springframework.data.r2dbc.core.DatabaseClient.BindSpec;
2322
import org.springframework.data.r2dbc.core.ReactiveDataAccessStrategy;
2423
import org.springframework.data.relational.repository.query.RelationalEntityMetadata;
2524
import org.springframework.data.relational.repository.query.RelationalParameterAccessor;
@@ -83,53 +82,7 @@ protected BindableQuery createQuery(RelationalParameterAccessor accessor) {
8382
R2dbcQueryCreator queryCreator = new R2dbcQueryCreator(tree, dataAccessStrategy, entityMetadata,
8483
parameterMetadataProvider);
8584
String sql = queryCreator.createQuery(getDynamicSort(accessor));
86-
return new BindableQuery() {
87-
@Override
88-
public <T extends BindSpec<T>> T bind(T bindSpec) {
89-
T bindSpecToUse = bindSpec;
90-
91-
int index = 0;
92-
int bindingIndex = 0;
93-
94-
for (Object value : accessor.getValues()) {
95-
ParameterMetadata metadata = parameterMetadataProvider.getParameterMetadata(index++);
96-
String parameterName = metadata.getName();
97-
Class<?> parameterType = metadata.getType();
98-
99-
if (parameterName != null) {
100-
if (value == null) {
101-
checkNullIsAllowed(metadata, "Value of parameter with name %s must not be null!",
102-
parameterName);
103-
bindSpecToUse = bindSpecToUse.bindNull(parameterName, parameterType);
104-
} else {
105-
bindSpecToUse = bindSpecToUse.bind(parameterName, metadata.prepare(value));
106-
}
107-
} else {
108-
if (value == null) {
109-
checkNullIsAllowed(metadata, "Value of parameter with index %d must not be null!",
110-
bindingIndex);
111-
bindSpecToUse = bindSpecToUse.bindNull(bindingIndex++, parameterType);
112-
} else {
113-
bindSpecToUse = bindSpecToUse.bind(bindingIndex++, metadata.prepare(value));
114-
}
115-
}
116-
}
117-
118-
return bindSpecToUse;
119-
}
120-
121-
@Override
122-
public String get() {
123-
return sql;
124-
}
125-
126-
private void checkNullIsAllowed(ParameterMetadata metadata, String errorMessage, Object messageArgument) {
127-
if (!metadata.isIsNullParameter()) {
128-
String message = String.format(errorMessage, messageArgument);
129-
throw new IllegalArgumentException(message);
130-
}
131-
}
132-
};
85+
return new PartTreeBindableQuery(sql, accessor, parameterMetadataProvider);
13386
}
13487

13588
private Sort getDynamicSort(RelationalParameterAccessor accessor) {

Diff for: src/test/java/org/springframework/data/r2dbc/repository/query/LikeEscaperTest.java

+15
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,18 @@
1+
/*
2+
* Copyright 2018-2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
116
package org.springframework.data.r2dbc.repository.query;
217

318
import org.junit.Test;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
/*
2+
* Copyright 2018-2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.r2dbc.repository.query;
17+
18+
import org.junit.Before;
19+
import org.junit.Rule;
20+
import org.junit.Test;
21+
import org.junit.rules.ExpectedException;
22+
import org.junit.runner.RunWith;
23+
import org.mockito.Mock;
24+
import org.mockito.junit.MockitoJUnitRunner;
25+
import org.springframework.data.r2dbc.core.DatabaseClient;
26+
import org.springframework.data.relational.repository.query.RelationalParameterAccessor;
27+
28+
import static org.mockito.ArgumentMatchers.anyInt;
29+
import static org.mockito.Mockito.*;
30+
31+
/**
32+
* @author Roman Chigvintsev
33+
*/
34+
@RunWith(MockitoJUnitRunner.class)
35+
public class PartTreeBindableQueryTest {
36+
@Mock
37+
private RelationalParameterAccessor parameterAccessor;
38+
@Mock
39+
private ParameterMetadataProvider parameterMetadataProvider;
40+
@Mock
41+
private ParameterMetadata parameterMetadata;
42+
43+
@Rule
44+
public ExpectedException thrown = ExpectedException.none();
45+
46+
@SuppressWarnings("ResultOfMethodCallIgnored")
47+
@Before
48+
public void setUp() {
49+
doReturn(String.class).when(parameterMetadata).getType();
50+
when(parameterMetadata.prepare(any())).thenCallRealMethod();
51+
when(parameterMetadataProvider.getParameterMetadata(anyInt())).thenReturn(parameterMetadata);
52+
}
53+
54+
@SuppressWarnings({"rawtypes", "unchecked"})
55+
@Test
56+
public void bindsNonNullValueToQueryParameterByIndex() {
57+
when(parameterAccessor.getValues()).thenReturn(new Object[]{"test"});
58+
DatabaseClient.BindSpec bindSpecMock = mock(DatabaseClient.BindSpec.class);
59+
PartTreeBindableQuery query = new PartTreeBindableQuery("SELECT", parameterAccessor, parameterMetadataProvider);
60+
query.bind(bindSpecMock);
61+
verify(bindSpecMock, times(1)).bind(0, "test");
62+
}
63+
64+
@SuppressWarnings({"rawtypes", "unchecked"})
65+
@Test
66+
public void bindsNonNullValueToQueryParameterByName() {
67+
when(parameterAccessor.getValues()).thenReturn(new Object[]{"test"});
68+
when(parameterMetadata.getName()).thenReturn("param0");
69+
DatabaseClient.BindSpec bindSpecMock = mock(DatabaseClient.BindSpec.class);
70+
71+
PartTreeBindableQuery query = new PartTreeBindableQuery("SELECT", parameterAccessor, parameterMetadataProvider);
72+
query.bind(bindSpecMock);
73+
verify(bindSpecMock, times(1)).bind("param0", "test");
74+
}
75+
76+
@SuppressWarnings({"rawtypes", "unchecked"})
77+
@Test
78+
public void bindsNullValueToQueryParameterByIndex() {
79+
when(parameterAccessor.getValues()).thenReturn(new Object[1]);
80+
when(parameterMetadata.isIsNullParameter()).thenReturn(true);
81+
DatabaseClient.BindSpec bindSpecMock = mock(DatabaseClient.BindSpec.class);
82+
83+
PartTreeBindableQuery query = new PartTreeBindableQuery("SELECT", parameterAccessor, parameterMetadataProvider);
84+
query.bind(bindSpecMock);
85+
verify(bindSpecMock, times(1)).bindNull(0, String.class);
86+
}
87+
88+
@SuppressWarnings({"rawtypes", "unchecked"})
89+
@Test
90+
public void bindsNullValueToQueryParameterByName() {
91+
when(parameterAccessor.getValues()).thenReturn(new Object[1]);
92+
when(parameterMetadata.getName()).thenReturn("param0");
93+
when(parameterMetadata.isIsNullParameter()).thenReturn(true);
94+
DatabaseClient.BindSpec bindSpecMock = mock(DatabaseClient.BindSpec.class);
95+
96+
PartTreeBindableQuery query = new PartTreeBindableQuery("SELECT", parameterAccessor, parameterMetadataProvider);
97+
query.bind(bindSpecMock);
98+
verify(bindSpecMock, times(1)).bindNull("param0", String.class);
99+
}
100+
101+
@SuppressWarnings({"rawtypes", "unchecked"})
102+
@Test
103+
public void throwsExceptionWhenNullIsNotAllowedAsIndexedQueryParameter() {
104+
thrown.expect(IllegalArgumentException.class);
105+
thrown.expectMessage("Value of parameter with index 0 must not be null!");
106+
107+
when(parameterAccessor.getValues()).thenReturn(new Object[1]);
108+
DatabaseClient.BindSpec bindSpecMock = mock(DatabaseClient.BindSpec.class);
109+
110+
PartTreeBindableQuery query = new PartTreeBindableQuery("SELECT", parameterAccessor, parameterMetadataProvider);
111+
query.bind(bindSpecMock);
112+
}
113+
114+
@SuppressWarnings({"rawtypes", "unchecked"})
115+
@Test
116+
public void throwsExceptionWhenNullIsNotAllowedAsNamedQueryParameter() {
117+
thrown.expect(IllegalArgumentException.class);
118+
thrown.expectMessage("Value of parameter with name param0 must not be null!");
119+
120+
when(parameterAccessor.getValues()).thenReturn(new Object[1]);
121+
when(parameterMetadata.getName()).thenReturn("param0");
122+
DatabaseClient.BindSpec bindSpecMock = mock(DatabaseClient.BindSpec.class);
123+
124+
PartTreeBindableQuery query = new PartTreeBindableQuery("SELECT", parameterAccessor, parameterMetadataProvider);
125+
query.bind(bindSpecMock);
126+
}
127+
}

0 commit comments

Comments
 (0)