diff --git a/module/spring-boot-data-jpa-test/src/test/java/org/springframework/boot/data/jpa/test/autoconfigure/DataJpaTestAttributesIntegrationTests.java b/module/spring-boot-data-jpa-test/src/test/java/org/springframework/boot/data/jpa/test/autoconfigure/DataJpaTestAttributesIntegrationTests.java index 9fbec8b4e14..218388b805a 100644 --- a/module/spring-boot-data-jpa-test/src/test/java/org/springframework/boot/data/jpa/test/autoconfigure/DataJpaTestAttributesIntegrationTests.java +++ b/module/spring-boot-data-jpa-test/src/test/java/org/springframework/boot/data/jpa/test/autoconfigure/DataJpaTestAttributesIntegrationTests.java @@ -19,6 +19,8 @@ package org.springframework.boot.data.jpa.test.autoconfigure; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.ImportAutoConfiguration; +import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration; import org.springframework.core.env.Environment; import org.springframework.data.repository.config.BootstrapMode; @@ -31,6 +33,7 @@ import static org.assertj.core.api.Assertions.assertThat; * @author Scott Frederick */ @DataJpaTest(properties = "spring.profiles.active=test", bootstrapMode = BootstrapMode.DEFERRED) +@ImportAutoConfiguration(TaskExecutionAutoConfiguration.class) class DataJpaTestAttributesIntegrationTests { @Autowired diff --git a/module/spring-boot-data-jpa/src/main/java/org/springframework/boot/data/jpa/autoconfigure/DataJpaRepositoriesAutoConfiguration.java b/module/spring-boot-data-jpa/src/main/java/org/springframework/boot/data/jpa/autoconfigure/DataJpaRepositoriesAutoConfiguration.java index 1cd398afad0..a66405b5ac9 100644 --- a/module/spring-boot-data-jpa/src/main/java/org/springframework/boot/data/jpa/autoconfigure/DataJpaRepositoriesAutoConfiguration.java +++ b/module/spring-boot-data-jpa/src/main/java/org/springframework/boot/data/jpa/autoconfigure/DataJpaRepositoriesAutoConfiguration.java @@ -25,7 +25,6 @@ import org.jspecify.annotations.Nullable; import org.springframework.boot.LazyInitializationExcludeFilter; import org.springframework.boot.autoconfigure.AutoConfiguration; 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.ConditionalOnBooleanProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; @@ -35,8 +34,8 @@ import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguratio import org.springframework.boot.data.jpa.autoconfigure.DataJpaRepositoriesAutoConfiguration.JpaRepositoriesImportSelector; import org.springframework.boot.hibernate.autoconfigure.HibernateJpaAutoConfiguration; import org.springframework.boot.jpa.autoconfigure.EntityManagerFactoryBuilderCustomizer; +import org.springframework.boot.jpa.autoconfigure.PropertyBasedRequiredBackgroundBootstrapping; import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.ImportSelector; import org.springframework.core.task.AsyncTaskExecutor; @@ -83,15 +82,11 @@ import org.springframework.util.ClassUtils; public final class DataJpaRepositoriesAutoConfiguration { @Bean - @Conditional(BootstrapExecutorCondition.class) + @ConditionalOnProperty(name = "spring.data.jpa.repositories.bootstrap-mode", havingValue = "deferred") EntityManagerFactoryBuilderCustomizer entityManagerFactoryBootstrapExecutorCustomizer( Map taskExecutors) { - return (builder) -> { - AsyncTaskExecutor bootstrapExecutor = determineBootstrapExecutor(taskExecutors); - if (bootstrapExecutor != null) { - builder.setBootstrapExecutor(bootstrapExecutor); - } - }; + return (builder) -> builder.requireBootstrapExecutor(new PropertyBasedRequiredBackgroundBootstrapping( + "spring.data.jpa.repositories.bootstrap-mode", "deferred")); } @Bean @@ -106,24 +101,6 @@ public final class DataJpaRepositoriesAutoConfiguration { return taskExecutors.get(TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME); } - private static final class BootstrapExecutorCondition extends AnyNestedCondition { - - BootstrapExecutorCondition() { - super(ConfigurationPhase.REGISTER_BEAN); - } - - @ConditionalOnProperty(name = "spring.data.jpa.repositories.bootstrap-mode", havingValue = "deferred") - static class DeferredBootstrapMode { - - } - - @ConditionalOnProperty(name = "spring.data.jpa.repositories.bootstrap-mode", havingValue = "lazy") - static class LazyBootstrapMode { - - } - - } - static class JpaRepositoriesImportSelector implements ImportSelector { private static final boolean ENVERS_AVAILABLE = ClassUtils.isPresent( diff --git a/module/spring-boot-data-jpa/src/test/java/org/springframework/boot/data/jpa/autoconfigure/AbstractDataJpaRepositoriesAutoConfigurationTests.java b/module/spring-boot-data-jpa/src/test/java/org/springframework/boot/data/jpa/autoconfigure/AbstractDataJpaRepositoriesAutoConfigurationTests.java index 6765133aa6f..7d7a219961d 100644 --- a/module/spring-boot-data-jpa/src/test/java/org/springframework/boot/data/jpa/autoconfigure/AbstractDataJpaRepositoriesAutoConfigurationTests.java +++ b/module/spring-boot-data-jpa/src/test/java/org/springframework/boot/data/jpa/autoconfigure/AbstractDataJpaRepositoriesAutoConfigurationTests.java @@ -88,23 +88,12 @@ abstract class AbstractDataJpaRepositoriesAutoConfigurationTests { } @Test - void whenBootstrapModeIsLazyWithMultipleAsyncExecutorBootstrapExecutorIsConfigured() { - 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()) - .isEqualTo(context.getBean("applicationTaskExecutor"))); - } - - @Test - void whenBootstrapModeIsLazyWithSingleAsyncExecutorBootstrapExecutorIsConfigured() { + void whenBootstrapModeDoesNotUseFallbackBootstrapExecutor() { 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"))); + .isNull()); } @Test diff --git a/module/spring-boot-hibernate/src/test/java/org/springframework/boot/hibernate/autoconfigure/HibernateJpaAutoConfigurationTests.java b/module/spring-boot-hibernate/src/test/java/org/springframework/boot/hibernate/autoconfigure/HibernateJpaAutoConfigurationTests.java index 406bee31d40..1a38445a321 100644 --- a/module/spring-boot-hibernate/src/test/java/org/springframework/boot/hibernate/autoconfigure/HibernateJpaAutoConfigurationTests.java +++ b/module/spring-boot-hibernate/src/test/java/org/springframework/boot/hibernate/autoconfigure/HibernateJpaAutoConfigurationTests.java @@ -126,7 +126,7 @@ import static org.assertj.core.api.Assertions.entry; import static org.mockito.Mockito.mock; /** - * Base for JPA tests and tests for {@link JpaBaseConfiguration}. + * Test for {@link HibernateJpaAutoConfiguration} and {@link JpaBaseConfiguration}. * * @author Phillip Webb * @author Dave Syer diff --git a/module/spring-boot-jpa/src/main/java/org/springframework/boot/jpa/EntityManagerFactoryBuilder.java b/module/spring-boot-jpa/src/main/java/org/springframework/boot/jpa/EntityManagerFactoryBuilder.java index 44a8024b63d..d9251fb9635 100644 --- a/module/spring-boot-jpa/src/main/java/org/springframework/boot/jpa/EntityManagerFactoryBuilder.java +++ b/module/spring-boot-jpa/src/main/java/org/springframework/boot/jpa/EntityManagerFactoryBuilder.java @@ -26,6 +26,7 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.function.Function; +import java.util.function.Supplier; import javax.sql.DataSource; @@ -35,9 +36,11 @@ import org.springframework.boot.context.properties.PropertyMapper; import org.springframework.core.task.AsyncTaskExecutor; import org.springframework.orm.jpa.JpaVendorAdapter; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; +import org.springframework.orm.jpa.LocalEntityManagerFactoryBean; import org.springframework.orm.jpa.persistenceunit.PersistenceManagedTypes; import org.springframework.orm.jpa.persistenceunit.PersistenceUnitManager; import org.springframework.orm.jpa.persistenceunit.PersistenceUnitPostProcessor; +import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; @@ -65,10 +68,14 @@ public class EntityManagerFactoryBuilder { private final @Nullable URL persistenceUnitRootLocation; + private final @Nullable AsyncTaskExecutor fallbackBootstrapExecutor; + private @Nullable AsyncTaskExecutor bootstrapExecutor; private @Nullable List persistenceUnitPostProcessors; + private @Nullable Supplier requireBootstrapExecutorExceptionSupplier; + /** * Create a new instance passing in the common pieces that will be shared if multiple * EntityManagerFactory instances are created. @@ -98,10 +105,32 @@ public class EntityManagerFactoryBuilder { public EntityManagerFactoryBuilder(JpaVendorAdapter jpaVendorAdapter, Function> jpaPropertiesFactory, @Nullable PersistenceUnitManager persistenceUnitManager, @Nullable URL persistenceUnitRootLocation) { + this(jpaVendorAdapter, jpaPropertiesFactory, 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 jpaPropertiesFactory the JPA properties to be passed to the persistence + * provider, based on the {@linkplain #dataSource(DataSource) configured data source} + * @param persistenceUnitManager optional source of persistence unit information (can + * be null) + * @param persistenceUnitRootLocation the persistence unit root location to use as a + * fallback or {@code null} + * @param fallbackBootstrapExecutor the fallback executor to use when background + * bootstrapping is required but no explicit executor has been set + * @since 4.1.0 + */ + public EntityManagerFactoryBuilder(JpaVendorAdapter jpaVendorAdapter, + Function> jpaPropertiesFactory, + @Nullable PersistenceUnitManager persistenceUnitManager, @Nullable URL persistenceUnitRootLocation, + @Nullable AsyncTaskExecutor fallbackBootstrapExecutor) { this.jpaVendorAdapter = jpaVendorAdapter; this.persistenceUnitManager = persistenceUnitManager; this.jpaPropertiesFactory = jpaPropertiesFactory; this.persistenceUnitRootLocation = persistenceUnitRootLocation; + this.fallbackBootstrapExecutor = fallbackBootstrapExecutor; } /** @@ -123,6 +152,20 @@ public class EntityManagerFactoryBuilder { this.bootstrapExecutor = bootstrapExecutor; } + /** + * Require a bootstrap executor to be configured when the + * {@link LocalEntityManagerFactoryBean} is built. If no bootstrap executor is + * {@link #setBootstrapExecutor(AsyncTaskExecutor) explicitly set} and no + * {@link #EntityManagerFactoryBuilder(JpaVendorAdapter, Function, PersistenceUnitManager, URL, AsyncTaskExecutor) + * fallbackBootstrapExecutor} is available then the supplied exception is thrown. + * @param exceptionSupplier a supplier providing the exception to throw + * @since 4.1.0 + */ + public void requireBootstrapExecutor(Supplier exceptionSupplier) { + Assert.notNull(exceptionSupplier, "'exceptionSupplier' must not be null"); + this.requireBootstrapExecutorExceptionSupplier = exceptionSupplier; + } + /** * Set the {@linkplain PersistenceUnitPostProcessor persistence unit post processors} * to be applied to the PersistenceUnitInfo used for creating the @@ -281,7 +324,7 @@ public class EntityManagerFactoryBuilder { map.from(EntityManagerFactoryBuilder.this.persistenceUnitRootLocation) .as(Object::toString) .to(factory::setPersistenceUnitRootLocation); - map.from(EntityManagerFactoryBuilder.this.bootstrapExecutor).to(factory::setBootstrapExecutor); + map.from(this::bootstrapExecutor).to(factory::setBootstrapExecutor); map.from(EntityManagerFactoryBuilder.this.persistenceUnitPostProcessors) .as((postProcessors) -> postProcessors.toArray(PersistenceUnitPostProcessor[]::new)) .to(factory::setPersistenceUnitPostProcessors); @@ -295,6 +338,20 @@ public class EntityManagerFactoryBuilder { return jpaPropertyMap; } + private @Nullable AsyncTaskExecutor bootstrapExecutor() { + if (EntityManagerFactoryBuilder.this.bootstrapExecutor != null) { + return EntityManagerFactoryBuilder.this.bootstrapExecutor; + } + if (EntityManagerFactoryBuilder.this.requireBootstrapExecutorExceptionSupplier != null) { + if (EntityManagerFactoryBuilder.this.fallbackBootstrapExecutor != null) { + return EntityManagerFactoryBuilder.this.fallbackBootstrapExecutor; + } + RuntimeException ex = EntityManagerFactoryBuilder.this.requireBootstrapExecutorExceptionSupplier.get(); + throw (ex != null) ? ex : new IllegalStateException("A bootstrap executor is required"); + } + return null; + } + } } diff --git a/module/spring-boot-jpa/src/main/java/org/springframework/boot/jpa/autoconfigure/JpaBaseConfiguration.java b/module/spring-boot-jpa/src/main/java/org/springframework/boot/jpa/autoconfigure/JpaBaseConfiguration.java index 47bbe2e9a8a..032d26361d7 100644 --- a/module/spring-boot-jpa/src/main/java/org/springframework/boot/jpa/autoconfigure/JpaBaseConfiguration.java +++ b/module/spring-boot-jpa/src/main/java/org/springframework/boot/jpa/autoconfigure/JpaBaseConfiguration.java @@ -37,6 +37,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingFilterBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type; +import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.jpa.EntityManagerFactoryBuilder; import org.springframework.boot.persistence.autoconfigure.EntityScanPackages; @@ -45,6 +46,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.core.io.ResourceLoader; +import org.springframework.core.task.AsyncTaskExecutor; import org.springframework.orm.jpa.JpaTransactionManager; import org.springframework.orm.jpa.JpaVendorAdapter; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; @@ -120,13 +122,20 @@ public abstract class JpaBaseConfiguration { @ConditionalOnMissingBean public EntityManagerFactoryBuilder entityManagerFactoryBuilder(JpaVendorAdapter jpaVendorAdapter, ObjectProvider persistenceUnitManager, - ObjectProvider customizers) { + ObjectProvider customizers, + Map taskExecutors) { + @Nullable AsyncTaskExecutor bootstrapExecutor = determineBootstrapExecutor(taskExecutors); EntityManagerFactoryBuilder builder = new EntityManagerFactoryBuilder(jpaVendorAdapter, - this::buildJpaProperties, persistenceUnitManager.getIfAvailable()); + this::buildJpaProperties, persistenceUnitManager.getIfAvailable(), null, bootstrapExecutor); customizers.orderedStream().forEach((customizer) -> customizer.customize(builder)); return builder; } + private @Nullable AsyncTaskExecutor determineBootstrapExecutor(Map taskExecutors) { + return (taskExecutors.size() == 1) ? taskExecutors.values().iterator().next() + : taskExecutors.get(TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME); + } + private Map buildJpaProperties(DataSource dataSource) { Map properties = new HashMap<>(this.properties.getProperties()); Map vendorProperties = getVendorProperties(dataSource); diff --git a/module/spring-boot-jpa/src/main/java/org/springframework/boot/jpa/autoconfigure/PropertyBasedRequiredBackgroundBootstrapping.java b/module/spring-boot-jpa/src/main/java/org/springframework/boot/jpa/autoconfigure/PropertyBasedRequiredBackgroundBootstrapping.java new file mode 100644 index 00000000000..1f139a2a8c6 --- /dev/null +++ b/module/spring-boot-jpa/src/main/java/org/springframework/boot/jpa/autoconfigure/PropertyBasedRequiredBackgroundBootstrapping.java @@ -0,0 +1,60 @@ +/* + * Copyright 2012-present 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 + * + * https://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.jpa.autoconfigure; + +import java.util.function.Supplier; + +import org.springframework.boot.diagnostics.FailureAnalyzedException; +import org.springframework.boot.jpa.EntityManagerFactoryBuilder; +import org.springframework.util.Assert; + +/** + * {@link Supplier} to use with + * {@link EntityManagerFactoryBuilder#requireBootstrapExecutor} when a property indicates + * background bootstrapping is required. + * + * @author Phillip Webb + * @since 4.1.0 + */ +public class PropertyBasedRequiredBackgroundBootstrapping implements Supplier { + + private final String propertyName; + + private final String propertyValue; + + public PropertyBasedRequiredBackgroundBootstrapping(String propertyName, String propertyValue) { + Assert.notNull(propertyName, "'propertyName' must not be null"); + Assert.notNull(propertyValue, "'propertyValue' must not be null"); + this.propertyName = propertyName; + this.propertyValue = propertyValue; + } + + @Override + public RuntimeException get() { + String description = "A LocalContainerEntityManagerFactoryBean bootstrap executor is required when '%s' is set to '%s'" + .formatted(this.propertyName, this.propertyValue); + StringBuilder action = new StringBuilder(); + action.append("Use a different '%s' or provide a bootstrap executor using one of the following methods:\n" + .formatted(this.propertyName)); + action.append("\tWith an auto-configured task executor " + + "(you may need to set 'spring.task.execution.mode' to 'force')."); + action.append("\tWith an AsyncTaskExecutor bean named 'applicationTaskExecutor.'"); + action.append("\tUsing a EntityManagerFactoryBuilderCustomizer."); + return new FailureAnalyzedException(description, action.toString()); + } + +} diff --git a/module/spring-boot-jpa/src/test/java/org/springframework/boot/jpa/EntityManagerFactoryBuilderTests.java b/module/spring-boot-jpa/src/test/java/org/springframework/boot/jpa/EntityManagerFactoryBuilderTests.java index 5925dd6bac4..58b584f2795 100644 --- a/module/spring-boot-jpa/src/test/java/org/springframework/boot/jpa/EntityManagerFactoryBuilderTests.java +++ b/module/spring-boot-jpa/src/test/java/org/springframework/boot/jpa/EntityManagerFactoryBuilderTests.java @@ -24,12 +24,17 @@ import javax.sql.DataSource; import jakarta.persistence.spi.PersistenceProvider; import org.assertj.core.api.InstanceOfAssertFactories; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.Test; +import org.springframework.core.task.AsyncTaskExecutor; +import org.springframework.core.task.SimpleAsyncTaskExecutor; import org.springframework.orm.jpa.persistenceunit.PersistenceUnitPostProcessor; import org.springframework.orm.jpa.vendor.AbstractJpaVendorAdapter; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalStateException; +import static org.assertj.core.api.Assertions.assertThatNoException; import static org.mockito.Mockito.mock; /** @@ -86,9 +91,48 @@ class EntityManagerFactoryBuilderTests { .containsExactly(postProcessor, postProcessor2); } + @Test + void requireBootstrapExecutorWhenExecutorProvidedDoesNotThrow() { + EntityManagerFactoryBuilder builder = createEmptyBuilder(); + builder.requireBootstrapExecutor(() -> new IllegalStateException("BAD")); + builder.setBootstrapExecutor(new SimpleAsyncTaskExecutor()); + DataSource dataSource = mock(); + assertThatNoException().isThrownBy(builder.dataSource(dataSource)::build); + } + + @Test + void requireBootstrapExecutorWhenFallbackExecutorProvidesExecutorDoesNotThrow() { + EntityManagerFactoryBuilder builder = createEmptyBuilder(new SimpleAsyncTaskExecutor()); + builder.requireBootstrapExecutor(() -> new IllegalStateException("BAD")); + DataSource dataSource = mock(); + assertThatNoException().isThrownBy(builder.dataSource(dataSource)::build); + } + + @Test + void requireBootstrapExecutorWhenExecutorAndNoFallbackExecutorThrowsException() { + EntityManagerFactoryBuilder builder = createEmptyBuilder(); + builder.requireBootstrapExecutor(() -> new IllegalStateException("BAD")); + DataSource dataSource = mock(); + assertThatIllegalStateException().isThrownBy(builder.dataSource(dataSource)::build).withMessage("BAD"); + } + + @Test + void requireBootstrapExecutorWhenSupplierReturnsNullExecutorAndNoFallbackExecutorThrowsException() { + EntityManagerFactoryBuilder builder = createEmptyBuilder(); + builder.requireBootstrapExecutor(() -> null); + DataSource dataSource = mock(); + assertThatIllegalStateException().isThrownBy(builder.dataSource(dataSource)::build) + .withMessage("A bootstrap executor is required"); + } + private EntityManagerFactoryBuilder createEmptyBuilder() { + return createEmptyBuilder(null); + } + + private EntityManagerFactoryBuilder createEmptyBuilder(@Nullable AsyncTaskExecutor fallbackBootstrapExecutor) { Function> jpaPropertiesFactory = (dataSource) -> Collections.emptyMap(); - return new EntityManagerFactoryBuilder(new TestJpaVendorAdapter(), jpaPropertiesFactory, null); + return new EntityManagerFactoryBuilder(new TestJpaVendorAdapter(), jpaPropertiesFactory, null, null, + fallbackBootstrapExecutor); } static class TestJpaVendorAdapter extends AbstractJpaVendorAdapter { diff --git a/module/spring-boot-jpa/src/test/java/org/springframework/boot/jpa/autoconfigure/PropertyBasedRequiredBackgroundBootstrappingTests.java b/module/spring-boot-jpa/src/test/java/org/springframework/boot/jpa/autoconfigure/PropertyBasedRequiredBackgroundBootstrappingTests.java new file mode 100644 index 00000000000..765b0f1fa63 --- /dev/null +++ b/module/spring-boot-jpa/src/test/java/org/springframework/boot/jpa/autoconfigure/PropertyBasedRequiredBackgroundBootstrappingTests.java @@ -0,0 +1,40 @@ +/* + * Copyright 2012-present 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 + * + * https://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.jpa.autoconfigure; + +import org.junit.jupiter.api.Test; + +import org.springframework.boot.diagnostics.FailureAnalyzedException; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link PropertyBasedRequiredBackgroundBootstrapping}. + * + * @author Phillip Webb + */ +class PropertyBasedRequiredBackgroundBootstrappingTests { + + @Test + void getReturnsFailureAnalyzableException() { + RuntimeException exception = new PropertyBasedRequiredBackgroundBootstrapping("test.bootstrap", "true").get(); + assertThat(exception).isInstanceOf(FailureAnalyzedException.class) + .hasMessage("A LocalContainerEntityManagerFactoryBean bootstrap executor is required " + + "when 'test.bootstrap' is set to 'true'"); + } + +}