diff --git a/pom.xml b/pom.xml index bf477e770c..f513cd1cbc 100755 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-jpa-parent - 4.0.0-SNAPSHOT + 4.0.0-GH-4068-SNAPSHOT pom Spring Data JPA Parent diff --git a/spring-data-envers/pom.xml b/spring-data-envers/pom.xml index 0bdf2c8e7e..822c162b4a 100755 --- a/spring-data-envers/pom.xml +++ b/spring-data-envers/pom.xml @@ -5,12 +5,12 @@ org.springframework.data spring-data-envers - 4.0.0-SNAPSHOT + 4.0.0-GH-4068-SNAPSHOT org.springframework.data spring-data-jpa-parent - 4.0.0-SNAPSHOT + 4.0.0-GH-4068-SNAPSHOT ../pom.xml diff --git a/spring-data-jpa-distribution/pom.xml b/spring-data-jpa-distribution/pom.xml index af5244a230..e6b8e58c2c 100644 --- a/spring-data-jpa-distribution/pom.xml +++ b/spring-data-jpa-distribution/pom.xml @@ -14,7 +14,7 @@ org.springframework.data spring-data-jpa-parent - 4.0.0-SNAPSHOT + 4.0.0-GH-4068-SNAPSHOT ../pom.xml diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index bb01d9f6f6..91ddf67d76 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -7,7 +7,7 @@ org.springframework.data spring-data-jpa - 4.0.0-SNAPSHOT + 4.0.0-GH-4068-SNAPSHOT Spring Data JPA Spring Data module for JPA repositories. @@ -16,7 +16,7 @@ org.springframework.data spring-data-jpa-parent - 4.0.0-SNAPSHOT + 4.0.0-GH-4068-SNAPSHOT ../pom.xml diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/AotMetamodel.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/AotMetamodel.java index 238d270de5..812abe5e6f 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/AotMetamodel.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/AotMetamodel.java @@ -37,7 +37,7 @@ import org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl; import org.hibernate.jpa.boot.internal.PersistenceUnitInfoDescriptor; import org.jspecify.annotations.Nullable; -import org.springframework.data.repository.config.AotRepositoryContext; + import org.springframework.data.util.Lazy; import org.springframework.orm.jpa.persistenceunit.PersistenceManagedTypes; import org.springframework.orm.jpa.persistenceunit.SpringPersistenceUnitInfo; @@ -55,19 +55,6 @@ class AotMetamodel implements Metamodel { private final Lazy entityManagerFactory; private final Lazy entityManager = Lazy.of(() -> getEntityManagerFactory().createEntityManager()); - public AotMetamodel(AotRepositoryContext repositoryContext) { - this(repositoryContext.getResolvedTypes().stream().filter(AotMetamodel::isJakartaAnnotated).map(Class::getName) - .toList(), null); - } - - private static boolean isJakartaAnnotated(Class cls) { - - return cls.isAnnotationPresent(jakarta.persistence.Entity.class) - || cls.isAnnotationPresent(jakarta.persistence.Embeddable.class) - || cls.isAnnotationPresent(jakarta.persistence.MappedSuperclass.class) - || cls.isAnnotationPresent(jakarta.persistence.Converter.class); - } - public AotMetamodel(PersistenceManagedTypes managedTypes) { this(managedTypes.getManagedClassNames(), managedTypes.getPersistenceUnitRootUrl()); } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/EntityGraphLookup.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/EntityGraphLookup.java index 7e715f9e24..33dd14e189 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/EntityGraphLookup.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/EntityGraphLookup.java @@ -42,13 +42,7 @@ * @author Mark Paluch * @since 4.0 */ -class EntityGraphLookup { - - private final EntityManagerFactory entityManagerFactory; - - public EntityGraphLookup(EntityManagerFactory entityManagerFactory) { - this.entityManagerFactory = entityManagerFactory; - } +record EntityGraphLookup(EntityManagerFactory entityManagerFactory) { @SuppressWarnings("unchecked") public @Nullable AotEntityGraph findEntityGraph(MergedAnnotation entityGraph, diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/JpaRepositoryContributor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/JpaRepositoryContributor.java index 2c169efac0..75a8078682 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/JpaRepositoryContributor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/JpaRepositoryContributor.java @@ -19,7 +19,6 @@ import jakarta.persistence.EntityManagerFactory; import jakarta.persistence.PersistenceUnitUtil; import jakarta.persistence.metamodel.Metamodel; -import jakarta.persistence.spi.PersistenceUnitInfo; import java.lang.reflect.Method; import java.util.Map; @@ -61,7 +60,6 @@ import org.springframework.data.repository.query.ReturnedType; import org.springframework.javapoet.CodeBlock; import org.springframework.javapoet.TypeName; -import org.springframework.orm.jpa.persistenceunit.PersistenceManagedTypes; import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; @@ -77,39 +75,31 @@ */ public class JpaRepositoryContributor extends RepositoryContributor { + private final AotRepositoryContext context; + private final PersistenceUnitContext persistenceUnit; private final Metamodel metamodel; private final PersistenceUnitUtil persistenceUnitUtil; private final PersistenceProvider persistenceProvider; private final QueriesFactory queriesFactory; private final EntityGraphLookup entityGraphLookup; - private final AotRepositoryContext context; public JpaRepositoryContributor(AotRepositoryContext repositoryContext) { - this(repositoryContext, new AotMetamodel(repositoryContext)); - } - - public JpaRepositoryContributor(AotRepositoryContext repositoryContext, PersistenceUnitInfo unitInfo) { - this(repositoryContext, new AotMetamodel(unitInfo)); - } - - public JpaRepositoryContributor(AotRepositoryContext repositoryContext, PersistenceManagedTypes managedTypes) { - this(repositoryContext, new AotMetamodel(managedTypes)); + this(repositoryContext, PersistenceUnitContexts.from(repositoryContext)); } public JpaRepositoryContributor(AotRepositoryContext repositoryContext, EntityManagerFactory entityManagerFactory) { - this(repositoryContext, entityManagerFactory, entityManagerFactory.getMetamodel()); + this(repositoryContext, PersistenceUnitContexts.from(entityManagerFactory)); } - private JpaRepositoryContributor(AotRepositoryContext repositoryContext, AotMetamodel metamodel) { - this(repositoryContext, metamodel.getEntityManagerFactory(), metamodel); - } - - private JpaRepositoryContributor(AotRepositoryContext repositoryContext, EntityManagerFactory entityManagerFactory, - Metamodel metamodel) { + public JpaRepositoryContributor(AotRepositoryContext repositoryContext, PersistenceUnitContext persistenceUnit) { super(repositoryContext); - this.metamodel = metamodel; + this.persistenceUnit = persistenceUnit; + this.metamodel = persistenceUnit.getMetamodel(); + + EntityManagerFactory entityManagerFactory = persistenceUnit.getEntityManagerFactory(); + this.persistenceUnitUtil = entityManagerFactory.getPersistenceUnitUtil(); this.persistenceProvider = PersistenceProvider.fromEntityManagerFactory(entityManagerFactory); this.queriesFactory = new QueriesFactory(repositoryContext.getConfigurationSource(), entityManagerFactory, @@ -258,8 +248,8 @@ private Optional> getQueryEnhancerSelectorClass() { }); } - public Metamodel getMetamodel() { - return metamodel; + public PersistenceUnitContext getPersistenceUnit() { + return persistenceUnit; } record StoredProcedureMetadata(String procedure) implements QueryMetadata { @@ -268,6 +258,7 @@ record StoredProcedureMetadata(String procedure) implements QueryMetadata { public Map serialize() { return Map.of("procedure", procedure()); } + } record NamedStoredProcedureMetadata(String procedureName) implements QueryMetadata { @@ -276,6 +267,7 @@ record NamedStoredProcedureMetadata(String procedureName) implements QueryMetada public Map serialize() { return Map.of("procedure-name", procedureName()); } + } /** diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/PersistenceUnitContext.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/PersistenceUnitContext.java new file mode 100644 index 0000000000..23eade324c --- /dev/null +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/PersistenceUnitContext.java @@ -0,0 +1,36 @@ +/* + * Copyright 2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.aot; + +import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.metamodel.Metamodel; + +/** + * Strategy interface representing a JPA PersistenceUnit providing access to {@link EntityManagerFactory} and + * {@link Metamodel} for AOT repository generation. + */ +public interface PersistenceUnitContext { + + /** + * @return the entity manager factory. + */ + EntityManagerFactory getEntityManagerFactory(); + + /** + * @return metamodel describing managed types used in the persistence unit. + */ + Metamodel getMetamodel(); +} diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/PersistenceUnitContexts.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/PersistenceUnitContexts.java new file mode 100644 index 0000000000..d61047f689 --- /dev/null +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/PersistenceUnitContexts.java @@ -0,0 +1,210 @@ +/* + * Copyright 2025-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.aot; + +import jakarta.persistence.Converter; +import jakarta.persistence.Embeddable; +import jakarta.persistence.Entity; +import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.MappedSuperclass; +import jakarta.persistence.metamodel.Metamodel; +import jakarta.persistence.spi.PersistenceUnitInfo; + +import java.util.List; +import java.util.function.Supplier; + +import org.springframework.data.repository.config.AotRepositoryContext; +import org.springframework.data.util.Lazy; +import org.springframework.orm.jpa.persistenceunit.PersistenceManagedTypes; +import org.springframework.util.ConcurrentLruCache; + +/** + * @author Christoph Strobl + */ +public class PersistenceUnitContexts { + + private static final ConcurrentLruCache PERSISTENCE_UNIT_FACTORY_CACHE = new ConcurrentLruCache<>( + 16, PersistenceUnitContexts::createFactory); + + private static PersistenceUnitContextFactory createFactory(Object source) { + + if (source instanceof AotRepositoryContext ctx) { + + List typeNames = ctx.getResolvedTypes().stream() + .filter(DefaultPersistenceUnitContextFactory::isJakartaAnnotated).map(Class::getName).toList(); + + return from(() -> new AotMetamodel(PersistenceManagedTypes.of(typeNames, List.of()))); + } + if (source instanceof PersistenceUnitInfo pui) { + return from(() -> new AotMetamodel(pui)); + } + if (source instanceof PersistenceManagedTypes managedTypes) { + return from(() -> new AotMetamodel(managedTypes)); + } + if (source instanceof EntityManagerFactory emf) { + return new DefaultPersistenceUnitContextFactory( + () -> new EntityManagerPersistenceUnitContext(emf, emf.getMetamodel())); + } + + throw new IllegalArgumentException(String.format("Cannot create PersistenceUnitContexts for %s", source)); + } + + /** + * Create a {@code PersistenceUnitContext} from the given {@link AotRepositoryContext} using Jakarta + * Persistence-annotated classes. + * + * @param repositoryContext repository context providing classes. + */ + public static PersistenceUnitContext from(AotRepositoryContext repositoryContext) { + return factory().from(repositoryContext).create(); + } + + /** + * Create a {@code PersistenceUnitContext} from the given {@link PersistenceUnitInfo}. + * + * @param persistenceUnitInfo persistence unit info to use. + */ + public static PersistenceUnitContext from(PersistenceUnitInfo persistenceUnitInfo) { + return factory().from(persistenceUnitInfo).create(); + } + + /** + * Create a {@code PersistenceUnitContext} from the given {@link PersistenceManagedTypes}. + * + * @param managedTypes managed types to use. + */ + public static PersistenceUnitContext from(PersistenceManagedTypes managedTypes) { + return factory().from(managedTypes).create(); + } + + /** + * Create a {@code PersistenceUnitContext} from the given {@link EntityManagerFactory} and its {@link Metamodel}. + * + * @param entityManagerFactory the entity manager factory to use. + */ + public static PersistenceUnitContext from(EntityManagerFactory entityManagerFactory) { + return factory().from(entityManagerFactory).create(); + } + + private static PersistenceUnitContextFactory from(Supplier metamodel) { + return new DefaultPersistenceUnitContextFactory(() -> new AotMetamodelContext(metamodel.get())); + } + + public static PeristenceUnitFactoryBuilder factory() { + + return new PeristenceUnitFactoryBuilder() { + + @Override + public PersistenceUnitContextFactory from(EntityManagerFactory entityManagerFactory) { + return PERSISTENCE_UNIT_FACTORY_CACHE.get(entityManagerFactory); + } + + @Override + public PersistenceUnitContextFactory from(PersistenceManagedTypes managedTypes) { + return PERSISTENCE_UNIT_FACTORY_CACHE.get(managedTypes); + } + + @Override + public PersistenceUnitContextFactory from(PersistenceUnitInfo persistenceUnitInfo) { + return PERSISTENCE_UNIT_FACTORY_CACHE.get(persistenceUnitInfo); + } + + @Override + public PersistenceUnitContextFactory from(AotRepositoryContext repositoryContext) { + return PERSISTENCE_UNIT_FACTORY_CACHE.get(repositoryContext); + } + }; + } + + public interface PeristenceUnitFactoryBuilder { + PersistenceUnitContextFactory from(EntityManagerFactory entityManagerFactory); + + PersistenceUnitContextFactory from(PersistenceManagedTypes entityManagerFactory); + + PersistenceUnitContextFactory from(PersistenceUnitInfo entityManagerFactory); + + PersistenceUnitContextFactory from(AotRepositoryContext entityManagerFactory); + } + + /** + * Persistence unit context backed by an {@link AotMetamodel}. + */ + record AotMetamodelContext(AotMetamodel metamodel) implements PersistenceUnitContext { + + @Override + public EntityManagerFactory getEntityManagerFactory() { + return metamodel.getEntityManagerFactory(); + } + + @Override + public Metamodel getMetamodel() { + return metamodel; + } + } + + /** + * Persistence unit context backed by an {@link EntityManagerFactory}. + */ + record EntityManagerPersistenceUnitContext(EntityManagerFactory factory, + Metamodel metamodel) implements PersistenceUnitContext { + + public EntityManagerPersistenceUnitContext(EntityManagerFactory factory) { + this(factory, factory.getMetamodel()); + } + + @Override + public Metamodel getMetamodel() { + return metamodel(); + } + + @Override + public EntityManagerFactory getEntityManagerFactory() { + return factory(); + } + + } + + public interface PersistenceUnitContextFactory { + PersistenceUnitContext create(); + } + + /** + * Factory for deferred {@link PersistenceUnitContext} creation. Factory objects implement equality checks based on + * their creation and can be used conveniently as cache keys. + */ + public static class DefaultPersistenceUnitContextFactory implements PersistenceUnitContextFactory { + + private final Supplier persistenceUnitContextSupplier; + + private DefaultPersistenceUnitContextFactory( + Supplier persistenceUnitContextSupplier) { + + this.persistenceUnitContextSupplier = Lazy.of(persistenceUnitContextSupplier); + } + + private static boolean isJakartaAnnotated(Class cls) { + + return cls.isAnnotationPresent(Entity.class) // + || cls.isAnnotationPresent(Embeddable.class) // + || cls.isAnnotationPresent(MappedSuperclass.class) // + || cls.isAnnotationPresent(Converter.class); + } + + public PersistenceUnitContext create() { + return persistenceUnitContextSupplier.get(); + } + } +} diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/QueriesFactory.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/QueriesFactory.java index 7dd293b313..83a1d3dce7 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/QueriesFactory.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/QueriesFactory.java @@ -54,7 +54,8 @@ import org.springframework.util.StringUtils; /** - * Factory for {@link AotQueries}. + * Factory for {@link AotQueries}. Requires {@link EntityManagerFactory} for named query resolution and + * {@link Metamodel} for query derivation to navigate the entity model. * * @author Mark Paluch * @author Christoph Strobl diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java index e61f0e13ab..057b47f3ca 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java @@ -15,7 +15,8 @@ */ package org.springframework.data.jpa.repository.config; -import static org.springframework.data.jpa.repository.config.BeanDefinitionNames.*; +import static org.springframework.data.jpa.repository.config.BeanDefinitionNames.JPA_CONTEXT_BEAN_NAME; +import static org.springframework.data.jpa.repository.config.BeanDefinitionNames.JPA_MAPPING_CONTEXT_BEAN_NAME; import jakarta.persistence.Entity; import jakarta.persistence.EntityManager; @@ -39,7 +40,6 @@ import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; - import org.springframework.aot.generate.GenerationContext; import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.aot.BeanRegistrationAotProcessor; @@ -61,6 +61,9 @@ import org.springframework.data.aot.AotContext; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.aot.JpaRepositoryContributor; +import org.springframework.data.jpa.repository.aot.PersistenceUnitContexts; +import org.springframework.data.jpa.repository.aot.PersistenceUnitContexts.PersistenceUnitContextFactory; +import org.springframework.data.jpa.repository.aot.PersistenceUnitContext; import org.springframework.data.jpa.repository.support.DefaultJpaContext; import org.springframework.data.jpa.repository.support.JpaEvaluationContextExtension; import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean; @@ -75,6 +78,7 @@ import org.springframework.orm.jpa.persistenceunit.PersistenceManagedTypes; import org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor; import org.springframework.util.ClassUtils; +import org.springframework.util.ConcurrentLruCache; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; @@ -378,6 +382,9 @@ public static class JpaRepositoryRegistrationAotProcessor extends RepositoryRegi private static final String MODULE_NAME = "jpa"; + private static final ConcurrentLruCache PERSISTENCE_UNIT_CACHE = new ConcurrentLruCache<>( + 16, PersistenceUnitContextFactory::create); + @Override protected void configureTypeContributions(AotRepositoryContext repositoryContext, GenerationContext generationContext) { @@ -405,8 +412,10 @@ protected void configureTypeContribution(Class type, AotContext aotContext) { Optional entityManagerFactoryRef = repositoryContext.getConfigurationSource() .getAttribute("entityManagerFactoryRef"); - log.debug( - "Using EntityManager '%s' for AOT repository generation".formatted(entityManagerFactoryRef.orElse(""))); + if (log.isDebugEnabled()) { + log.debug( + "Using EntityManager '%s' for AOT repository generation".formatted(entityManagerFactoryRef.orElse(""))); + } EntityManagerFactory emf = entityManagerFactoryRef .map(it -> beanFactory.getBean(it, EntityManagerFactory.class)) @@ -420,8 +429,10 @@ protected void configureTypeContribution(Class type, AotContext aotContext) { if (managedTypes != null) { - log.debug("Using PersistenceManagedTypes for AOT repository generation"); - return new JpaRepositoryContributor(repositoryContext, managedTypes); + if (log.isDebugEnabled()) { + log.debug("Using PersistenceManagedTypes for AOT repository generation"); + } + return contribute(repositoryContext, PersistenceUnitContexts.from(managedTypes)); } ObjectProvider infoProvider = beanFactory.getBeanProvider(PersistenceUnitInfo.class); @@ -429,12 +440,22 @@ protected void configureTypeContribution(Class type, AotContext aotContext) { if (unitInfo != null) { - log.debug("Using PersistenceUnitInfo for AOT repository generation"); - return new JpaRepositoryContributor(repositoryContext, unitInfo); + if (log.isDebugEnabled()) { + log.debug("Using PersistenceUnitInfo for AOT repository generation"); + } + return contribute(repositoryContext, PersistenceUnitContexts.from(unitInfo)); + } + + if (log.isDebugEnabled()) { + log.debug("Using scanned types for AOT repository generation"); } + return contribute(repositoryContext, PersistenceUnitContexts.from(repositoryContext)); + } + + private JpaRepositoryContributor contribute(AotRepositoryContext repositoryContext, + PersistenceUnitContext persistenceUnitContext) { - log.debug("Using scanned types for AOT repository generation"); - return new JpaRepositoryContributor(repositoryContext); + return new JpaRepositoryContributor(repositoryContext, persistenceUnitContext); } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/aot/PersistenceUnitContextsUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/aot/PersistenceUnitContextsUnitTests.java new file mode 100644 index 0000000000..ccea4ed061 --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/aot/PersistenceUnitContextsUnitTests.java @@ -0,0 +1,41 @@ +/* + * Copyright 2025-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.aot; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +import jakarta.persistence.spi.PersistenceUnitInfo; + +import org.junit.jupiter.api.Test; +import org.springframework.data.jpa.repository.aot.PersistenceUnitContexts.PersistenceUnitContextFactory; + +/** + * @author Christoph Strobl + */ +public class PersistenceUnitContextsUnitTests { + + @Test // GH-4068 + void cachesPersistenceUnitContextFactory() { + + PersistenceUnitInfo persistenceUnitInfo = mock(PersistenceUnitInfo.class); + + PersistenceUnitContextFactory ctxFactory = PersistenceUnitContexts.factory().from(persistenceUnitInfo); + + assertThat(PersistenceUnitContexts.factory().from(persistenceUnitInfo)).isSameAs(ctxFactory); + assertThat(PersistenceUnitContexts.factory().from(mock(PersistenceUnitInfo.class))).isNotSameAs(ctxFactory); + } +} diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryRegistrationAotProcessorUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryRegistrationAotProcessorUnitTests.java index 25277bad6c..66f2ff0095 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryRegistrationAotProcessorUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryRegistrationAotProcessorUnitTests.java @@ -131,7 +131,7 @@ public List getManagedPackages() { JpaRepositoryContributor contributor = new JpaRepositoryConfigExtension.JpaRepositoryRegistrationAotProcessor() .contributeAotRepository(new DummyAotRepositoryContext(context)); - assertThat(contributor.getMetamodel().managedType(Person.class)).isNotNull(); + assertThat(contributor.getPersistenceUnit().getMetamodel().managedType(Person.class)).isNotNull(); } @Test // GH-3899