Skip to content

Commit f96595b

Browse files
committed
Refactor RepositoryFactoryBeanSupport fragments setters.
Introduce RepositoryFragmentsFunction and a collection of functions to provide a well-formed contract. Use RepositoryMetadata from initialized RepositoryFactoryBean. See #3265
1 parent 644537a commit f96595b

32 files changed

+1296
-639
lines changed

Diff for: src/main/java/org/springframework/data/repository/aot/generate/AotCodeContributor.java

-25
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,320 @@
1+
/*
2+
* Copyright 2025. 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+
* http://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+
17+
/*
18+
* Copyright 2025 the original author or authors.
19+
*
20+
* Licensed under the Apache License, Version 2.0 (the "License");
21+
* you may not use this file except in compliance with the License.
22+
* You may obtain a copy of the License at
23+
*
24+
* http://www.apache.org/licenses/LICENSE-2.0
25+
*
26+
* Unless required by applicable law or agreed to in writing, software
27+
* distributed under the License is distributed on an "AS IS" BASIS,
28+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
29+
* See the License for the specific language governing permissions and
30+
* limitations under the License.
31+
*/
32+
package org.springframework.data.repository.aot.generate;
33+
34+
import java.lang.annotation.Annotation;
35+
import java.lang.reflect.Method;
36+
import java.util.ArrayList;
37+
import java.util.List;
38+
import java.util.Map.Entry;
39+
40+
import javax.lang.model.element.Modifier;
41+
42+
import org.jspecify.annotations.Nullable;
43+
44+
import org.springframework.core.ResolvableType;
45+
import org.springframework.core.annotation.MergedAnnotation;
46+
import org.springframework.core.annotation.MergedAnnotationSelectors;
47+
import org.springframework.core.annotation.MergedAnnotations;
48+
import org.springframework.data.repository.core.RepositoryInformation;
49+
import org.springframework.data.repository.query.Parameter;
50+
import org.springframework.data.repository.query.QueryMethod;
51+
import org.springframework.data.repository.query.ReturnedType;
52+
import org.springframework.javapoet.FieldSpec;
53+
import org.springframework.javapoet.ParameterSpec;
54+
import org.springframework.javapoet.TypeName;
55+
import org.springframework.util.ObjectUtils;
56+
57+
/**
58+
* Generational AOT context for repository query method generation.
59+
*
60+
* @author Christoph Strobl
61+
* @author Mark Paluch
62+
* @since 4.0
63+
*/
64+
public class AotQueryMethodGenerationContext {
65+
66+
private final Method method;
67+
private final MergedAnnotations annotations;
68+
private final QueryMethod queryMethod;
69+
private final RepositoryInformation repositoryInformation;
70+
private final AotRepositoryFragmentMetadata targetTypeMetadata;
71+
private final AotRepositoryMethodImplementationMetadata targetMethodMetadata;
72+
private final CodeBlocks codeBlocks;
73+
74+
AotQueryMethodGenerationContext(RepositoryInformation repositoryInformation, Method method, QueryMethod queryMethod,
75+
AotRepositoryFragmentMetadata targetTypeMetadata) {
76+
77+
this.method = method;
78+
this.annotations = MergedAnnotations.from(method);
79+
this.queryMethod = queryMethod;
80+
this.repositoryInformation = repositoryInformation;
81+
this.targetTypeMetadata = targetTypeMetadata;
82+
this.targetMethodMetadata = new AotRepositoryMethodImplementationMetadata(repositoryInformation, method);
83+
this.codeBlocks = new CodeBlocks(targetTypeMetadata);
84+
}
85+
86+
AotRepositoryFragmentMetadata getTargetTypeMetadata() {
87+
return targetTypeMetadata;
88+
}
89+
90+
AotRepositoryMethodImplementationMetadata getTargetMethodMetadata() {
91+
return targetMethodMetadata;
92+
}
93+
94+
public RepositoryInformation getRepositoryInformation() {
95+
return repositoryInformation;
96+
}
97+
98+
public Method getMethod() {
99+
return method;
100+
}
101+
102+
public CodeBlocks codeBlocks() {
103+
return codeBlocks;
104+
}
105+
106+
/**
107+
* @return the {@link MergedAnnotations} that are present on the method.
108+
*/
109+
public MergedAnnotations getAnnotations() {
110+
return annotations;
111+
}
112+
113+
/**
114+
* Get the {@linkplain MergedAnnotationSelectors#nearest() nearest} matching annotation or meta-annotation of the
115+
* specified type, or {@link MergedAnnotation#missing()} if none is present.
116+
*
117+
* @param annotationType the annotation type to get
118+
* @return a {@link MergedAnnotation} instance
119+
*/
120+
public <A extends Annotation> MergedAnnotation<A> getAnnotation(Class<A> annotationType) {
121+
return annotations.get(annotationType);
122+
}
123+
124+
/**
125+
* @return the returned type without considering dynamic projections.
126+
*/
127+
public ReturnedType getReturnedType() {
128+
return queryMethod.getResultProcessor().getReturnedType();
129+
}
130+
131+
public ResolvableType getActualReturnType() {
132+
return targetMethodMetadata.getActualReturnType();
133+
}
134+
135+
public ResolvableType getReturnType() {
136+
return targetMethodMetadata.getReturnType();
137+
}
138+
139+
/**
140+
* @return the {@link TypeName} representing the method return type.
141+
*/
142+
public TypeName getReturnTypeName() {
143+
return TypeName.get(getReturnType().getType());
144+
}
145+
146+
/**
147+
* Returns the required parameter name for the {@link Parameter#isBindable() bindable parameter} at the given
148+
* {@code parameterIndex} or throws {@link IllegalArgumentException} if the parameter cannot be determined by its
149+
* index.
150+
*
151+
* @param parameterIndex the zero-based parameter index as used in the query to reference bindable parameters.
152+
* @return the parameter name.
153+
*/
154+
public String getRequiredBindableParameterName(int parameterIndex) {
155+
156+
String name = getBindableParameterName(parameterIndex);
157+
158+
if (ObjectUtils.isEmpty(name)) {
159+
throw new IllegalArgumentException("No bindable parameter with index %d".formatted(parameterIndex));
160+
}
161+
162+
return name;
163+
}
164+
165+
/**
166+
* Returns the parameter name for the {@link Parameter#isBindable() bindable parameter} at the given
167+
* {@code parameterIndex} or {@code null} if the parameter cannot be determined by its index.
168+
*
169+
* @param parameterIndex the zero-based parameter index as used in the query to reference bindable parameters.
170+
* @return the parameter name.
171+
*/
172+
// TODO: Simplify?!
173+
public @Nullable String getBindableParameterName(int parameterIndex) {
174+
175+
int bindable = 0;
176+
int totalIndex = 0;
177+
for (Parameter parameter : queryMethod.getParameters()) {
178+
179+
if (parameter.isBindable()) {
180+
181+
if (bindable == parameterIndex) {
182+
return getParameterName(totalIndex);
183+
}
184+
bindable++;
185+
}
186+
187+
totalIndex++;
188+
}
189+
190+
return null;
191+
}
192+
193+
/**
194+
* Returns the required parameter name for the {@link Parameter#isBindable() bindable parameter} at the given
195+
* {@code parameterName} or throws {@link IllegalArgumentException} if the parameter cannot be determined by its
196+
* index.
197+
*
198+
* @param parameterName the parameter name as used in the query to reference bindable parameters.
199+
* @return the parameter name.
200+
*/
201+
public String getRequiredBindableParameterName(String parameterName) {
202+
203+
String name = getBindableParameterName(parameterName);
204+
205+
if (ObjectUtils.isEmpty(name)) {
206+
throw new IllegalArgumentException("No bindable parameter with name '%s'".formatted(parameterName));
207+
}
208+
209+
return name;
210+
}
211+
212+
/**
213+
* Returns the required parameter name for the {@link Parameter#isBindable() bindable parameter} at the given
214+
* {@code parameterName} or {@code null} if the parameter cannot be determined by its index.
215+
*
216+
* @param parameterName the parameter name as used in the query to reference bindable parameters.
217+
* @return the parameter name.
218+
*/
219+
// TODO: Simplify?!
220+
public @Nullable String getBindableParameterName(String parameterName) {
221+
222+
int totalIndex = 0;
223+
for (Parameter parameter : queryMethod.getParameters()) {
224+
225+
totalIndex++;
226+
if (!parameter.isBindable()) {
227+
continue;
228+
}
229+
230+
if (parameter.getName().filter(it -> it.equals(parameterName)).isPresent()) {
231+
return getParameterName(totalIndex - 1);
232+
}
233+
}
234+
235+
return null;
236+
}
237+
238+
/**
239+
* @return list of bindable parameter names.
240+
*/
241+
public List<String> getBindableParameterNames() {
242+
243+
List<String> result = new ArrayList<>();
244+
245+
for (Parameter parameter : queryMethod.getParameters().getBindableParameters()) {
246+
parameter.getName().map(result::add);
247+
}
248+
249+
return result;
250+
}
251+
252+
/**
253+
* @return list of all parameter names (including non-bindable special parameters).
254+
*/
255+
public List<String> getAllParameterNames() {
256+
257+
List<String> result = new ArrayList<>();
258+
259+
for (Parameter parameter : queryMethod.getParameters()) {
260+
parameter.getName().map(result::add);
261+
}
262+
263+
return result;
264+
}
265+
266+
public boolean hasField(String fieldName) {
267+
return targetTypeMetadata.hasField(fieldName);
268+
}
269+
270+
public void addField(String fieldName, TypeName type, Modifier... modifiers) {
271+
targetTypeMetadata.addField(fieldName, type, modifiers);
272+
}
273+
274+
public void addField(FieldSpec fieldSpec) {
275+
targetTypeMetadata.addField(fieldSpec);
276+
}
277+
278+
public @Nullable String fieldNameOf(Class<?> type) {
279+
return targetTypeMetadata.fieldNameOf(type);
280+
}
281+
282+
@Nullable
283+
public String getParameterNameOf(Class<?> type) {
284+
return targetMethodMetadata.getParameterNameOf(type);
285+
}
286+
287+
public @Nullable String getParameterName(int position) {
288+
289+
if (0 > position) {
290+
return null;
291+
}
292+
293+
List<Entry<String, ParameterSpec>> entries = new ArrayList<>(
294+
targetMethodMetadata.getMethodArguments().entrySet());
295+
if (position < entries.size()) {
296+
return entries.get(position).getKey();
297+
}
298+
return null;
299+
}
300+
301+
public void addParameter(ParameterSpec parameter) {
302+
this.targetMethodMetadata.addParameter(parameter);
303+
}
304+
305+
@Nullable
306+
public String getSortParameterName() {
307+
return getParameterName(queryMethod.getParameters().getSortIndex());
308+
}
309+
310+
@Nullable
311+
public String getPageableParameterName() {
312+
return getParameterName(queryMethod.getParameters().getPageableIndex());
313+
}
314+
315+
@Nullable
316+
public String getLimitParameterName() {
317+
return getParameterName(queryMethod.getParameters().getLimitIndex());
318+
}
319+
320+
}

0 commit comments

Comments
 (0)