19
19
import jakarta .persistence .EntityManagerFactory ;
20
20
21
21
import java .lang .reflect .Method ;
22
+ import java .util .Map ;
22
23
23
24
import org .jspecify .annotations .Nullable ;
24
25
26
+ import org .springframework .core .annotation .AnnotatedElementUtils ;
25
27
import org .springframework .core .annotation .MergedAnnotation ;
28
+ import org .springframework .core .annotation .MergedAnnotations ;
26
29
import org .springframework .data .jpa .provider .PersistenceProvider ;
27
30
import org .springframework .data .jpa .repository .EntityGraph ;
28
31
import org .springframework .data .jpa .repository .Modifying ;
31
34
import org .springframework .data .jpa .repository .QueryHints ;
32
35
import org .springframework .data .jpa .repository .query .JpaParameters ;
33
36
import org .springframework .data .jpa .repository .query .JpaQueryMethod ;
37
+ import org .springframework .data .jpa .repository .query .Procedure ;
34
38
import org .springframework .data .jpa .repository .query .QueryEnhancerSelector ;
35
39
import org .springframework .data .repository .aot .generate .AotRepositoryConstructorBuilder ;
36
40
import org .springframework .data .repository .aot .generate .AotRepositoryFragmentMetadata ;
37
41
import org .springframework .data .repository .aot .generate .MethodContributor ;
42
+ import org .springframework .data .repository .aot .generate .QueryMetadata ;
38
43
import org .springframework .data .repository .aot .generate .RepositoryContributor ;
39
44
import org .springframework .data .repository .config .AotRepositoryContext ;
40
45
import org .springframework .data .repository .core .RepositoryInformation ;
46
51
import org .springframework .javapoet .TypeName ;
47
52
import org .springframework .javapoet .TypeSpec ;
48
53
import org .springframework .util .ClassUtils ;
54
+ import org .springframework .util .StringUtils ;
49
55
50
56
/**
51
57
* JPA-specific {@link RepositoryContributor} contributing an AOT repository fragment using the {@link EntityManager}
@@ -113,20 +119,50 @@ protected void customizeConstructor(AotRepositoryConstructorBuilder constructorB
113
119
114
120
// no stored procedures for now.
115
121
if (queryMethod .isProcedureQuery ()) {
122
+
123
+ Procedure procedure = AnnotatedElementUtils .findMergedAnnotation (method , Procedure .class );
124
+
125
+ MethodContributor .QueryMethodMetadataContributorBuilder <JpaQueryMethod > builder = MethodContributor
126
+ .forQueryMethod (queryMethod );
127
+
128
+
129
+ if (procedure != null ) {
130
+
131
+ if (StringUtils .hasText (procedure .name ())) {
132
+ return builder .metadataOnly (new NamedStoredProcedureMetadata (procedure .name ()));
133
+ }
134
+
135
+ if (StringUtils .hasText (procedure .procedureName ())) {
136
+ return builder .metadataOnly (new StoredProcedureMetadata (procedure .procedureName ()));
137
+ }
138
+
139
+ if (StringUtils .hasText (procedure .value ())) {
140
+ return builder .metadataOnly (new StoredProcedureMetadata (procedure .value ()));
141
+ }
142
+ }
143
+
144
+ // TODO: Better fallback.
116
145
return null ;
117
146
}
118
147
119
148
ReturnedType returnedType = queryMethod .getResultProcessor ().getReturnedType ();
120
149
JpaParameters parameters = queryMethod .getParameters ();
121
150
151
+ MergedAnnotation <Query > query = MergedAnnotations .from (method ).get (Query .class );
152
+
153
+ AotQueries aotQueries = queriesFactory .createQueries (repositoryInformation , query , selector , queryMethod ,
154
+ returnedType );
155
+
122
156
// no KeysetScrolling for now.
123
157
if (parameters .hasScrollPositionParameter ()) {
124
- return null ;
158
+ return MethodContributor .forQueryMethod (queryMethod )
159
+ .metadataOnly (aotQueries .toMetadata (queryMethod .isPageQuery ()));
125
160
}
126
161
127
162
// no dynamic projections.
128
163
if (parameters .hasDynamicProjection ()) {
129
- return null ;
164
+ return MethodContributor .forQueryMethod (queryMethod )
165
+ .metadataOnly (aotQueries .toMetadata (queryMethod .isPageQuery ()));
130
166
}
131
167
132
168
if (queryMethod .isModifyingQuery ()) {
@@ -138,23 +174,23 @@ protected void customizeConstructor(AotRepositoryConstructorBuilder constructorB
138
174
boolean isVoid = ClassUtils .isVoidType (returnType .getType ());
139
175
140
176
if (!returnsCount && !isVoid ) {
141
- return null ;
177
+ return MethodContributor .forQueryMethod (queryMethod )
178
+ .metadataOnly (aotQueries .toMetadata (queryMethod .isPageQuery ()));
142
179
}
143
180
}
144
181
145
- return MethodContributor .forQueryMethod (queryMethod ).contribute (context -> {
182
+ return MethodContributor .forQueryMethod (queryMethod ).withMetadata (aotQueries .toMetadata (queryMethod .isPageQuery ()))
183
+ .contribute (context -> {
146
184
147
185
CodeBlock .Builder body = CodeBlock .builder ();
148
186
149
- MergedAnnotation <Query > query = context .getAnnotation (Query .class );
150
187
MergedAnnotation <NativeQuery > nativeQuery = context .getAnnotation (NativeQuery .class );
151
188
MergedAnnotation <QueryHints > queryHints = context .getAnnotation (QueryHints .class );
152
189
MergedAnnotation <EntityGraph > entityGraph = context .getAnnotation (EntityGraph .class );
153
190
MergedAnnotation <Modifying > modifying = context .getAnnotation (Modifying .class );
154
191
155
192
body .add (context .codeBlocks ().logDebug ("invoking [%s]" .formatted (context .getMethod ().getName ())));
156
193
157
- AotQueries aotQueries = queriesFactory .createQueries (context , query , selector , queryMethod , returnedType );
158
194
AotEntityGraph aotEntityGraph = entityGraphLookup .findEntityGraph (entityGraph , repositoryInformation ,
159
195
returnedType , queryMethod );
160
196
@@ -170,4 +206,20 @@ protected void customizeConstructor(AotRepositoryConstructorBuilder constructorB
170
206
});
171
207
}
172
208
209
+ record StoredProcedureMetadata (String procedure ) implements QueryMetadata {
210
+
211
+ @ Override
212
+ public Map <String , Object > serialize () {
213
+ return Map .of ("procedure" , procedure ());
214
+ }
215
+ }
216
+
217
+ record NamedStoredProcedureMetadata (String procedureName ) implements QueryMetadata {
218
+
219
+ @ Override
220
+ public Map <String , Object > serialize () {
221
+ return Map .of ("procedure-name" , procedureName ());
222
+ }
223
+ }
224
+
173
225
}
0 commit comments