mirror of
https://github.com/spring-projects/spring-boot.git
synced 2026-09-26 03:19:02 +00:00
Add 'spring.jpa.bootstrap' property
Add a `spring.jpa.bootstrap` property that can be set to `async` to switch on background bootstrapping in classic JPA setup. Closes gh-49733
This commit is contained in:
+91
@@ -67,6 +67,8 @@ import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.autoconfigure.TestAutoConfigurationPackage;
|
||||
import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration;
|
||||
import org.springframework.boot.diagnostics.FailureAnalyzedException;
|
||||
import org.springframework.boot.flyway.autoconfigure.FlywayAutoConfiguration;
|
||||
import org.springframework.boot.hibernate.SpringImplicitNamingStrategy;
|
||||
import org.springframework.boot.hibernate.autoconfigure.HibernateJpaAutoConfigurationTests.JpaUsingApplicationListenerConfiguration.EventCapturingApplicationListener;
|
||||
@@ -102,6 +104,7 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.context.event.ContextRefreshedEvent;
|
||||
import org.springframework.core.task.SimpleAsyncTaskExecutor;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.support.SQLExceptionTranslator;
|
||||
import org.springframework.jdbc.support.SQLStateSQLExceptionTranslator;
|
||||
@@ -247,6 +250,59 @@ class HibernateJpaAutoConfigurationTests {
|
||||
.run((context) -> assertThat(context).doesNotHaveBean(OpenEntityManagerInViewInterceptor.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenBackgroundBootstrapingAndSingleAsyncTaksExecutorConfiguresBackgroundExecutor() {
|
||||
this.contextRunner.withPropertyValues("spring.jpa.bootstrap=async")
|
||||
.withUserConfiguration(SingleAsyncTaskExecutorConfiguration.class)
|
||||
.run((context) -> assertThat(
|
||||
context.getBean(LocalContainerEntityManagerFactoryBean.class).getBootstrapExecutor())
|
||||
.isInstanceOf(SimpleAsyncTaskExecutor.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenBackgroundBootstrapingAndApplicationTaksExecutorConfiguresBackgroundExecutor() {
|
||||
this.contextRunner.withPropertyValues("spring.jpa.bootstrap=async")
|
||||
.withUserConfiguration(MultipleAsyncTaskExecutorsConfiguration.class,
|
||||
ApplicationTaskExecutorConfiguration.class)
|
||||
.run((context) -> assertThat(
|
||||
context.getBean(LocalContainerEntityManagerFactoryBean.class).getBootstrapExecutor())
|
||||
.isSameAs(context.getBean(TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenBackgroundBootstrapingAndMissingTaksExecutorThrowsException() {
|
||||
this.contextRunner.withPropertyValues("spring.jpa.bootstrap=async")
|
||||
.run((context) -> assertThat(context).getFailure()
|
||||
.rootCause()
|
||||
.isInstanceOf(FailureAnalyzedException.class)
|
||||
.message()
|
||||
.contains("bootstrap executor is required when 'spring.jpa.bootstrap' is set to 'async'"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenBackgroundBootstrapingAndMultipleTaksExecutorThrowsException() {
|
||||
this.contextRunner.withPropertyValues("spring.jpa.bootstrap=async")
|
||||
.withUserConfiguration(MultipleAsyncTaskExecutorsConfiguration.class)
|
||||
.run((context) -> assertThat(context).getFailure()
|
||||
.rootCause()
|
||||
.isInstanceOf(FailureAnalyzedException.class)
|
||||
.message()
|
||||
.contains("bootstrap executor is required when 'spring.jpa.bootstrap' is set to 'async'"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenBackgroundBootstrapingAndCustomizedBackgroundExecutorThrowsException() {
|
||||
this.contextRunner.withPropertyValues("spring.jpa.bootstrap=async")
|
||||
.withBean(EntityManagerFactoryBuilderCustomizer.class, this::bootstrapExecutorCustomizer)
|
||||
.run((context) -> assertThat(
|
||||
context.getBean(LocalContainerEntityManagerFactoryBean.class).getBootstrapExecutor())
|
||||
.isInstanceOf(SimpleAsyncTaskExecutor.class));
|
||||
}
|
||||
|
||||
private EntityManagerFactoryBuilderCustomizer bootstrapExecutorCustomizer() {
|
||||
return (builder) -> builder.setBootstrapExecutor(new SimpleAsyncTaskExecutor());
|
||||
}
|
||||
|
||||
@Test
|
||||
void customJpaProperties() {
|
||||
this.contextRunner
|
||||
@@ -1383,4 +1439,39 @@ class HibernateJpaAutoConfigurationTests {
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class SingleAsyncTaskExecutorConfiguration {
|
||||
|
||||
@Bean
|
||||
SimpleAsyncTaskExecutor exampleTaskExecutor() {
|
||||
return new SimpleAsyncTaskExecutor();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class MultipleAsyncTaskExecutorsConfiguration {
|
||||
|
||||
@Bean
|
||||
SimpleAsyncTaskExecutor exampleTaskExecutor1() {
|
||||
return new SimpleAsyncTaskExecutor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
SimpleAsyncTaskExecutor exampleTaskExecutor2() {
|
||||
return new SimpleAsyncTaskExecutor();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class ApplicationTaskExecutorConfiguration {
|
||||
|
||||
@Bean
|
||||
SimpleAsyncTaskExecutor applicationTaskExecutor() {
|
||||
return new SimpleAsyncTaskExecutor();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+5
@@ -40,6 +40,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplicat
|
||||
import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.jpa.EntityManagerFactoryBuilder;
|
||||
import org.springframework.boot.jpa.autoconfigure.JpaProperties.Bootstrap;
|
||||
import org.springframework.boot.persistence.autoconfigure.EntityScanPackages;
|
||||
import org.springframework.boot.transaction.autoconfigure.TransactionManagerCustomizers;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -127,6 +128,10 @@ public abstract class JpaBaseConfiguration {
|
||||
@Nullable AsyncTaskExecutor bootstrapExecutor = determineBootstrapExecutor(taskExecutors);
|
||||
EntityManagerFactoryBuilder builder = new EntityManagerFactoryBuilder(jpaVendorAdapter,
|
||||
this::buildJpaProperties, persistenceUnitManager.getIfAvailable(), null, bootstrapExecutor);
|
||||
if (this.properties.getBootstrap() == Bootstrap.ASYNC) {
|
||||
builder.requireBootstrapExecutor(
|
||||
new PropertyBasedRequiredBackgroundBootstrapping("spring.jpa.bootstrap", "async"));
|
||||
}
|
||||
customizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
|
||||
return builder;
|
||||
}
|
||||
|
||||
+32
@@ -77,6 +77,11 @@ public class JpaProperties {
|
||||
*/
|
||||
private @Nullable Boolean openInView;
|
||||
|
||||
/**
|
||||
* Bootstrap method to use.
|
||||
*/
|
||||
private Bootstrap bootstrap = Bootstrap.DEFAULT;
|
||||
|
||||
public Map<String, String> getProperties() {
|
||||
return this.properties;
|
||||
}
|
||||
@@ -129,4 +134,31 @@ public class JpaProperties {
|
||||
this.openInView = openInView;
|
||||
}
|
||||
|
||||
public Bootstrap getBootstrap() {
|
||||
return this.bootstrap;
|
||||
}
|
||||
|
||||
public void setBootstrap(Bootstrap bootstrap) {
|
||||
this.bootstrap = bootstrap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstrap methods that can be used with JPA.
|
||||
*/
|
||||
public enum Bootstrap {
|
||||
|
||||
/**
|
||||
* Default JPA bootstrapping.
|
||||
*/
|
||||
DEFAULT,
|
||||
|
||||
/**
|
||||
* Asynchronous JPA bootstrapping. The ApplicationContext must either have a
|
||||
* single AsyncTaskExecutor bean, or an AsyncTaskExecutor bean named
|
||||
* 'applicationTaskExecutor'.
|
||||
*/
|
||||
ASYNC
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user