Browse Source

Refine Spring Data JPA background bootstrapping mode support

Update `spring.data.jpa.repositories.bootstrap-mode` logic
so that:

- A value of `lazy` no longer sets a bootstrap executor.

- A value of `deferred` requires that a bootstrap executor is
  ultimately set, and fails with a meaningful message if it is
  not.

The logic used to find the AsyncTaskExecutor has now moved to
the `JpaBaseConfiguration` and is triggered by the
`EntityManagerFactoryBuilder` only when background
bootstrapping is required.

Closes gh-49688
pull/49740/head
Phillip Webb 1 week ago
parent
commit
e76c6c5488
  1. 3
      module/spring-boot-data-jpa-test/src/test/java/org/springframework/boot/data/jpa/test/autoconfigure/DataJpaTestAttributesIntegrationTests.java
  2. 31
      module/spring-boot-data-jpa/src/main/java/org/springframework/boot/data/jpa/autoconfigure/DataJpaRepositoriesAutoConfiguration.java
  3. 15
      module/spring-boot-data-jpa/src/test/java/org/springframework/boot/data/jpa/autoconfigure/AbstractDataJpaRepositoriesAutoConfigurationTests.java
  4. 2
      module/spring-boot-hibernate/src/test/java/org/springframework/boot/hibernate/autoconfigure/HibernateJpaAutoConfigurationTests.java
  5. 59
      module/spring-boot-jpa/src/main/java/org/springframework/boot/jpa/EntityManagerFactoryBuilder.java
  6. 13
      module/spring-boot-jpa/src/main/java/org/springframework/boot/jpa/autoconfigure/JpaBaseConfiguration.java
  7. 60
      module/spring-boot-jpa/src/main/java/org/springframework/boot/jpa/autoconfigure/PropertyBasedRequiredBackgroundBootstrapping.java
  8. 46
      module/spring-boot-jpa/src/test/java/org/springframework/boot/jpa/EntityManagerFactoryBuilderTests.java
  9. 40
      module/spring-boot-jpa/src/test/java/org/springframework/boot/jpa/autoconfigure/PropertyBasedRequiredBackgroundBootstrappingTests.java

3
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; @@ -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; @@ -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

31
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; @@ -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 @@ -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; @@ -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<String, AsyncTaskExecutor> 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 { @@ -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(

15
module/spring-boot-data-jpa/src/test/java/org/springframework/boot/data/jpa/autoconfigure/AbstractDataJpaRepositoriesAutoConfigurationTests.java

@ -88,23 +88,12 @@ abstract class AbstractDataJpaRepositoriesAutoConfigurationTests { @@ -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

2
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; @@ -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

59
module/spring-boot-jpa/src/main/java/org/springframework/boot/jpa/EntityManagerFactoryBuilder.java

@ -26,6 +26,7 @@ import java.util.List; @@ -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; @@ -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 { @@ -65,10 +68,14 @@ public class EntityManagerFactoryBuilder {
private final @Nullable URL persistenceUnitRootLocation;
private final @Nullable AsyncTaskExecutor fallbackBootstrapExecutor;
private @Nullable AsyncTaskExecutor bootstrapExecutor;
private @Nullable List<PersistenceUnitPostProcessor> persistenceUnitPostProcessors;
private @Nullable Supplier<? extends RuntimeException> 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 { @@ -98,10 +105,32 @@ public class EntityManagerFactoryBuilder {
public EntityManagerFactoryBuilder(JpaVendorAdapter jpaVendorAdapter,
Function<DataSource, Map<String, ?>> 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<DataSource, Map<String, ?>> 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 { @@ -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<? extends RuntimeException> 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 { @@ -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 { @@ -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;
}
}
}

13
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 @@ -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; @@ -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 { @@ -120,13 +122,20 @@ public abstract class JpaBaseConfiguration {
@ConditionalOnMissingBean
public EntityManagerFactoryBuilder entityManagerFactoryBuilder(JpaVendorAdapter jpaVendorAdapter,
ObjectProvider<PersistenceUnitManager> persistenceUnitManager,
ObjectProvider<EntityManagerFactoryBuilderCustomizer> customizers) {
ObjectProvider<EntityManagerFactoryBuilderCustomizer> customizers,
Map<String, AsyncTaskExecutor> 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<String, AsyncTaskExecutor> taskExecutors) {
return (taskExecutors.size() == 1) ? taskExecutors.values().iterator().next()
: taskExecutors.get(TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME);
}
private Map<String, ?> buildJpaProperties(DataSource dataSource) {
Map<String, Object> properties = new HashMap<>(this.properties.getProperties());
Map<String, Object> vendorProperties = getVendorProperties(dataSource);

60
module/spring-boot-jpa/src/main/java/org/springframework/boot/jpa/autoconfigure/PropertyBasedRequiredBackgroundBootstrapping.java

@ -0,0 +1,60 @@ @@ -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<RuntimeException> {
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());
}
}

46
module/spring-boot-jpa/src/test/java/org/springframework/boot/jpa/EntityManagerFactoryBuilderTests.java

@ -24,12 +24,17 @@ import javax.sql.DataSource; @@ -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 { @@ -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<DataSource, Map<String, ?>> 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 {

40
module/spring-boot-jpa/src/test/java/org/springframework/boot/jpa/autoconfigure/PropertyBasedRequiredBackgroundBootstrappingTests.java

@ -0,0 +1,40 @@ @@ -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'");
}
}
Loading…
Cancel
Save