11/*
2- * Copyright 2024- 2025 the original author or authors.
2+ * Copyright 2025 the original author or authors.
33 *
44 * Licensed under the Apache License, Version 2.0 (the "License");
55 * you may not use this file except in compliance with the License.
1313 * See the License for the specific language governing permissions and
1414 * limitations under the License.
1515 */
16- package org .springframework .data .jpa .repository .aot . generated ;
16+ package org .springframework .data .jpa .repository .aot ;
1717
1818import jakarta .persistence .EntityManager ;
1919import jakarta .persistence .EntityManagerFactory ;
2020import jakarta .persistence .Tuple ;
2121import jakarta .persistence .TypedQueryReference ;
22+ import jakarta .persistence .metamodel .Metamodel ;
2223
2324import java .lang .reflect .Method ;
2425import java .util .Arrays ;
5758import org .springframework .data .repository .query .QueryMethod ;
5859import org .springframework .data .repository .query .ReturnedType ;
5960import org .springframework .data .repository .query .parser .PartTree ;
61+ import org .springframework .data .util .TypeInformation ;
6062import org .springframework .javapoet .CodeBlock ;
6163import org .springframework .javapoet .TypeName ;
6264import org .springframework .javapoet .TypeSpec ;
7678 */
7779public class JpaRepositoryContributor extends RepositoryContributor {
7880
79- private final AotMetamodel metaModel ;
81+ private final EntityManagerFactory emf ;
82+ private final Metamodel metaModel ;
8083 private final PersistenceProvider persistenceProvider ;
8184
8285 public JpaRepositoryContributor (AotRepositoryContext repositoryContext ) {
8386 super (repositoryContext );
84- this .metaModel = new AotMetamodel (repositoryContext .getResolvedTypes ());
85- this .persistenceProvider = PersistenceProvider .fromEntityManagerFactory (metaModel .getEntityManagerFactory ());
87+ AotMetamodel amm = new AotMetamodel (repositoryContext .getResolvedTypes ());
88+ this .metaModel = amm ;
89+ this .emf = amm .getEntityManagerFactory ();
90+ this .persistenceProvider = PersistenceProvider .fromEntityManagerFactory (amm .getEntityManagerFactory ());
91+ }
92+
93+ public JpaRepositoryContributor (AotRepositoryContext repositoryContext , EntityManagerFactory entityManagerFactory ) {
94+ super (repositoryContext );
95+ this .emf = entityManagerFactory ;
96+ this .metaModel = entityManagerFactory .getMetamodel ();
97+ this .persistenceProvider = PersistenceProvider .fromEntityManagerFactory (entityManagerFactory );
8698 }
8799
88100 @ Override
@@ -118,16 +130,31 @@ protected void customizeConstructor(AotRepositoryConstructorBuilder constructorB
118130 return null ;
119131 }
120132
133+ ReturnedType returnedType = queryMethod .getResultProcessor ().getReturnedType ();
134+
135+ // no interface/dynamic projections for now.
136+ if (returnedType .isProjecting () && returnedType .getReturnedType ().isInterface ()) {
137+ return null ;
138+ }
139+
140+ if (queryMethod .getParameters ().hasDynamicProjection ()) {
141+ return null ;
142+ }
143+
121144 // no KeysetScrolling for now.
122145 if (queryMethod .getParameters ().hasScrollPositionParameter ()) {
123146 return null ;
124147 }
125148
126149 if (queryMethod .isModifyingQuery ()) {
127150
128- Class <?> returnType = repositoryInformation .getReturnType (method ).getType ();
129- if (!ClassUtils .isVoidType (returnType )
130- && !JpaCodeBlocks .QueryExecutionBlockBuilder .returnsModifying (returnType )) {
151+ TypeInformation <?> returnType = repositoryInformation .getReturnType (method );
152+
153+ boolean returnsCount = JpaCodeBlocks .QueryExecutionBlockBuilder .returnsModifying (returnType .getType ());
154+
155+ boolean isVoid = ClassUtils .isVoidType (returnType .getType ());
156+
157+ if (!returnsCount && !isVoid ) {
131158 return null ;
132159 }
133160 }
@@ -140,15 +167,14 @@ protected void customizeConstructor(AotRepositoryConstructorBuilder constructorB
140167 MergedAnnotation <NativeQuery > nativeQuery = context .getAnnotation (NativeQuery .class );
141168 MergedAnnotation <QueryHints > queryHints = context .getAnnotation (QueryHints .class );
142169 MergedAnnotation <Modifying > modifying = context .getAnnotation (Modifying .class );
143- ReturnedType returnedType = context .getReturnedType ();
144170
145171 body .add (context .codeBlocks ().logDebug ("invoking [%s]" .formatted (context .getMethod ().getName ())));
146172
147173 AotQueries aotQueries = getQueries (context , query , selector , queryMethod , returnedType );
148174
149175 body .add (JpaCodeBlocks .queryBuilder (context , queryMethod ).filter (aotQueries )
150- .queryReturnType (getQueryReturnType (aotQueries .result (), returnedType , context )).query ( query )
151- .nativeQuery ( nativeQuery ). queryHints (queryHints ).build ());
176+ .queryReturnType (getQueryReturnType (aotQueries .result (), returnedType , context )).nativeQuery ( nativeQuery )
177+ .queryHints (queryHints ).build ());
152178
153179 body .add (
154180 JpaCodeBlocks .executionBuilder (context , queryMethod ).modifying (modifying ).query (aotQueries .result ()).build ());
@@ -178,8 +204,7 @@ private AotQueries buildStringQuery(Class<?> domainType, ReturnedType returnedTy
178204
179205 UnaryOperator <String > operator = s -> s .replaceAll ("#\\ {#entityName}" , domainType .getName ());
180206 boolean isNative = query .getBoolean ("nativeQuery" );
181- Function <String , StringAotQuery > queryFunction = isNative ? StringAotQuery ::nativeQuery
182- : StringAotQuery ::jpqlQuery ;
207+ Function <String , StringAotQuery > queryFunction = isNative ? StringAotQuery ::nativeQuery : StringAotQuery ::jpqlQuery ;
183208 queryFunction = operator .andThen (queryFunction );
184209
185210 String queryString = query .getString ("value" );
@@ -252,8 +277,6 @@ private NamedAotQuery buildNamedAotQuery(TypedQueryReference<?> namedQuery, JpaQ
252277 returnedType .getReturnedType (), returnedType .getTypeToRead (), void .class , null , Long .class , Integer .class ,
253278 Long .TYPE , Integer .TYPE , Number .class );
254279
255- EntityManagerFactory emf = metaModel .getEntityManagerFactory ();
256-
257280 for (Class <?> candidate : candidates ) {
258281
259282 Map <String , ? extends TypedQueryReference <?>> namedQueries = emf .getNamedQueries (candidate );
0 commit comments