Skip to content

Commit 830da28

Browse files
committed
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
1 parent d499277 commit 830da28

File tree

3 files changed

+78
-14
lines changed

3 files changed

+78
-14
lines changed

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

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2018 the original author or authors.
2+
* Copyright 2012-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,9 +16,10 @@
1616

1717
package org.springframework.boot.autoconfigure.data.jpa;
1818

19+
import java.util.Map;
20+
1921
import javax.sql.DataSource;
2022

21-
import org.springframework.beans.factory.ObjectProvider;
2223
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
2324
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
2425
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
@@ -72,8 +73,23 @@ public class JpaRepositoriesAutoConfiguration {
7273
@Bean
7374
@Conditional(BootstrapExecutorCondition.class)
7475
public EntityManagerFactoryBuilderCustomizer entityManagerFactoryBootstrapExecutorCustomizer(
75-
ObjectProvider<AsyncTaskExecutor> taskExecutor) {
76-
return (builder) -> builder.setBootstrapExecutor(taskExecutor.getIfAvailable());
76+
Map<String, AsyncTaskExecutor> taskExecutors) {
77+
return (builder) -> {
78+
AsyncTaskExecutor bootstrapExecutor = determineBootstrapExecutor(
79+
taskExecutors);
80+
if (bootstrapExecutor != null) {
81+
builder.setBootstrapExecutor(bootstrapExecutor);
82+
}
83+
};
84+
}
85+
86+
private AsyncTaskExecutor determineBootstrapExecutor(
87+
Map<String, AsyncTaskExecutor> taskExecutors) {
88+
if (taskExecutors.size() == 1) {
89+
return taskExecutors.values().iterator().next();
90+
}
91+
return taskExecutors
92+
.get(TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME);
7793
}
7894

7995
private static final class BootstrapExecutorCondition extends AnyNestedCondition {

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

Lines changed: 56 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2018 the original author or authors.
2+
* Copyright 2012-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -31,12 +31,17 @@
3131
import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration;
3232
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
3333
import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration;
34+
import org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration;
3435
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
36+
import org.springframework.context.annotation.Bean;
3537
import org.springframework.context.annotation.ComponentScan.Filter;
3638
import org.springframework.context.annotation.Configuration;
3739
import org.springframework.context.annotation.FilterType;
40+
import org.springframework.context.annotation.Import;
41+
import org.springframework.core.task.SimpleAsyncTaskExecutor;
3842
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
3943
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
44+
import org.springframework.scheduling.annotation.EnableScheduling;
4045
import org.springframework.transaction.PlatformTransactionManager;
4146

4247
import static org.assertj.core.api.Assertions.assertThat;
@@ -52,8 +57,7 @@ public class JpaRepositoriesAutoConfigurationTests {
5257
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
5358
.withConfiguration(AutoConfigurations.of(HibernateJpaAutoConfiguration.class,
5459
JpaRepositoriesAutoConfiguration.class,
55-
PropertyPlaceholderAutoConfiguration.class,
56-
TaskExecutionAutoConfiguration.class))
60+
PropertyPlaceholderAutoConfiguration.class))
5761
.withUserConfiguration(EmbeddedDataSourceConfiguration.class);
5862

5963
@Test
@@ -87,33 +91,76 @@ public void autoConfigurationShouldNotKickInEvenIfManualConfigDidNotCreateAnyRep
8791
}
8892

