diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaBaseConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaBaseConfiguration.java index 538ac5c0a4f0..1d026dfa74eb 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaBaseConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaBaseConfiguration.java @@ -44,6 +44,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Primary; +import org.springframework.core.task.AsyncTaskExecutor; import org.springframework.orm.jpa.JpaTransactionManager; import org.springframework.orm.jpa.JpaVendorAdapter; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; @@ -107,11 +108,13 @@ public JpaVendorAdapter jpaVendorAdapter() { @ConditionalOnMissingBean public EntityManagerFactoryBuilder entityManagerFactoryBuilder( JpaVendorAdapter jpaVendorAdapter, - ObjectProvider persistenceUnitManagerProvider) { + ObjectProvider persistenceUnitManagerProvider, + ObjectProvider bootstrapExecutorProvider) { EntityManagerFactoryBuilder builder = new EntityManagerFactoryBuilder( jpaVendorAdapter, this.properties.getProperties(), persistenceUnitManagerProvider.getIfAvailable(), - determinePersistenceUnitRootLocation()); + determinePersistenceUnitRootLocation(), + bootstrapExecutorProvider.getIfUnique()); builder.setCallback(getVendorCallback()); return builder; } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/AbstractJpaAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/AbstractJpaAutoConfigurationTests.java index dc079933bf72..ea6bf76804d6 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/AbstractJpaAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/AbstractJpaAutoConfigurationTests.java @@ -41,6 +41,8 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.core.task.AsyncTaskExecutor; import org.springframework.orm.jpa.JpaTransactionManager; import org.springframework.orm.jpa.JpaVendorAdapter; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; @@ -48,6 +50,8 @@ import org.springframework.orm.jpa.persistenceunit.PersistenceUnitManager; import org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter; import org.springframework.orm.jpa.support.OpenEntityManagerInViewInterceptor; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.test.util.ReflectionTestUtils; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; @@ -58,6 +62,7 @@ * * @author Phillip Webb * @author Dave Syer + * @author Vedran Pavic */ public abstract class AbstractJpaAutoConfigurationTests { @@ -196,6 +201,41 @@ public void customPersistenceUnitManager() throws Exception { .isEqualTo(this.context.getBean(PersistenceUnitManager.class)); } + @Test + public void withTaskExecutor() { + setupTestConfiguration(TestConfigurationWithTaskExecutor.class); + this.context.refresh(); + LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = this.context + .getBean(LocalContainerEntityManagerFactoryBean.class); + AsyncTaskExecutor bootstrapExecutor = (AsyncTaskExecutor) ReflectionTestUtils + .getField(entityManagerFactoryBean, "bootstrapExecutor"); + assertThat(bootstrapExecutor).isEqualTo( + this.context.getBean(AsyncTaskExecutor.class)); + } + + @Test + public void withMultipleTaskExecutors() { + setupTestConfiguration(TestConfigurationWithMultipleTaskExecutors.class); + this.context.refresh(); + LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = this.context + .getBean(LocalContainerEntityManagerFactoryBean.class); + AsyncTaskExecutor bootstrapExecutor = (AsyncTaskExecutor) ReflectionTestUtils + .getField(entityManagerFactoryBean, "bootstrapExecutor"); + assertThat(bootstrapExecutor).isNull(); + } + + @Test + public void withPrimaryTaskExecutor() { + setupTestConfiguration(TestConfigurationWithPrimaryTaskExecutor.class); + this.context.refresh(); + LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = this.context + .getBean(LocalContainerEntityManagerFactoryBean.class); + AsyncTaskExecutor bootstrapExecutor = (AsyncTaskExecutor) ReflectionTestUtils + .getField(entityManagerFactoryBean, "bootstrapExecutor"); + assertThat(bootstrapExecutor).isEqualTo( + this.context.getBean("taskExecutorOne", AsyncTaskExecutor.class)); + } + protected void setupTestConfiguration() { setupTestConfiguration(TestConfiguration.class); } @@ -306,6 +346,47 @@ public PersistenceUnitManager persistenceUnitManager() { } + @Configuration + protected static class TestConfigurationWithTaskExecutor extends TestConfiguration { + + @Bean + public ThreadPoolTaskExecutor taskExecutor() { + return new ThreadPoolTaskExecutor(); + } + + } + + @Configuration + protected static class TestConfigurationWithMultipleTaskExecutors extends TestConfiguration { + + @Bean + public ThreadPoolTaskExecutor taskExecutorOne() { + return new ThreadPoolTaskExecutor(); + } + + @Bean + public ThreadPoolTaskExecutor taskExecutorTwo() { + return new ThreadPoolTaskExecutor(); + } + + } + + @Configuration + protected static class TestConfigurationWithPrimaryTaskExecutor extends TestConfiguration { + + @Bean + @Primary + public ThreadPoolTaskExecutor taskExecutorOne() { + return new ThreadPoolTaskExecutor(); + } + + @Bean + public ThreadPoolTaskExecutor taskExecutorTwo() { + return new ThreadPoolTaskExecutor(); + } + + } + @SuppressWarnings("serial") static class CustomJpaTransactionManager extends JpaTransactionManager { } diff --git a/spring-boot/src/main/java/org/springframework/boot/orm/jpa/EntityManagerFactoryBuilder.java b/spring-boot/src/main/java/org/springframework/boot/orm/jpa/EntityManagerFactoryBuilder.java index ca40524dede4..32c07fdd2d93 100644 --- a/spring-boot/src/main/java/org/springframework/boot/orm/jpa/EntityManagerFactoryBuilder.java +++ b/spring-boot/src/main/java/org/springframework/boot/orm/jpa/EntityManagerFactoryBuilder.java @@ -25,6 +25,7 @@ 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; @@ -41,6 +42,7 @@ * @author Dave Syer * @author Phillip Webb * @author Stephane Nicoll + * @author Vedran Pavic * @since 1.3.0 */ public class EntityManagerFactoryBuilder { @@ -53,6 +55,8 @@ public class EntityManagerFactoryBuilder { private final URL persistenceUnitRootLocation; + private final AsyncTaskExecutor bootstrapExecutor; + private EntityManagerFactoryBeanCallback callback; /** @@ -82,10 +86,31 @@ public EntityManagerFactoryBuilder(JpaVendorAdapter jpaVendorAdapter, public EntityManagerFactoryBuilder(JpaVendorAdapter jpaVendorAdapter, Map jpaProperties, PersistenceUnitManager persistenceUnitManager, URL persistenceUnitRootLocation) { + this(jpaVendorAdapter, jpaProperties, persistenceUnitManager, + persistenceUnitRootLocation, null); + } + + /** + * Create a new instance passing in the common pieces that will be shared if multiple + * EntityManagerFactory instances are created. + * @param jpaVendorAdapter a vendor adapter + * @param jpaProperties JPA properties to be passed to the persistence provider. + * @param persistenceUnitManager optional source of persistence unit information (can + * be null) + * @param persistenceUnitRootLocation the persistence unit root location to use as a + * fallback (can be null) + * @param bootstrapExecutor optional asynchronous executor for background + * bootstrapping (can be null) + * @since 1.5.0 + */ + public EntityManagerFactoryBuilder(JpaVendorAdapter jpaVendorAdapter, + Map jpaProperties, PersistenceUnitManager persistenceUnitManager, + URL persistenceUnitRootLocation, AsyncTaskExecutor bootstrapExecutor) { this.jpaVendorAdapter = jpaVendorAdapter; this.persistenceUnitManager = persistenceUnitManager; this.jpaProperties = new LinkedHashMap(jpaProperties); this.persistenceUnitRootLocation = persistenceUnitRootLocation; + this.bootstrapExecutor = bootstrapExecutor; } public Builder dataSource(DataSource dataSource) { @@ -192,7 +217,10 @@ public LocalContainerEntityManagerFactoryBean build() { } entityManagerFactoryBean.setJpaVendorAdapter( EntityManagerFactoryBuilder.this.jpaVendorAdapter); - + if (EntityManagerFactoryBuilder.this.bootstrapExecutor != null) { + entityManagerFactoryBean.setBootstrapExecutor( + EntityManagerFactoryBuilder.this.bootstrapExecutor); + } if (this.jta) { entityManagerFactoryBean.setJtaDataSource(this.dataSource); }