diff --git a/src/main/asciidoc/jpa.adoc b/src/main/asciidoc/jpa.adoc index 027fb4795..ac43c9d25 100644 --- a/src/main/asciidoc/jpa.adoc +++ b/src/main/asciidoc/jpa.adoc @@ -96,6 +96,35 @@ NOTE: You must create `LocalContainerEntityManagerFactoryBean` and not `EntityMa The preceding configuration class sets up an embedded HSQL database by using the `EmbeddedDatabaseBuilder` API of `spring-jdbc`. Spring Data then sets up an `EntityManagerFactory` and uses Hibernate as the sample persistence provider. The last infrastructure component declared here is the `JpaTransactionManager`. Finally, the example activates Spring Data JPA repositories by using the `@EnableJpaRepositories` annotation, which essentially carries the same attributes as the XML namespace. If no base package is configured, it uses the one in which the configuration class resides. +[[jpa.bootstrap-mode]] +=== Bootstrap Mode + +By default, Spring Data JPA repositories are default Spring beans. +They are singleton scoped and eagerly initialized. +During startup, they already interact with the JPA `EntityManager` for verification and metadata analysis purposes. +Spring Framework supports the initialization of the JPA `EntityManagerFactory` in a background thread because that process usually takes up a significant amount of startup time in a Spring application. +To make use of that background initialization effectively, we need to make sure that JPA repositories are initialized as late as possible. + +As of Spring Data JPA 2.1 you can now configure a `BootstrapMode` (either via the `@EnableJpaRepositories` annotation or the XML namespace) that takes the following values: + +* `DEFAULT` (default) -- Repositories are instantiated eagerly unless explicitly annotated with `@Lazy`. +The lazification only has effect if no client bean needs an instance of the repository as that will require the initialization of the repository bean. +* `LAZY` -- Implicitly declares all repository beans lazy and also causes lazy initialization proxies to be created to be injected into client beans. +That means, that repositories will not get instantiated if the client bean is simply storing the instance in a field and not making use of the repository during initialization. +Repository instances will be initialized and verified upon first interaction with the repository. +* `DEFERRED` -- Fundamentally the same mode of operation as `LAZY`, but triggering repository initialization in response to an `ContextRefreshedEvent` so that repositories are verified before the application has completely started. + +==== Recommendations + +If you're not using asynchronous JPA bootstrap stick with the default bootstrap mode. + +In case you bootstrap JPA asynchronously, `DEFERRED` is a reasonable default as it will make sure the Spring Data JPA bootstrap only waits for the `EntityManagerFactory` setup if that itself takes longer than initializing all other application components. +Still, it makes sure that repositories are properly initialized and validated before the application signals it's up. + +`LAZY` is a decent choice for testing scenarios and local development. +Once you're pretty sure that repositories will properly bootstrap, or in cases where you're testing other parts of the application, executing verification for all repositories might just unnecessarily increase the startup time. +The same applies to local development in which you only access parts of the application which might just need a single repository initialized. + [[jpa.entity-persistence]] == Persisting Entities diff --git a/src/main/java/org/springframework/data/jpa/repository/config/EnableJpaRepositories.java b/src/main/java/org/springframework/data/jpa/repository/config/EnableJpaRepositories.java index fe0801058..3d2b66705 100644 --- a/src/main/java/org/springframework/data/jpa/repository/config/EnableJpaRepositories.java +++ b/src/main/java/org/springframework/data/jpa/repository/config/EnableJpaRepositories.java @@ -27,7 +27,9 @@ import javax.persistence.EntityManagerFactory; import org.springframework.beans.factory.FactoryBean; import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.context.annotation.Import; +import org.springframework.context.annotation.Lazy; import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean; +import org.springframework.data.repository.config.BootstrapMode; import org.springframework.data.repository.config.DefaultRepositoryBaseClass; import org.springframework.data.repository.query.QueryLookupStrategy; import org.springframework.data.repository.query.QueryLookupStrategy.Key; @@ -150,4 +152,18 @@ public @interface EnableJpaRepositories { * @return whether to enable default transactions, defaults to {@literal true}. */ boolean enableDefaultTransactions() default true; + + /** + * Configures when the repositories are initialized in the bootstrap lifecycle. {@link BootstrapMode#DEFAULT} + * (default) means eager initialization except all repository interfaces annotated with {@link Lazy}, + * {@link BootstrapMode#LAZY} means lazy by default including injection of lazy-initialization proxies into client + * beans so that those can be instantiated but will only trigger the initialization upon first repository usage (i.e a + * method invocation on it). This means repositories can still be uninitialized when the application context has + * completed its bootstrap. {@link BootstrapMode#DEFERRED} is fundamentally the same as {@link BootstrapMode#LAZY}, + * but triggers repository initialization when the application context finishes its bootstrap. + * + * @return + * @since 2.1 + */ + BootstrapMode bootstrapMode() default BootstrapMode.DEFAULT; } diff --git a/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java b/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java index 1a68aed60..eff006672 100644 --- a/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java +++ b/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java @@ -172,21 +172,26 @@ public class JpaRepositoryConfigExtension extends RepositoryConfigurationExtensi Object source = config.getSource(); - registerIfNotAlreadyRegistered(new RootBeanDefinition(EntityManagerBeanDefinitionRegistrarPostProcessor.class), - registry, EM_BEAN_DEFINITION_REGISTRAR_POST_PROCESSOR_BEAN_NAME, source); + registerLazyIfNotAlreadyRegistered( + () -> new RootBeanDefinition(EntityManagerBeanDefinitionRegistrarPostProcessor.class), registry, + EM_BEAN_DEFINITION_REGISTRAR_POST_PROCESSOR_BEAN_NAME, source); - registerIfNotAlreadyRegistered(new RootBeanDefinition(JpaMetamodelMappingContextFactoryBean.class), registry, - JPA_MAPPING_CONTEXT_BEAN_NAME, source); + registerLazyIfNotAlreadyRegistered(() -> new RootBeanDefinition(JpaMetamodelMappingContextFactoryBean.class), + registry, JPA_MAPPING_CONTEXT_BEAN_NAME, source); - registerIfNotAlreadyRegistered(new RootBeanDefinition(PAB_POST_PROCESSOR), registry, + registerLazyIfNotAlreadyRegistered(() -> new RootBeanDefinition(PAB_POST_PROCESSOR), registry, AnnotationConfigUtils.PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME, source); // Register bean definition for DefaultJpaContext - RootBeanDefinition contextDefinition = new RootBeanDefinition(DefaultJpaContext.class); - contextDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR); + registerLazyIfNotAlreadyRegistered(() -> { - registerIfNotAlreadyRegistered(contextDefinition, registry, JPA_CONTEXT_BEAN_NAME, source); + RootBeanDefinition contextDefinition = new RootBeanDefinition(DefaultJpaContext.class); + contextDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR); + + return contextDefinition; + + }, registry, JPA_CONTEXT_BEAN_NAME, source); } /* diff --git a/src/main/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessor.java b/src/main/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessor.java index 985782be8..2b0756fb4 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessor.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessor.java @@ -88,6 +88,7 @@ public class EntityManagerBeanDefinitionRegistrarPostProcessor implements BeanFa emBeanDefinition.addQualifier(new AutowireCandidateQualifier(Qualifier.class, definition.getBeanName())); emBeanDefinition.setScope(definition.getBeanDefinition().getScope()); emBeanDefinition.setSource(definition.getBeanDefinition().getSource()); + emBeanDefinition.setLazyInit(true); BeanDefinitionReaderUtils.registerWithGeneratedName(emBeanDefinition, definitionRegistry); }