Browse Source

Fix JPA bootstrap executor detection with multiple candidates

Spring Boot provides two separate auto-configurations that can
potentially expose an `AsyncTaskExecutor` implementation so relying on
the presence of a single instance is too weak.

This commit fixes the detection of the AsyncTaskExecutor that can be
used to bootstrap JPA so that a single instance is used and, in the case
more than one exists, the one named `applicationTaskExecutor`.

Closes gh-15447
pull/15692/head
Stephane Nicoll 7 years ago
parent
commit
830da28b8c
  1. 24
      spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/jpa/JpaRepositoriesAutoConfiguration.java
  2. 65
      spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/jpa/JpaRepositoriesAutoConfigurationTests.java
  3. 3
      spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc

24
spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/jpa/JpaRepositoriesAutoConfiguration.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2012-2018 the original author or authors.
* Copyright 2012-2019 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.
@ -16,9 +16,10 @@ @@ -16,9 +16,10 @@
package org.springframework.boot.autoconfigure.data.jpa;
import java.util.Map;
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;
@ -72,8 +73,23 @@ public class JpaRepositoriesAutoConfiguration { @@ -72,8 +73,23 @@ public class JpaRepositoriesAutoConfiguration {
@Bean
@Conditional(BootstrapExecutorCondition.class)
public EntityManagerFactoryBuilderCustomizer entityManagerFactoryBootstrapExecutorCustomizer(
ObjectProvider<AsyncTaskExecutor> taskExecutor) {
return (builder) -> builder.setBootstrapExecutor(taskExecutor.getIfAvailable());
Map<String, AsyncTaskExecutor> taskExecutors) {
return (builder) -> {
AsyncTaskExecutor bootstrapExecutor = determineBootstrapExecutor(
taskExecutors);
if (bootstrapExecutor != null) {
builder.setBootstrapExecutor(bootstrapExecutor);
}
};
}
private AsyncTaskExecutor determineBootstrapExecutor(
Map<String, AsyncTaskExecutor> taskExecutors) {
if (taskExecutors.size() == 1) {
return taskExecutors.values().iterator().next();
}
return taskExecutors
.get(TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME);
}
private static final class BootstrapExecutorCondition extends AnyNestedCondition {

65
spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/jpa/JpaRepositoriesAutoConfigurationTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2012-2018 the original author or authors.
* Copyright 2012-2019 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.
@ -31,12 +31,17 @@ import org.springframework.boot.autoconfigure.data.jpa.city.CityRepository; @@ -31,12 +31,17 @@ 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.boot.autoconfigure.task.TaskExecutionAutoConfiguration;
import org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.context.annotation.Import;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.transaction.PlatformTransactionManager;
import static org.assertj.core.api.Assertions.assertThat;
@ -52,8 +57,7 @@ public class JpaRepositoriesAutoConfigurationTests { @@ -52,8 +57,7 @@ public class JpaRepositoriesAutoConfigurationTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(HibernateJpaAutoConfiguration.class,
JpaRepositoriesAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class,
TaskExecutionAutoConfiguration.class))
PropertyPlaceholderAutoConfiguration.class))
.withUserConfiguration(EmbeddedDataSourceConfiguration.class);
@Test
@ -87,33 +91,76 @@ public class JpaRepositoriesAutoConfigurationTests { @@ -87,33 +91,76 @@ public class JpaRepositoriesAutoConfigurationTests {
}
@Test
public void whenBootstrappingModeIsLazyBootstrapExecutorIsConfigured() {
this.contextRunner.withUserConfiguration(TestConfiguration.class)
public void whenBootstrappingModeIsLazyWithMultipleAsyncExecutorBootstrapExecutorIsConfigured() {
this.contextRunner
.withUserConfiguration(MultipleAsyncTaskExecutorConfiguration.class)
.withConfiguration(
AutoConfigurations.of(TaskExecutionAutoConfiguration.class,
TaskSchedulingAutoConfiguration.class))
.withPropertyValues("spring.data.jpa.repositories.bootstrap-mode=lazy")
.run((context) -> assertThat(
context.getBean(LocalContainerEntityManagerFactoryBean.class)
.getBootstrapExecutor()).isNotNull());
.getBootstrapExecutor()).isEqualTo(
context.getBean("applicationTaskExecutor")));
}
@Test
public void whenBootstrappingModeIsLazyWithSingleAsyncExecutorBootstrapExecutorIsConfigured() {
this.contextRunner
.withUserConfiguration(SingleAsyncTaskExecutorConfiguration.class)
.withPropertyValues("spring.data.jpa.repositories.bootstrap-mode=lazy")
.run((context) -> assertThat(
context.getBean(LocalContainerEntityManagerFactoryBean.class)
.getBootstrapExecutor()).isEqualTo(
context.getBean("testAsyncTaskExecutor")));
}
@Test
public void whenBootstrappingModeIsDeferredBootstrapExecutorIsConfigured() {
this.contextRunner.withUserConfiguration(TestConfiguration.class)
this.contextRunner
.withUserConfiguration(MultipleAsyncTaskExecutorConfiguration.class)
.withConfiguration(
AutoConfigurations.of(TaskExecutionAutoConfiguration.class,
TaskSchedulingAutoConfiguration.class))
.withPropertyValues(
"spring.data.jpa.repositories.bootstrap-mode=deferred")
.run((context) -> assertThat(
context.getBean(LocalContainerEntityManagerFactoryBean.class)
.getBootstrapExecutor()).isNotNull());
.getBootstrapExecutor()).isEqualTo(
context.getBean("applicationTaskExecutor")));
}
@Test
public void whenBootstrappingModeIsDefaultBootstrapExecutorIsNotConfigured() {
this.contextRunner.withUserConfiguration(TestConfiguration.class)
this.contextRunner
.withUserConfiguration(MultipleAsyncTaskExecutorConfiguration.class)
.withConfiguration(
AutoConfigurations.of(TaskExecutionAutoConfiguration.class,
TaskSchedulingAutoConfiguration.class))
.withPropertyValues("spring.data.jpa.repositories.bootstrap-mode=default")
.run((context) -> assertThat(
context.getBean(LocalContainerEntityManagerFactoryBean.class)
.getBootstrapExecutor()).isNull());
}
@Configuration
@EnableScheduling
@Import(TestConfiguration.class)
protected static class MultipleAsyncTaskExecutorConfiguration {
}
@Configuration
@Import(TestConfiguration.class)
protected static class SingleAsyncTaskExecutorConfiguration {
@Bean
public SimpleAsyncTaskExecutor testAsyncTaskExecutor() {
return new SimpleAsyncTaskExecutor();
}
}
@Configuration
@TestAutoConfigurationPackage(City.class)
protected static class TestConfiguration {

3
spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc

@ -3840,7 +3840,8 @@ Spring Data JPA repositories support three different modes of bootstrapping: def @@ -3840,7 +3840,8 @@ Spring Data JPA repositories support three different modes of bootstrapping: def
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.
will use the context's `AsyncTaskExecutor`, if any, as the bootstrap executor. If more
than one exists, the one named `applicationTaskExecutor` will be used.
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

Loading…
Cancel
Save