8993
@Test
90-
public void whenBootstrappingModeIsLazyBootstrapExecutorIsConfigured() {
91-
this.contextRunner.withUserConfiguration(TestConfiguration.class)
94+
public void whenBootstrappingModeIsLazyWithMultipleAsyncExecutorBootstrapExecutorIsConfigured() {
95+
this.contextRunner
96+
.withUserConfiguration(MultipleAsyncTaskExecutorConfiguration.class)
97+
.withConfiguration(
98+
AutoConfigurations.of(TaskExecutionAutoConfiguration.class,
99+
TaskSchedulingAutoConfiguration.class))
92100
.withPropertyValues("spring.data.jpa.repositories.bootstrap-mode=lazy")
93101
.run((context) -> assertThat(
94102
context.getBean(LocalContainerEntityManagerFactoryBean.class)
95-
.getBootstrapExecutor()).isNotNull());
103+
.getBootstrapExecutor()).isEqualTo(
104+
context.getBean("applicationTaskExecutor")));
105+
}
106+
107+
@Test
108+
public void whenBootstrappingModeIsLazyWithSingleAsyncExecutorBootstrapExecutorIsConfigured() {
109+
this.contextRunner
110+
.withUserConfiguration(SingleAsyncTaskExecutorConfiguration.class)
111+
.withPropertyValues("spring.data.jpa.repositories.bootstrap-mode=lazy")
112+
.run((context) -> assertThat(
113+
context.getBean(LocalContainerEntityManagerFactoryBean.class)
114+
.getBootstrapExecutor()).isEqualTo(
115+
context.getBean("testAsyncTaskExecutor")));
96116
}
97117

98118
@Test
99119
public void whenBootstrappingModeIsDeferredBootstrapExecutorIsConfigured() {
100-
this.contextRunner.withUserConfiguration(TestConfiguration.class)
120+
this.contextRunner
121+
.withUserConfiguration(MultipleAsyncTaskExecutorConfiguration.class)
122+
.withConfiguration(
123+
AutoConfigurations.of(TaskExecutionAutoConfiguration.class,
124+
TaskSchedulingAutoConfiguration.class))
101125
.withPropertyValues(
102126
"spring.data.jpa.repositories.bootstrap-mode=deferred")
103127
.run((context) -> assertThat(
104128
context.getBean(LocalContainerEntityManagerFactoryBean.class)
105-
.getBootstrapExecutor()).isNotNull());
129+
.getBootstrapExecutor()).isEqualTo(
130+
context.getBean("applicationTaskExecutor")));
106131
}
107132

108133
@Test
109134
public void whenBootstrappingModeIsDefaultBootstrapExecutorIsNotConfigured() {
110-
this.contextRunner.withUserConfiguration(TestConfiguration.class)
135+
this.contextRunner
136+
.withUserConfiguration(MultipleAsyncTaskExecutorConfiguration.class)
137+
.withConfiguration(
138+
AutoConfigurations.of(TaskExecutionAutoConfiguration.class,
139+
TaskSchedulingAutoConfiguration.class))
111140
.withPropertyValues("spring.data.jpa.repositories.bootstrap-mode=default")
112141
.run((context) -> assertThat(
113142
context.getBean(LocalContainerEntityManagerFactoryBean.class)
114143
.getBootstrapExecutor()).isNull());
115144
}
116145

146+
@Configuration
147+
@EnableScheduling
148+
@Import(TestConfiguration.class)
149+
protected static class MultipleAsyncTaskExecutorConfiguration {
150+
151+
}
152+
153+
@Configuration
154+
@Import(TestConfiguration.class)
155+
protected static class SingleAsyncTaskExecutorConfiguration {
156+
157+
@Bean
158+
public SimpleAsyncTaskExecutor testAsyncTaskExecutor() {
159+
return new SimpleAsyncTaskExecutor();
160+
}
161+
162+
}
163+
117164
@Configuration
118165
@TestAutoConfigurationPackage(City.class)
119166
protected static class TestConfiguration {

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3840,7 +3840,8 @@ Spring Data JPA repositories support three different modes of bootstrapping: def
38403840
deferred, and lazy. To enable deferred or lazy bootstrapping, set the
38413841
`spring.data.jpa.repositories.bootstrap-mode` to `deferred` or `lazy` respectively. When
38423842
using deferred or lazy bootstrapping, the auto-configured `EntityManagerFactoryBuilder`
3843-
will use the context's async task executor, if any, as the bootstrap executor.
3843+
will use the context's `AsyncTaskExecutor`, if any, as the bootstrap executor. If more
3844+
than one exists, the one named `applicationTaskExecutor` will be used.
38443845

38453846
TIP: We have barely scratched the surface of Spring Data JPA. For complete details, see
38463847
the https://docs.spring.io/spring-data/jpa/docs/current/reference/html/[Spring Data JPA

0 commit comments

Comments
 (0)