From f28528a527c9e64e17aa28ca0467337bdb29d750 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Tue, 14 Aug 2018 17:07:11 +0100 Subject: [PATCH] Allow Data JPA's bootstrap mode to be configured via the environment In Spring Data Lovelace, repositories' bootstrap mode can be configured via @EnableJpaRepositories. This commit adds support for configuring the mode via the environment rather than having to use the annotation. Additionally, when deferred or lazy bootstrapping is being used, the LocalContainerEntityManagerFactoryBean is configured to use a bootstrap executor. This allows JPA's initialization to be performed on a separate thread, allowing the rest of application context initialization to proceed in parallel. Closes gh-13833 --- ...tRepositoryConfigurationSourceSupport.java | 17 ++++ .../jpa/JpaRepositoriesAutoConfiguration.java | 37 +++++++- ...JpaRepositoriesAutoConfigureRegistrar.java | 27 ++++++ ...EntityManagerFactoryBuilderCustomizer.java | 37 ++++++++ .../orm/jpa/JpaBaseConfiguration.java | 8 +- ...itional-spring-configuration-metadata.json | 6 ++ ...JpaRepositoriesAutoConfigurationTests.java | 90 ++++++++++++------- .../appendix-application-properties.adoc | 1 + .../main/asciidoc/spring-boot-features.adoc | 6 ++ .../orm/jpa/EntityManagerFactoryBuilder.java | 17 ++++ .../src/main/resources/application.properties | 1 + 11 files changed, 211 insertions(+), 36 deletions(-) create mode 100644 spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/EntityManagerFactoryBuilderCustomizer.java diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/AbstractRepositoryConfigurationSourceSupport.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/AbstractRepositoryConfigurationSourceSupport.java index 5c91fb013d..7b77aaf79c 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/AbstractRepositoryConfigurationSourceSupport.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/AbstractRepositoryConfigurationSourceSupport.java @@ -31,6 +31,7 @@ import org.springframework.core.io.ResourceLoader; import org.springframework.core.type.AnnotationMetadata; import org.springframework.core.type.StandardAnnotationMetadata; import org.springframework.data.repository.config.AnnotationRepositoryConfigurationSource; +import org.springframework.data.repository.config.BootstrapMode; import org.springframework.data.repository.config.RepositoryConfigurationDelegate; import org.springframework.data.repository.config.RepositoryConfigurationExtension; import org.springframework.data.util.Streamable; @@ -72,6 +73,13 @@ public abstract class AbstractRepositoryConfigurationSourceSupport return AbstractRepositoryConfigurationSourceSupport.this .getBasePackages(); } + + @Override + public BootstrapMode getBootstrapMode() { + return AbstractRepositoryConfigurationSourceSupport.this + .getBootstrapMode(); + } + }; } @@ -97,6 +105,15 @@ public abstract class AbstractRepositoryConfigurationSourceSupport */ protected abstract RepositoryConfigurationExtension getRepositoryConfigurationExtension(); + /** + * The {@link BootstrapMode} for the particular repository support. Defaults to + * {@link BootstrapMode#DEFAULT}. + * @return the bootstrap mode + */ + protected BootstrapMode getBootstrapMode() { + return BootstrapMode.DEFAULT; + } + @Override public void setResourceLoader(ResourceLoader resourceLoader) { this.resourceLoader = resourceLoader; diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/jpa/JpaRepositoriesAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/jpa/JpaRepositoriesAutoConfiguration.java index 03dff256c2..09aaff4e33 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/jpa/JpaRepositoriesAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/jpa/JpaRepositoriesAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 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. @@ -18,15 +18,22 @@ package org.springframework.boot.autoconfigure.data.jpa; import javax.sql.DataSource; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.AnyNestedCondition; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.orm.jpa.EntityManagerFactoryBuilderCustomizer; import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration; +import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; +import org.springframework.core.task.AsyncTaskExecutor; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.data.jpa.repository.config.JpaRepositoryConfigExtension; @@ -58,7 +65,33 @@ import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean; JpaRepositoryConfigExtension.class }) @ConditionalOnProperty(prefix = "spring.data.jpa.repositories", name = "enabled", havingValue = "true", matchIfMissing = true) @Import(JpaRepositoriesAutoConfigureRegistrar.class) -@AutoConfigureAfter(HibernateJpaAutoConfiguration.class) +@AutoConfigureAfter({ HibernateJpaAutoConfiguration.class, + TaskExecutionAutoConfiguration.class }) public class JpaRepositoriesAutoConfiguration { + @Bean + @Conditional(BootstrapExecutorCondition.class) + public EntityManagerFactoryBuilderCustomizer entityManagerFactoryBoostrapExecutorCustomizer( + ObjectProvider taskExecutor) { + return (builder) -> builder.setBootstrapExecutor(taskExecutor.getIfAvailable()); + } + + private static final class BootstrapExecutorCondition extends AnyNestedCondition { + + BootstrapExecutorCondition() { + super(ConfigurationPhase.REGISTER_BEAN); + } + + @ConditionalOnProperty(prefix = "spring.data.jpa.repositories", name = "bootstrap-mode", havingValue = "deferred", matchIfMissing = false) + static class DeferredBootstrapMode { + + } + + @ConditionalOnProperty(prefix = "spring.data.jpa.repositories", name = "bootstrap-mode", havingValue = "lazy", matchIfMissing = false) + static class LazyBootstrapMode { + + } + + } + } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/jpa/JpaRepositoriesAutoConfigureRegistrar.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/jpa/JpaRepositoriesAutoConfigureRegistrar.java index ce1554b07b..72ccde37e6 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/jpa/JpaRepositoriesAutoConfigureRegistrar.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/jpa/JpaRepositoriesAutoConfigureRegistrar.java @@ -17,12 +17,16 @@ package org.springframework.boot.autoconfigure.data.jpa; import java.lang.annotation.Annotation; +import java.util.Locale; import org.springframework.boot.autoconfigure.data.AbstractRepositoryConfigurationSourceSupport; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; +import org.springframework.core.env.Environment; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.data.jpa.repository.config.JpaRepositoryConfigExtension; +import org.springframework.data.repository.config.BootstrapMode; import org.springframework.data.repository.config.RepositoryConfigurationExtension; +import org.springframework.util.StringUtils; /** * {@link ImportBeanDefinitionRegistrar} used to auto-configure Spring Data JPA @@ -34,6 +38,8 @@ import org.springframework.data.repository.config.RepositoryConfigurationExtensi class JpaRepositoriesAutoConfigureRegistrar extends AbstractRepositoryConfigurationSourceSupport { + private BootstrapMode bootstrapMode = null; + @Override protected Class getAnnotation() { return EnableJpaRepositories.class; @@ -49,6 +55,27 @@ class JpaRepositoriesAutoConfigureRegistrar return new JpaRepositoryConfigExtension(); } + @Override + protected BootstrapMode getBootstrapMode() { + return (this.bootstrapMode == null) ? super.getBootstrapMode() + : this.bootstrapMode; + } + + @Override + public void setEnvironment(Environment environment) { + super.setEnvironment(environment); + configureBootstrapMode(environment); + } + + private void configureBootstrapMode(Environment environment) { + String property = environment + .getProperty("spring.data.jpa.repositories.bootstrap-mode"); + if (StringUtils.hasText(property)) { + this.bootstrapMode = BootstrapMode + .valueOf(property.toUpperCase(Locale.ENGLISH)); + } + } + @EnableJpaRepositories private static class EnableJpaRepositoriesConfiguration { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/EntityManagerFactoryBuilderCustomizer.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/EntityManagerFactoryBuilderCustomizer.java new file mode 100644 index 0000000000..a4430839b0 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/EntityManagerFactoryBuilderCustomizer.java @@ -0,0 +1,37 @@ +/* + * Copyright 2012-2018 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.boot.autoconfigure.orm.jpa; + +import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder; + +/** + * Callback interface that can be used to customize the auto-configured + * {@link EntityManagerFactoryBuilder}. + * + * @author Andy Wilkinson + * @since 2.1.0 + */ +@FunctionalInterface +public interface EntityManagerFactoryBuilderCustomizer { + + /** + * Customize the given {@code builder}. + * @param builder the builder to customize + */ + void customize(EntityManagerFactoryBuilder builder); + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaBaseConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaBaseConfiguration.java index 0c64dbe30f..a207980387 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaBaseConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaBaseConfiguration.java @@ -16,6 +16,7 @@ package org.springframework.boot.autoconfigure.orm.jpa; +import java.util.Collections; import java.util.List; import java.util.Map; @@ -118,11 +119,16 @@ public abstract class JpaBaseConfiguration implements BeanFactoryAware { @ConditionalOnMissingBean public EntityManagerFactoryBuilder entityManagerFactoryBuilder( JpaVendorAdapter jpaVendorAdapter, - ObjectProvider persistenceUnitManager) { + ObjectProvider persistenceUnitManager, + ObjectProvider> customizers) { EntityManagerFactoryBuilder builder = new EntityManagerFactoryBuilder( jpaVendorAdapter, this.properties.getProperties(), persistenceUnitManager.getIfAvailable()); builder.setCallback(getVendorCallback()); + for (EntityManagerFactoryBuilderCustomizer customizer : customizers + .getIfAvailable(Collections::emptyList)) { + customizer.customize(builder); + } return builder; } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json index e6c5126dc3..4ca16adb4f 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -156,6 +156,12 @@ "description": "Whether to enable Elasticsearch repositories.", "defaultValue": true }, + { + "name": "spring.data.jpa.repositories.bootstrap-mode", + "type": "org.springframework.data.repository.config.BootstrapMode", + "description": "Bootstrap mode for JPA repositories.", + "defaultValue": "default" + }, { "name": "spring.data.jpa.repositories.enabled", "type": "java.lang.Boolean", diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/jpa/JpaRepositoriesAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/jpa/JpaRepositoriesAutoConfigurationTests.java index 3d547cb049..2865daca97 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/jpa/JpaRepositoriesAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/jpa/JpaRepositoriesAutoConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 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. @@ -18,23 +18,25 @@ package org.springframework.boot.autoconfigure.data.jpa; import javax.persistence.EntityManagerFactory; -import org.junit.After; import org.junit.Test; -import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.TestAutoConfigurationPackage; import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; +import org.springframework.boot.autoconfigure.data.alt.jpa.CityJpaRepository; import org.springframework.boot.autoconfigure.data.alt.mongo.CityMongoDbRepository; import org.springframework.boot.autoconfigure.data.alt.solr.CitySolrRepository; import org.springframework.boot.autoconfigure.data.jpa.city.City; import org.springframework.boot.autoconfigure.data.jpa.city.CityRepository; import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration; import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration; -import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.FilterType; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.transaction.PlatformTransactionManager; import static org.assertj.core.api.Assertions.assertThat; @@ -47,47 +49,69 @@ import static org.assertj.core.api.Assertions.assertThat; */ public class JpaRepositoriesAutoConfigurationTests { - private AnnotationConfigApplicationContext context; - - @After - public void close() { - this.context.close(); - } + private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(HibernateJpaAutoConfiguration.class, + JpaRepositoriesAutoConfiguration.class, + PropertyPlaceholderAutoConfiguration.class, + TaskExecutionAutoConfiguration.class)) + .withUserConfiguration(EmbeddedDataSourceConfiguration.class); @Test public void testDefaultRepositoryConfiguration() { - prepareApplicationContext(TestConfiguration.class); - - assertThat(this.context.getBean(CityRepository.class)).isNotNull(); - assertThat(this.context.getBean(PlatformTransactionManager.class)).isNotNull(); - assertThat(this.context.getBean(EntityManagerFactory.class)).isNotNull(); + this.contextRunner.withUserConfiguration(TestConfiguration.class) + .run((context) -> { + assertThat(context).hasSingleBean(CityRepository.class); + assertThat(context).hasSingleBean(PlatformTransactionManager.class); + assertThat(context).hasSingleBean(EntityManagerFactory.class); + assertThat( + context.getBean(LocalContainerEntityManagerFactoryBean.class) + .getBootstrapExecutor()).isNull(); + }); } @Test public void testOverrideRepositoryConfiguration() { - prepareApplicationContext(CustomConfiguration.class); - assertThat(this.context.getBean( - org.springframework.boot.autoconfigure.data.alt.jpa.CityJpaRepository.class)) - .isNotNull(); - assertThat(this.context.getBean(PlatformTransactionManager.class)).isNotNull(); - assertThat(this.context.getBean(EntityManagerFactory.class)).isNotNull(); + this.contextRunner.withUserConfiguration(CustomConfiguration.class) + .run((context) -> { + assertThat(context).hasSingleBean(CityJpaRepository.class); + assertThat(context).hasSingleBean(PlatformTransactionManager.class); + assertThat(context).hasSingleBean(EntityManagerFactory.class); + }); } - @Test(expected = NoSuchBeanDefinitionException.class) + @Test public void autoConfigurationShouldNotKickInEvenIfManualConfigDidNotCreateAnyRepositories() { - prepareApplicationContext(SortOfInvalidCustomConfiguration.class); - - this.context.getBean(CityRepository.class); + this.contextRunner.withUserConfiguration(SortOfInvalidCustomConfiguration.class) + .run((context) -> assertThat(context) + .doesNotHaveBean(CityRepository.class)); } - private void prepareApplicationContext(Class... configurationClasses) { - this.context = new AnnotationConfigApplicationContext(); - this.context.register(configurationClasses); - this.context.register(EmbeddedDataSourceConfiguration.class, - HibernateJpaAutoConfiguration.class, - JpaRepositoriesAutoConfiguration.class, - PropertyPlaceholderAutoConfiguration.class); - this.context.refresh(); + @Test + public void whenBootstrappingModeIsLazyBoostrapExecutorIsConfigured() { + this.contextRunner.withUserConfiguration(TestConfiguration.class) + .withPropertyValues("spring.data.jpa.repositories.bootstrap-mode=lazy") + .run((context) -> assertThat( + context.getBean(LocalContainerEntityManagerFactoryBean.class) + .getBootstrapExecutor()).isNotNull()); + } + + @Test + public void whenBootstrappingModeIsDeferredBoostrapExecutorIsConfigured() { + this.contextRunner.withUserConfiguration(TestConfiguration.class) + .withPropertyValues( + "spring.data.jpa.repositories.bootstrap-mode=deferred") + .run((context) -> assertThat( + context.getBean(LocalContainerEntityManagerFactoryBean.class) + .getBootstrapExecutor()).isNotNull()); + } + + @Test + public void whenBootstrappingModeIsDefaultBoostrapExecutorIsNotConfigured() { + this.contextRunner.withUserConfiguration(TestConfiguration.class) + .withPropertyValues("spring.data.jpa.repositories.bootstrap-mode=default") + .run((context) -> assertThat( + context.getBean(LocalContainerEntityManagerFactoryBean.class) + .getBootstrapExecutor()).isNull()); } @Configuration diff --git a/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc b/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc index 46e95da908..6d319c696e 100644 --- a/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc +++ b/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc @@ -778,6 +778,7 @@ content into your application. Rather, pick only the properties that you need. spring.jdbc.template.query-timeout= # Query timeout. Default is to use the JDBC driver's default configuration. If a duration suffix is not specified, seconds will be used. # JPA ({sc-spring-boot-autoconfigure}/orm/jpa/JpaBaseConfiguration.{sc-ext}[JpaBaseConfiguration], {sc-spring-boot-autoconfigure}/orm/jpa/HibernateJpaAutoConfiguration.{sc-ext}[HibernateJpaAutoConfiguration]) + spring.data.jpa.repositories.boostrap-mode=default # Bootstrap mode for JPA repositories. spring.data.jpa.repositories.enabled=true # Whether to enable JPA repositories. spring.jpa.database= # Target database to operate on, auto-detected by default. Can be alternatively set using the "databasePlatform" property. spring.jpa.database-platform= # Name of the target database to operate on, auto-detected by default. Can be alternatively set using the "Database" enum. diff --git a/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc b/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc index 2c61a849be..f9b58872e0 100644 --- a/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc +++ b/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc @@ -3694,6 +3694,12 @@ The following example shows a typical Spring Data repository interface definitio } ---- +Spring Data JPA repositories support three different modes of bootstrapping: default, +deferred, and lazy. To enable deferred or lazy bootstrapping, set the +`spring.data.jpa.repositories.bootstrap-mode` to `deferred` or `lazy` respectively. When +using deferred or lazy bootstrapping, the auto-configured `EntityManagerFactoryBuilder` +will use the context's async task executor, if any, as the bootstrap executor. + TIP: We have barely scratched the surface of Spring Data JPA. For complete details, see the https://docs.spring.io/spring-data/jpa/docs/current/reference/html/[Spring Data JPA reference documentation]. diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/orm/jpa/EntityManagerFactoryBuilder.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/orm/jpa/EntityManagerFactoryBuilder.java index e4fbf821ec..562e800186 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/orm/jpa/EntityManagerFactoryBuilder.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/orm/jpa/EntityManagerFactoryBuilder.java @@ -25,6 +25,7 @@ import java.util.Set; import javax.sql.DataSource; +import org.springframework.core.task.AsyncTaskExecutor; import org.springframework.orm.jpa.JpaVendorAdapter; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.orm.jpa.persistenceunit.PersistenceUnitManager; @@ -55,6 +56,8 @@ public class EntityManagerFactoryBuilder { private final URL persistenceUnitRootLocation; + private AsyncTaskExecutor bootstrapExecutor; + private EntityManagerFactoryBeanCallback callback; /** @@ -94,6 +97,16 @@ public class EntityManagerFactoryBuilder { return new Builder(dataSource); } + /** + * Configure the bootstrap executor to be used by the + * {@link LocalContainerEntityManagerFactoryBean}. + * @param bootstrapExecutor the executor + * @since 2.1.0 + */ + public void setBootstrapExecutor(AsyncTaskExecutor bootstrapExecutor) { + this.bootstrapExecutor = bootstrapExecutor; + } + /** * An optional callback for new entity manager factory beans. * @param callback the entity manager factory bean callback @@ -230,6 +243,10 @@ public class EntityManagerFactoryBuilder { entityManagerFactoryBean .setPersistenceUnitRootLocation(rootLocation.toString()); } + if (EntityManagerFactoryBuilder.this.bootstrapExecutor != null) { + entityManagerFactoryBean.setBootstrapExecutor( + EntityManagerFactoryBuilder.this.bootstrapExecutor); + } if (EntityManagerFactoryBuilder.this.callback != null) { EntityManagerFactoryBuilder.this.callback .execute(entityManagerFactoryBean); diff --git a/spring-boot-samples/spring-boot-sample-data-jpa/src/main/resources/application.properties b/spring-boot-samples/spring-boot-sample-data-jpa/src/main/resources/application.properties index 15b2174d3b..265a5e9a85 100644 --- a/spring-boot-samples/spring-boot-sample-data-jpa/src/main/resources/application.properties +++ b/spring-boot-samples/spring-boot-sample-data-jpa/src/main/resources/application.properties @@ -1,3 +1,4 @@ spring.h2.console.enabled=true spring.jpa.open-in-view=true +spring.data.jpa.repositories.bootstrap-mode=default logging.level.org.hibernate.SQL=debug