1515 */
1616package org .springframework .data .jpa .repository .aot ;
1717
18+ import jakarta .persistence .Converter ;
19+ import jakarta .persistence .Embeddable ;
20+ import jakarta .persistence .Entity ;
1821import jakarta .persistence .EntityManager ;
1922import jakarta .persistence .EntityManagerFactory ;
23+ import jakarta .persistence .MappedSuperclass ;
2024import jakarta .persistence .PersistenceUnitUtil ;
2125import jakarta .persistence .metamodel .Metamodel ;
2226import jakarta .persistence .spi .PersistenceUnitInfo ;
2327
2428import java .lang .reflect .Method ;
29+ import java .util .List ;
2530import java .util .Map ;
2631import java .util .Optional ;
2732import java .util .function .Function ;
33+ import java .util .function .Supplier ;
2834
2935import org .jspecify .annotations .Nullable ;
3036
5965import org .springframework .data .repository .query .ParametersSource ;
6066import org .springframework .data .repository .query .QueryMethod ;
6167import org .springframework .data .repository .query .ReturnedType ;
68+ import org .springframework .data .util .Lazy ;
6269import org .springframework .javapoet .CodeBlock ;
6370import org .springframework .javapoet .TypeName ;
6471import org .springframework .orm .jpa .persistenceunit .PersistenceManagedTypes ;
6572import org .springframework .util .ClassUtils ;
73+ import org .springframework .util .ObjectUtils ;
6674import org .springframework .util .StringUtils ;
6775
6876/**
7785 */
7886public class JpaRepositoryContributor extends RepositoryContributor {
7987
88+ private final AotRepositoryContext context ;
89+ private final PersistenceUnitContext persistenceUnit ;
8090 private final Metamodel metamodel ;
8191 private final PersistenceUnitUtil persistenceUnitUtil ;
8292 private final PersistenceProvider persistenceProvider ;
8393 private final QueriesFactory queriesFactory ;
8494 private final EntityGraphLookup entityGraphLookup ;
85- private final AotRepositoryContext context ;
8695
8796 public JpaRepositoryContributor (AotRepositoryContext repositoryContext ) {
88- this (repositoryContext , new AotMetamodel (repositoryContext ));
89- }
90-
91- public JpaRepositoryContributor (AotRepositoryContext repositoryContext , PersistenceUnitInfo unitInfo ) {
92- this (repositoryContext , new AotMetamodel (unitInfo ));
93- }
94-
95- public JpaRepositoryContributor (AotRepositoryContext repositoryContext , PersistenceManagedTypes managedTypes ) {
96- this (repositoryContext , new AotMetamodel (managedTypes ));
97+ this (repositoryContext , PersistenceUnitContextFactory .from (repositoryContext ).create ());
9798 }
9899
99100 public JpaRepositoryContributor (AotRepositoryContext repositoryContext , EntityManagerFactory entityManagerFactory ) {
100- this (repositoryContext , entityManagerFactory , entityManagerFactory . getMetamodel ( ));
101+ this (repositoryContext , PersistenceUnitContext . just ( entityManagerFactory ));
101102 }
102103
103- private JpaRepositoryContributor (AotRepositoryContext repositoryContext , AotMetamodel metamodel ) {
104- this (repositoryContext , metamodel .getEntityManagerFactory (), metamodel );
105- }
106-
107- private JpaRepositoryContributor (AotRepositoryContext repositoryContext , EntityManagerFactory entityManagerFactory ,
108- Metamodel metamodel ) {
104+ public JpaRepositoryContributor (AotRepositoryContext repositoryContext , PersistenceUnitContext persistenceUnit ) {
109105
110106 super (repositoryContext );
111107
112- this .metamodel = metamodel ;
108+ this .persistenceUnit = persistenceUnit ;
109+ this .metamodel = persistenceUnit .getMetamodel ();
110+
111+ EntityManagerFactory entityManagerFactory = persistenceUnit .getEntityManagerFactory ();
112+
113113 this .persistenceUnitUtil = entityManagerFactory .getPersistenceUnitUtil ();
114114 this .persistenceProvider = PersistenceProvider .fromEntityManagerFactory (entityManagerFactory );
115115 this .queriesFactory = new QueriesFactory (repositoryContext .getConfigurationSource (), entityManagerFactory ,
@@ -258,8 +258,195 @@ private Optional<Class<QueryEnhancerSelector>> getQueryEnhancerSelectorClass() {
258258 });
259259 }
260260
261- public Metamodel getMetamodel () {
262- return metamodel ;
261+ public PersistenceUnitContext getPersistenceUnit () {
262+ return persistenceUnit ;
263+ }
264+
265+ /**
266+ * Factory for deferred {@link PersistenceUnitContext} creation. Factory objects implement equality checks based on
267+ * their creation and can be used conveniently as cache keys.
268+ */
269+ public static class PersistenceUnitContextFactory {
270+
271+ private final Supplier <? extends PersistenceUnitContext > factory ;
272+ private final Object key ;
273+
274+ private PersistenceUnitContextFactory (Supplier <? extends PersistenceUnitContext > factory , Object key ) {
275+ this .factory = Lazy .of (factory );
276+ this .key = key ;
277+ }
278+
279+ /**
280+ * Create a {@code PersistenceUnitContext} from the given {@link AotRepositoryContext} using Jakarta
281+ * Persistence-annotated classes.
282+ *
283+ * @param repositoryContext repository context providing classes.
284+ */
285+ public static PersistenceUnitContextFactory from (AotRepositoryContext repositoryContext ) {
286+
287+ List <String > typeNames = repositoryContext .getResolvedTypes ().stream ()
288+ .filter (PersistenceUnitContextFactory ::isJakartaAnnotated ).map (Class ::getName ).toList ();
289+
290+ return from (() -> new AotMetamodel (PersistenceManagedTypes .of (typeNames , List .of ())), typeNames );
291+ }
292+
293+ /**
294+ * Create a {@code PersistenceUnitContext} from the given {@link PersistenceUnitInfo}.
295+ *
296+ * @param persistenceUnitInfo persistence unit info to use.
297+ */
298+ public static PersistenceUnitContextFactory from (PersistenceUnitInfo persistenceUnitInfo ) {
299+ return from (() -> new AotMetamodel (persistenceUnitInfo ), persistenceUnitInfo );
300+ }
301+
302+ /**
303+ * Create a {@code PersistenceUnitContext} from the given {@link PersistenceManagedTypes}.
304+ *
305+ * @param managedTypes managed types to use.
306+ */
307+ public static PersistenceUnitContextFactory from (PersistenceManagedTypes managedTypes ) {
308+ return from (() -> new AotMetamodel (managedTypes ), managedTypes );
309+ }
310+
311+ /**
312+ * Create a {@code PersistenceUnitContext} from the given {@link EntityManagerFactory} and its {@link Metamodel}.
313+ *
314+ * @param entityManagerFactory the entity manager factory to use.
315+ */
316+ public static PersistenceUnitContextFactory just (EntityManagerFactory entityManagerFactory ) {
317+ return new PersistenceUnitContextFactory (() -> new EntityManagerPersistenceUnitContext (entityManagerFactory ),
318+ entityManagerFactory .getMetamodel ());
319+ }
320+
321+ /**
322+ * Create a {@code PersistenceUnitContext} from the given {@link EntityManagerFactory} and its {@link Metamodel}.
323+ *
324+ * @param metamodel the metamodel to use.
325+ * @param entityManagerFactory the entity manager factory to use.
326+ */
327+ public static PersistenceUnitContextFactory just (EntityManagerFactory entityManagerFactory , Metamodel metamodel ) {
328+ return new PersistenceUnitContextFactory (
329+ () -> new EntityManagerPersistenceUnitContext (entityManagerFactory , metamodel ), metamodel );
330+ }
331+
332+ private static PersistenceUnitContextFactory from (Supplier <? extends AotMetamodel > metamodel , Object key ) {
333+ return new PersistenceUnitContextFactory (() -> new AotMetamodelContext (metamodel .get ()), key );
334+ }
335+
336+ private static boolean isJakartaAnnotated (Class <?> cls ) {
337+
338+ return cls .isAnnotationPresent (Entity .class ) //
339+ || cls .isAnnotationPresent (Embeddable .class ) //
340+ || cls .isAnnotationPresent (MappedSuperclass .class ) //
341+ || cls .isAnnotationPresent (Converter .class );
342+ }
343+
344+ public PersistenceUnitContext create () {
345+ return factory .get ();
346+ }
347+
348+ @ Override
349+ public boolean equals (Object o ) {
350+ if (!(o instanceof PersistenceUnitContextFactory that )) {
351+ return false ;
352+ }
353+ return ObjectUtils .nullSafeEquals (key , that .key );
354+ }
355+
356+ @ Override
357+ public int hashCode () {
358+ return ObjectUtils .nullSafeHashCode (key );
359+ }
360+
361+ @ Override
362+ public String toString () {
363+ return "PersistenceUnitContextFactory{" + key + '}' ;
364+ }
365+
366+ }
367+
368+ /**
369+ * Strategy interface representing a JPA PersistenceUnit providing access to {@link EntityManagerFactory} and
370+ * {@link Metamodel} for AOT repository generation.
371+ */
372+ public interface PersistenceUnitContext {
373+
374+ /**
375+ * @return the entity manager factory.
376+ */
377+ EntityManagerFactory getEntityManagerFactory ();
378+
379+ /**
380+ * @return metamodel describing managed types used in the persistence unit.
381+ */
382+ Metamodel getMetamodel ();
383+
384+ /**
385+ * Create a {@code PersistenceUnitContext} from the given {@link PersistenceUnitInfo}.
386+ *
387+ * @param persistenceUnitInfo persistence unit info to use.
388+ */
389+ static PersistenceUnitContext from (PersistenceUnitInfo persistenceUnitInfo ) {
390+ return new AotMetamodelContext (new AotMetamodel (persistenceUnitInfo ));
391+ }
392+
393+ /**
394+ * Create a {@code PersistenceUnitContext} from the given {@link PersistenceManagedTypes}.
395+ *
396+ * @param managedTypes managed types to use.
397+ */
398+ static PersistenceUnitContext from (PersistenceManagedTypes managedTypes ) {
399+ return new AotMetamodelContext (new AotMetamodel (managedTypes ));
400+ }
401+
402+ /**
403+ * Create a {@code PersistenceUnitContext} from the given {@link EntityManagerFactory} and its {@link Metamodel}.
404+ *
405+ * @param entityManagerFactory the entity manager factory to use.
406+ */
407+ static PersistenceUnitContext just (EntityManagerFactory entityManagerFactory ) {
408+ return new EntityManagerPersistenceUnitContext (entityManagerFactory );
409+ }
410+
411+ }
412+
413+ /**
414+ * Persistence unit context backed by an {@link EntityManagerFactory}.
415+ */
416+ record EntityManagerPersistenceUnitContext (EntityManagerFactory factory ,
417+ Metamodel metamodel ) implements PersistenceUnitContext {
418+
419+ public EntityManagerPersistenceUnitContext (EntityManagerFactory factory ) {
420+ this (factory , factory .getMetamodel ());
421+ }
422+
423+ @ Override
424+ public Metamodel getMetamodel () {
425+ return metamodel ();
426+ }
427+
428+ @ Override
429+ public EntityManagerFactory getEntityManagerFactory () {
430+ return factory ();
431+ }
432+
433+ }
434+
435+ /**
436+ * Persistence unit context backed by an {@link AotMetamodel}.
437+ */
438+ private record AotMetamodelContext (AotMetamodel metamodel ) implements PersistenceUnitContext {
439+
440+ @ Override
441+ public EntityManagerFactory getEntityManagerFactory () {
442+ return metamodel .getEntityManagerFactory ();
443+ }
444+
445+ @ Override
446+ public Metamodel getMetamodel () {
447+ return metamodel ;
448+ }
449+
263450 }
264451
265452 record StoredProcedureMetadata (String procedure ) implements QueryMetadata {
@@ -268,6 +455,7 @@ record StoredProcedureMetadata(String procedure) implements QueryMetadata {
268455 public Map <String , Object > serialize () {
269456 return Map .of ("procedure" , procedure ());
270457 }
458+
271459 }
272460
273461 record NamedStoredProcedureMetadata (String procedureName ) implements QueryMetadata {
@@ -276,6 +464,7 @@ record NamedStoredProcedureMetadata(String procedureName) implements QueryMetada
276464 public Map <String , Object > serialize () {
277465 return Map .of ("procedure-name" , procedureName ());
278466 }
467+
279468 }
280469
281470 /**
0 commit comments