Skip to content

Commit a4fcbb1

Browse files
committed
Adopt MongoParameters and MongoParameter to reflect the actual parameter type when using generics.
Closes #4579
1 parent 6b87492 commit a4fcbb1

File tree

4 files changed

+77
-46
lines changed

4 files changed

+77
-46
lines changed

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoParameters.java

+55-27
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import org.springframework.data.mongodb.repository.query.MongoParameters.MongoParameter;
3131
import org.springframework.data.repository.query.Parameter;
3232
import org.springframework.data.repository.query.Parameters;
33+
import org.springframework.data.repository.query.ParametersSource;
3334
import org.springframework.data.util.TypeInformation;
3435
import org.springframework.lang.Nullable;
3536

@@ -49,38 +50,48 @@ public class MongoParameters extends Parameters<MongoParameters, MongoParameter>
4950
private final @Nullable Integer nearIndex;
5051
private final @Nullable Integer collationIndex;
5152
private final int updateIndex;
53+
private final TypeInformation<?> domainType;
5254

5355
/**
5456
* Creates a new {@link MongoParameters} instance from the given {@link Method} and {@link MongoQueryMethod}.
5557
*
56-
* @param method must not be {@literal null}.
58+
* @param parametersSource must not be {@literal null}.
5759
* @param isGeoNearMethod indicate if this is a geo spatial query method
5860
*/
59-
public MongoParameters(Method method, boolean isGeoNearMethod) {
61+
public MongoParameters(ParametersSource parametersSource, boolean isGeoNearMethod) {
62+
this(parametersSource, new NearIndex(parametersSource, isGeoNearMethod));
63+
}
64+
65+
/**
66+
* Creates a new {@link MongoParameters} instance from the given {@link Method} and {@link MongoQueryMethod}.
67+
*
68+
* @param parametersSource must not be {@literal null}.
69+
* @param nearIndex the near parameter index.
70+
*/
71+
private MongoParameters(ParametersSource parametersSource, NearIndex nearIndex) {
6072

61-
super(method);
73+
super(parametersSource, methodParameter -> new MongoParameter(methodParameter,
74+
parametersSource.getDomainTypeInformation(), nearIndex.nearIndex));
75+
76+
Method method = parametersSource.getMethod();
6277
List<Class<?>> parameterTypes = Arrays.asList(method.getParameterTypes());
6378

79+
this.domainType = parametersSource.getDomainTypeInformation();
6480
this.fullTextIndex = parameterTypes.indexOf(TextCriteria.class);
6581

66-
TypeInformation<?> declaringClassInfo = TypeInformation.of(method.getDeclaringClass());
82+
TypeInformation<?> declaringClassInfo = TypeInformation.of(parametersSource.getContainingClass());
6783
List<TypeInformation<?>> parameterTypeInfo = declaringClassInfo.getParameterTypes(method);
6884

6985
this.rangeIndex = getTypeIndex(parameterTypeInfo, Range.class, Distance.class);
7086
this.maxDistanceIndex = this.rangeIndex == -1 ? getTypeIndex(parameterTypeInfo, Distance.class, null) : -1;
7187
this.collationIndex = getTypeIndex(parameterTypeInfo, Collation.class, null);
7288
this.updateIndex = QueryUtils.indexOfAssignableParameter(UpdateDefinition.class, parameterTypes);
73-
74-
int index = findNearIndexInParameters(method);
75-
if (index == -1 && isGeoNearMethod) {
76-
index = getNearIndex(parameterTypes);
77-
}
78-
79-
this.nearIndex = index;
89+
this.nearIndex = nearIndex.nearIndex;
8090
}
8191

8292
private MongoParameters(List<MongoParameter> parameters, int maxDistanceIndex, @Nullable Integer nearIndex,
83-
@Nullable Integer fullTextIndex, int rangeIndex, @Nullable Integer collationIndex, int updateIndex) {
93+
@Nullable Integer fullTextIndex, int rangeIndex, @Nullable Integer collationIndex, int updateIndex,
94+
TypeInformation<?> domainType) {
8495

8596
super(parameters);
8697

@@ -90,9 +101,25 @@ private MongoParameters(List<MongoParameter> parameters, int maxDistanceIndex, @
90101
this.rangeIndex = rangeIndex;
91102
this.collationIndex = collationIndex;
92103
this.updateIndex = updateIndex;
104+
this.domainType = domainType;
93105
}
94106

95-
private int getNearIndex(List<Class<?>> parameterTypes) {
107+
static class NearIndex {
108+
109+
private final @Nullable Integer nearIndex;
110+
111+
public NearIndex(ParametersSource parametersSource, boolean isGeoNearMethod) {
112+
113+
int index = findNearIndexInParameters(parametersSource.getMethod());
114+
if (index == -1 && isGeoNearMethod) {
115+
index = getNearIndex(Arrays.asList(parametersSource.getMethod().getParameterTypes()));
116+
}
117+
118+
this.nearIndex = index;
119+
}
120+
}
121+
122+
private static int getNearIndex(List<Class<?>> parameterTypes) {
96123

97124
for (Class<?> reference : Arrays.asList(Point.class, double[].class)) {
98125

@@ -112,15 +139,18 @@ private int getNearIndex(List<Class<?>> parameterTypes) {
112139
return -1;
113140
}
114141

115-
private int findNearIndexInParameters(Method method) {
142+
static int findNearIndexInParameters(Method method) {
116143

117144
int index = -1;
118145
for (java.lang.reflect.Parameter p : method.getParameters()) {
119146

120-
MongoParameter param = createParameter(MethodParameter.forParameter(p));
121-
if (param.isManuallyAnnotatedNearParameter()) {
147+
MethodParameter methodParameter = MethodParameter.forParameter(p);
148+
149+
if ((Point.class.isAssignableFrom(methodParameter.getParameterType())
150+
|| methodParameter.getParameterType().equals(double[].class))
151+
&& methodParameter.hasParameterAnnotation(Near.class)) {
122152
if (index == -1) {
123-
index = param.getIndex();
153+
index = methodParameter.getParameterIndex();
124154
} else {
125155
throw new IllegalStateException(
126156
String.format("Found multiple @Near annotations ond method %s; Only one allowed", method));
@@ -131,11 +161,6 @@ private int findNearIndexInParameters(Method method) {
131161
return index;
132162
}
133163

134-
@Override
135-
protected MongoParameter createParameter(MethodParameter parameter) {
136-
return new MongoParameter(parameter);
137-
}
138-
139164
public int getDistanceRangeIndex() {
140165
return -1;
141166
}
@@ -197,6 +222,7 @@ public int getCollationParameterIndex() {
197222

198223
/**
199224
* Returns the index of the {@link UpdateDefinition} parameter or -1 if not present.
225+
*
200226
* @return -1 if not present.
201227
* @since 3.4
202228
*/
@@ -207,7 +233,7 @@ public int getUpdateIndex() {
207233
@Override
208234
protected MongoParameters createFrom(List<MongoParameter> parameters) {
209235
return new MongoParameters(parameters, this.maxDistanceIndex, this.nearIndex, this.fullTextIndex, this.rangeIndex,
210-
this.collationIndex, this.updateIndex);
236+
this.collationIndex, this.updateIndex, this.domainType);
211237
}
212238

213239
private int getTypeIndex(List<TypeInformation<?>> parameterTypes, Class<?> type, @Nullable Class<?> componentType) {
@@ -234,18 +260,21 @@ private int getTypeIndex(List<TypeInformation<?>> parameterTypes, Class<?> type,
234260
*
235261
* @author Oliver Gierke
236262
*/
237-
class MongoParameter extends Parameter {
263+
static class MongoParameter extends Parameter {
238264

239265
private final MethodParameter parameter;
266+
private final @Nullable Integer nearIndex;
240267

241268
/**
242269
* Creates a new {@link MongoParameter}.
243270
*
244271
* @param parameter must not be {@literal null}.
272+
* @param domainType must not be {@literal null}.
245273
*/
246-
MongoParameter(MethodParameter parameter) {
247-
super(parameter);
274+
MongoParameter(MethodParameter parameter, TypeInformation<?> domainType, @Nullable Integer nearIndex) {
275+
super(parameter, domainType);
248276
this.parameter = parameter;
277+
this.nearIndex = nearIndex;
249278

250279
if (!isPoint() && hasNearAnnotation()) {
251280
throw new IllegalArgumentException("Near annotation is only allowed at Point parameter");
@@ -259,7 +288,6 @@ public boolean isSpecialParameter() {
259288
}
260289

261290
private boolean isNearParameter() {
262-
Integer nearIndex = MongoParameters.this.nearIndex;
263291
return nearIndex != null && nearIndex.equals(getIndex());
264292
}
265293

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryMethod.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import org.springframework.data.mongodb.util.BsonUtils;
4343
import org.springframework.data.projection.ProjectionFactory;
4444
import org.springframework.data.repository.core.RepositoryMetadata;
45+
import org.springframework.data.repository.query.ParametersSource;
4546
import org.springframework.data.repository.query.QueryMethod;
4647
import org.springframework.data.util.Lazy;
4748
import org.springframework.data.util.ReactiveWrappers;
@@ -94,8 +95,8 @@ public MongoQueryMethod(Method method, RepositoryMetadata metadata, ProjectionFa
9495
}
9596

9697
@Override
97-
protected MongoParameters createParameters(Method method) {
98-
return new MongoParameters(method, isGeoNearQuery(method));
98+
protected MongoParameters createParameters(ParametersSource parametersSource) {
99+
return new MongoParameters(parametersSource, isGeoNearQuery(parametersSource.getMethod()));
99100
}
100101

101102
/**

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveMongoQueryMethod.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.springframework.data.mongodb.repository.query.MongoParameters.MongoParameter;
3232
import org.springframework.data.projection.ProjectionFactory;
3333
import org.springframework.data.repository.core.RepositoryMetadata;
34+
import org.springframework.data.repository.query.ParametersSource;
3435
import org.springframework.data.repository.util.ReactiveWrapperConverters;
3536
import org.springframework.data.util.Lazy;
3637
import org.springframework.data.util.ReactiveWrappers;
@@ -71,8 +72,8 @@ public ReactiveMongoQueryMethod(Method method, RepositoryMetadata metadata, Proj
7172
}
7273

7374
@Override
74-
protected MongoParameters createParameters(Method method) {
75-
return new MongoParameters(method, isGeoNearQuery(method));
75+
protected MongoParameters createParameters(ParametersSource parametersSource) {
76+
return new MongoParameters(parametersSource, isGeoNearQuery(parametersSource.getMethod()));
7677
}
7778

7879
@Override

spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoParametersUnitTests.java

+16-15
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import org.springframework.data.mongodb.repository.Near;
3737
import org.springframework.data.mongodb.repository.Person;
3838
import org.springframework.data.repository.query.Parameter;
39+
import org.springframework.data.repository.query.ParametersSource;
3940

4041
/**
4142
* Unit tests for {@link MongoParameters}.
@@ -52,7 +53,7 @@ class MongoParametersUnitTests {
5253
void discoversDistanceParameter() throws NoSuchMethodException, SecurityException {
5354

5455
Method method = PersonRepository.class.getMethod("findByLocationNear", Point.class, Distance.class);
55-
MongoParameters parameters = new MongoParameters(method, false);
56+
MongoParameters parameters = new MongoParameters(ParametersSource.of(method), false);
5657

5758
assertThat(parameters.getNumberOfParameters()).isEqualTo(2);
5859
assertThat(parameters.getMaxDistanceIndex()).isEqualTo(1);
@@ -67,7 +68,7 @@ void discoversDistanceParameter() throws NoSuchMethodException, SecurityExceptio
6768
@Test
6869
void doesNotConsiderPointAsNearForSimpleQuery() throws Exception {
6970
Method method = PersonRepository.class.getMethod("findByLocationNear", Point.class, Distance.class);
70-
MongoParameters parameters = new MongoParameters(method, false);
71+
MongoParameters parameters = new MongoParameters(ParametersSource.of(method), false);
7172

7273
assertThat(parameters.getNearIndex()).isEqualTo(-1);
7374
}
@@ -77,61 +78,61 @@ void rejectsMultiplePointsForGeoNearMethod() throws Exception {
7778

7879
Method method = PersonRepository.class.getMethod("findByLocationNearAndOtherLocation", Point.class, Point.class);
7980

80-
assertThatIllegalStateException().isThrownBy(() -> new MongoParameters(method, true));
81+
assertThatIllegalStateException().isThrownBy(() -> new MongoParameters(ParametersSource.of(method), true));
8182
}
8283

8384
@Test
8485
void rejectsMultipleDoubleArraysForGeoNearMethod() throws Exception {
8586

8687
Method method = PersonRepository.class.getMethod("invalidDoubleArrays", double[].class, double[].class);
8788

88-
assertThatIllegalStateException().isThrownBy(() -> new MongoParameters(method, true));
89+
assertThatIllegalStateException().isThrownBy(() -> new MongoParameters(ParametersSource.of(method), true));
8990
}
9091

9192
@Test
9293
void doesNotRejectMultiplePointsForSimpleQueryMethod() throws Exception {
9394

9495
Method method = PersonRepository.class.getMethod("someOtherMethod", Point.class, Point.class);
95-
new MongoParameters(method, false);
96+
new MongoParameters(ParametersSource.of(method), false);
9697
}
9798

9899
@Test
99100
void findsAnnotatedPointForGeoNearQuery() throws Exception {
100101

101102
Method method = PersonRepository.class.getMethod("findByOtherLocationAndLocationNear", Point.class, Point.class);
102-
MongoParameters parameters = new MongoParameters(method, true);
103+
MongoParameters parameters = new MongoParameters(ParametersSource.of(method), true);
103104
assertThat(parameters.getNearIndex()).isOne();
104105
}
105106

106107
@Test
107108
void findsAnnotatedDoubleArrayForGeoNearQuery() throws Exception {
108109

109110
Method method = PersonRepository.class.getMethod("validDoubleArrays", double[].class, double[].class);
110-
MongoParameters parameters = new MongoParameters(method, true);
111+
MongoParameters parameters = new MongoParameters(ParametersSource.of(method), true);
111112
assertThat(parameters.getNearIndex()).isOne();
112113
}
113114

114115
@Test // DATAMONGO-973
115116
void shouldFindTextCriteriaAtItsIndex() throws SecurityException, NoSuchMethodException {
116117

117118
Method method = PersonRepository.class.getMethod("findByNameAndText", String.class, TextCriteria.class);
118-
MongoParameters parameters = new MongoParameters(method, false);
119+
MongoParameters parameters = new MongoParameters(ParametersSource.of(method), false);
119120
assertThat(parameters.getFullTextParameterIndex()).isOne();
120121
}
121122

122123
@Test // DATAMONGO-973
123124
void shouldTreatTextCriteriaParameterAsSpecialParameter() throws SecurityException, NoSuchMethodException {
124125

125126
Method method = PersonRepository.class.getMethod("findByNameAndText", String.class, TextCriteria.class);
126-
MongoParameters parameters = new MongoParameters(method, false);
127+
MongoParameters parameters = new MongoParameters(ParametersSource.of(method), false);
127128
assertThat(parameters.getParameter(parameters.getFullTextParameterIndex()).isSpecialParameter()).isTrue();
128129
}
129130

130131
@Test // DATAMONGO-1110
131132
void shouldFindMinAndMaxDistanceParameters() throws NoSuchMethodException, SecurityException {
132133

133134
Method method = PersonRepository.class.getMethod("findByLocationNear", Point.class, Range.class);
134-
MongoParameters parameters = new MongoParameters(method, false);
135+
MongoParameters parameters = new MongoParameters(ParametersSource.of(method), false);
135136

136137
assertThat(parameters.getRangeIndex()).isOne();
137138
assertThat(parameters.getMaxDistanceIndex()).isEqualTo(-1);
@@ -141,7 +142,7 @@ void shouldFindMinAndMaxDistanceParameters() throws NoSuchMethodException, Secur
141142
void shouldNotHaveMinDistanceIfOnlyOneDistanceParameterPresent() throws NoSuchMethodException, SecurityException {
142143

143144
Method method = PersonRepository.class.getMethod("findByLocationNear", Point.class, Distance.class);
144-
MongoParameters parameters = new MongoParameters(method, false);
145+
MongoParameters parameters = new MongoParameters(ParametersSource.of(method), false);
145146

146147
assertThat(parameters.getRangeIndex()).isEqualTo(-1);
147148
assertThat(parameters.getMaxDistanceIndex()).isOne();
@@ -151,7 +152,7 @@ void shouldNotHaveMinDistanceIfOnlyOneDistanceParameterPresent() throws NoSuchMe
151152
void shouldReturnMinusOneIfCollationParameterDoesNotExist() throws NoSuchMethodException, SecurityException {
152153

153154
Method method = PersonRepository.class.getMethod("findByLocationNear", Point.class, Distance.class);
154-
MongoParameters parameters = new MongoParameters(method, false);
155+
MongoParameters parameters = new MongoParameters(ParametersSource.of(method), false);
155156

156157
assertThat(parameters.getCollationParameterIndex()).isEqualTo(-1);
157158
}
@@ -160,7 +161,7 @@ void shouldReturnMinusOneIfCollationParameterDoesNotExist() throws NoSuchMethodE
160161
void shouldReturnIndexOfCollationParameterIfExists() throws NoSuchMethodException, SecurityException {
161162

162163
Method method = PersonRepository.class.getMethod("findByText", String.class, Collation.class);
163-
MongoParameters parameters = new MongoParameters(method, false);
164+
MongoParameters parameters = new MongoParameters(ParametersSource.of(method), false);
164165

165166
assertThat(parameters.getCollationParameterIndex()).isOne();
166167
}
@@ -169,7 +170,7 @@ void shouldReturnIndexOfCollationParameterIfExists() throws NoSuchMethodExceptio
169170
void shouldReturnIndexUpdateIfExists() throws NoSuchMethodException, SecurityException {
170171

171172
Method method = PersonRepository.class.getMethod("findAndModifyByFirstname", String.class, UpdateDefinition.class, Pageable.class);
172-
MongoParameters parameters = new MongoParameters(method, false);
173+
MongoParameters parameters = new MongoParameters(ParametersSource.of(method), false);
173174

174175
assertThat(parameters.getUpdateIndex()).isOne();
175176
}
@@ -178,7 +179,7 @@ void shouldReturnIndexUpdateIfExists() throws NoSuchMethodException, SecurityExc
178179
void shouldReturnInvalidIndexIfUpdateDoesNotExist() throws NoSuchMethodException, SecurityException {
179180

180181
Method method = PersonRepository.class.getMethod("someOtherMethod", Point.class, Point.class);
181-
MongoParameters parameters = new MongoParameters(method, false);
182+
MongoParameters parameters = new MongoParameters(ParametersSource.of(method), false);
182183

183184
assertThat(parameters.getUpdateIndex()).isEqualTo(-1);
184185
}

0 commit comments

Comments
 (0)