mirror of
https://github.com/spring-projects/spring-boot.git
synced 2026-09-17 12:09:16 +00:00
Merge pull request #50801 from ns3154
* gh-50797-lazy-jpa-bootstrap-executor: Polish "Lazily resolve JPA fallback bootstrap executor" Lazily resolve JPA fallback bootstrap executor Closes gh-50801
This commit is contained in:
+1
-14
@@ -16,12 +16,8 @@
|
||||
|
||||
package org.springframework.boot.data.jpa.autoconfigure;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.LazyInitializationExcludeFilter;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
@@ -38,7 +34,6 @@ import org.springframework.boot.jpa.autoconfigure.EntityManagerFactoryBuilderCus
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.ImportSelector;
|
||||
import org.springframework.core.task.AsyncTaskExecutor;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.data.envers.repository.config.EnableEnversRepositories;
|
||||
import org.springframework.data.envers.repository.support.EnversRevisionRepositoryFactoryBean;
|
||||
@@ -83,8 +78,7 @@ public final class DataJpaRepositoriesAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(name = "spring.data.jpa.repositories.bootstrap-mode", havingValue = "deferred")
|
||||
EntityManagerFactoryBuilderCustomizer entityManagerFactoryBootstrapExecutorCustomizer(
|
||||
Map<String, AsyncTaskExecutor> taskExecutors) {
|
||||
EntityManagerFactoryBuilderCustomizer entityManagerFactoryBootstrapExecutorCustomizer() {
|
||||
return (builder) -> builder.requireBootstrapExecutor(() -> BootstrapExecutorRequiredException
|
||||
.ofProperty("spring.data.jpa.repositories.bootstrap-mode", "deferred"));
|
||||
}
|
||||
@@ -94,13 +88,6 @@ public final class DataJpaRepositoriesAutoConfiguration {
|
||||
return (name, definition, type) -> "org.springframework.data.jpa.util.JpaMetamodelCacheCleanup".equals(name);
|
||||
}
|
||||
|
||||
private @Nullable AsyncTaskExecutor determineBootstrapExecutor(Map<String, AsyncTaskExecutor> taskExecutors) {
|
||||
if (taskExecutors.size() == 1) {
|
||||
return taskExecutors.values().iterator().next();
|
||||
}
|
||||
return taskExecutors.get(TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME);
|
||||
}
|
||||
|
||||
static class JpaRepositoriesImportSelector implements ImportSelector {
|
||||
|
||||
private static final boolean ENVERS_AVAILABLE = ClassUtils.isPresent(
|
||||
|
||||
+23
@@ -104,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.AsyncTaskExecutor;
|
||||
import org.springframework.core.task.SimpleAsyncTaskExecutor;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.support.SQLExceptionTranslator;
|
||||
@@ -303,6 +304,15 @@ class HibernateJpaAutoConfigurationTests {
|
||||
return (builder) -> builder.setBootstrapExecutor(new SimpleAsyncTaskExecutor());
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenAsyncTaskExecutorIsDefinedInJpaDependentConfigurationDoesNotFail() {
|
||||
this.contextRunner.withUserConfiguration(TaskExecutorDependingOnEntityManagerFactoryConfiguration.class)
|
||||
.run((context) -> {
|
||||
assertThat(context).hasNotFailed();
|
||||
assertThat(context).hasSingleBean(EntityManagerFactory.class).hasSingleBean(AsyncTaskExecutor.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void customJpaProperties() {
|
||||
this.contextRunner
|
||||
@@ -1474,4 +1484,17 @@ class HibernateJpaAutoConfigurationTests {
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class TaskExecutorDependingOnEntityManagerFactoryConfiguration {
|
||||
|
||||
TaskExecutorDependingOnEntityManagerFactoryConfiguration(EntityManagerFactory entityManagerFactory) {
|
||||
}
|
||||
|
||||
@Bean
|
||||
SimpleAsyncTaskExecutor exampleTaskExecutor() {
|
||||
return new SimpleAsyncTaskExecutor();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+30
-4
@@ -68,7 +68,7 @@ public class EntityManagerFactoryBuilder {
|
||||
|
||||
private final @Nullable URL persistenceUnitRootLocation;
|
||||
|
||||
private final @Nullable AsyncTaskExecutor fallbackBootstrapExecutor;
|
||||
private final Supplier<? extends @Nullable AsyncTaskExecutor> fallbackBootstrapExecutor;
|
||||
|
||||
private @Nullable AsyncTaskExecutor bootstrapExecutor;
|
||||
|
||||
@@ -105,7 +105,7 @@ public class EntityManagerFactoryBuilder {
|
||||
public EntityManagerFactoryBuilder(JpaVendorAdapter jpaVendorAdapter,
|
||||
Function<DataSource, Map<String, ?>> jpaPropertiesFactory,
|
||||
@Nullable PersistenceUnitManager persistenceUnitManager, @Nullable URL persistenceUnitRootLocation) {
|
||||
this(jpaVendorAdapter, jpaPropertiesFactory, persistenceUnitManager, persistenceUnitRootLocation, null);
|
||||
this(jpaVendorAdapter, jpaPropertiesFactory, persistenceUnitManager, persistenceUnitRootLocation, () -> null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -121,11 +121,36 @@ public class EntityManagerFactoryBuilder {
|
||||
* @param fallbackBootstrapExecutor the fallback executor to use when background
|
||||
* bootstrapping is required but no explicit executor has been set
|
||||
* @since 4.1.0
|
||||
* @deprecated since 4.1.1 for removal in 4.3.0 in favor of
|
||||
* {@link #EntityManagerFactoryBuilder(JpaVendorAdapter, Function, PersistenceUnitManager, URL, Supplier)}
|
||||
*/
|
||||
@Deprecated(since = "4.1.1", forRemoval = true)
|
||||
public EntityManagerFactoryBuilder(JpaVendorAdapter jpaVendorAdapter,
|
||||
Function<DataSource, Map<String, ?>> jpaPropertiesFactory,
|
||||
@Nullable PersistenceUnitManager persistenceUnitManager, @Nullable URL persistenceUnitRootLocation,
|
||||
@Nullable AsyncTaskExecutor fallbackBootstrapExecutor) {
|
||||
this(jpaVendorAdapter, jpaPropertiesFactory, persistenceUnitManager, persistenceUnitRootLocation,
|
||||
() -> fallbackBootstrapExecutor);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 a supplier of the fallback executor to use when
|
||||
* background bootstrapping is required, but no explicit executor has been set.
|
||||
* @since 4.1.1
|
||||
*/
|
||||
public EntityManagerFactoryBuilder(JpaVendorAdapter jpaVendorAdapter,
|
||||
Function<DataSource, Map<String, ?>> jpaPropertiesFactory,
|
||||
@Nullable PersistenceUnitManager persistenceUnitManager, @Nullable URL persistenceUnitRootLocation,
|
||||
Supplier<? extends @Nullable AsyncTaskExecutor> fallbackBootstrapExecutor) {
|
||||
this.jpaVendorAdapter = jpaVendorAdapter;
|
||||
this.persistenceUnitManager = persistenceUnitManager;
|
||||
this.jpaPropertiesFactory = jpaPropertiesFactory;
|
||||
@@ -343,8 +368,9 @@ public class EntityManagerFactoryBuilder {
|
||||
return EntityManagerFactoryBuilder.this.bootstrapExecutor;
|
||||
}
|
||||
if (EntityManagerFactoryBuilder.this.requireBootstrapExecutorExceptionSupplier != null) {
|
||||
if (EntityManagerFactoryBuilder.this.fallbackBootstrapExecutor != null) {
|
||||
return EntityManagerFactoryBuilder.this.fallbackBootstrapExecutor;
|
||||
@Nullable AsyncTaskExecutor fallback = EntityManagerFactoryBuilder.this.fallbackBootstrapExecutor.get();
|
||||
if (fallback != null) {
|
||||
return fallback;
|
||||
}
|
||||
RuntimeException ex = EntityManagerFactoryBuilder.this.requireBootstrapExecutorExceptionSupplier.get();
|
||||
throw (ex != null) ? ex : new IllegalStateException("A bootstrap executor is required");
|
||||
|
||||
+6
-5
@@ -28,6 +28,7 @@ import org.apache.commons.logging.LogFactory;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurationPackages;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
@@ -123,11 +124,10 @@ public abstract class JpaBaseConfiguration {
|
||||
@ConditionalOnMissingBean
|
||||
public EntityManagerFactoryBuilder entityManagerFactoryBuilder(JpaVendorAdapter jpaVendorAdapter,
|
||||
ObjectProvider<PersistenceUnitManager> persistenceUnitManager,
|
||||
ObjectProvider<EntityManagerFactoryBuilderCustomizer> customizers,
|
||||
Map<String, AsyncTaskExecutor> taskExecutors) {
|
||||
AsyncTaskExecutor bootstrapExecutor = determineBootstrapExecutor(taskExecutors);
|
||||
ObjectProvider<EntityManagerFactoryBuilderCustomizer> customizers, ListableBeanFactory beanFactory) {
|
||||
EntityManagerFactoryBuilder builder = new EntityManagerFactoryBuilder(jpaVendorAdapter,
|
||||
this::buildJpaProperties, persistenceUnitManager.getIfAvailable(), null, bootstrapExecutor);
|
||||
this::buildJpaProperties, persistenceUnitManager.getIfAvailable(), null,
|
||||
() -> determineBootstrapExecutor(beanFactory));
|
||||
if (this.properties.getBootstrap() == Bootstrap.ASYNC) {
|
||||
builder.requireBootstrapExecutor(
|
||||
() -> BootstrapExecutorRequiredException.ofProperty("spring.jpa.bootstrap", "async"));
|
||||
@@ -136,7 +136,8 @@ public abstract class JpaBaseConfiguration {
|
||||
return builder;
|
||||
}
|
||||
|
||||
private @Nullable AsyncTaskExecutor determineBootstrapExecutor(Map<String, AsyncTaskExecutor> taskExecutors) {
|
||||
private @Nullable AsyncTaskExecutor determineBootstrapExecutor(ListableBeanFactory beanFactory) {
|
||||
Map<String, AsyncTaskExecutor> taskExecutors = beanFactory.getBeansOfType(AsyncTaskExecutor.class);
|
||||
return (taskExecutors.size() == 1) ? taskExecutors.values().iterator().next()
|
||||
: taskExecutors.get(TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME);
|
||||
}
|
||||
|
||||
+28
-5
@@ -18,7 +18,9 @@ package org.springframework.boot.jpa;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
@@ -102,7 +104,7 @@ class EntityManagerFactoryBuilderTests {
|
||||
|
||||
@Test
|
||||
void requireBootstrapExecutorWhenFallbackExecutorProvidesExecutorDoesNotThrow() {
|
||||
EntityManagerFactoryBuilder builder = createEmptyBuilder(new SimpleAsyncTaskExecutor());
|
||||
EntityManagerFactoryBuilder builder = createEmptyBuilder(SimpleAsyncTaskExecutor::new);
|
||||
builder.requireBootstrapExecutor(() -> new IllegalStateException("BAD"));
|
||||
DataSource dataSource = mock();
|
||||
assertThatNoException().isThrownBy(builder.dataSource(dataSource)::build);
|
||||
@@ -125,14 +127,35 @@ class EntityManagerFactoryBuilderTests {
|
||||
.withMessage("A bootstrap executor is required");
|
||||
}
|
||||
|
||||
private EntityManagerFactoryBuilder createEmptyBuilder() {
|
||||
return createEmptyBuilder(null);
|
||||
@Test
|
||||
void requireBootstrapExecutorWhenFallbackExecutorSupplierProvidesExecutorDoesNotThrow() {
|
||||
EntityManagerFactoryBuilder builder = createEmptyBuilder(SimpleAsyncTaskExecutor::new);
|
||||
builder.requireBootstrapExecutor(() -> new IllegalStateException("BAD"));
|
||||
DataSource dataSource = mock();
|
||||
assertThatNoException().isThrownBy(builder.dataSource(dataSource)::build);
|
||||
}
|
||||
|
||||
private EntityManagerFactoryBuilder createEmptyBuilder(@Nullable AsyncTaskExecutor fallbackBootstrapExecutor) {
|
||||
@Test
|
||||
void fallbackExecutorSupplierIsNotInvokedWhenBootstrapExecutorNotRequired() {
|
||||
AtomicBoolean invoked = new AtomicBoolean();
|
||||
EntityManagerFactoryBuilder builder = createEmptyBuilder(() -> {
|
||||
invoked.set(true);
|
||||
return new SimpleAsyncTaskExecutor();
|
||||
});
|
||||
DataSource dataSource = mock();
|
||||
builder.dataSource(dataSource).build();
|
||||
assertThat(invoked).isFalse();
|
||||
}
|
||||
|
||||
private EntityManagerFactoryBuilder createEmptyBuilder() {
|
||||
return createEmptyBuilder(() -> null);
|
||||
}
|
||||
|
||||
private EntityManagerFactoryBuilder createEmptyBuilder(
|
||||
Supplier<? extends @Nullable AsyncTaskExecutor> fallbackBootstrapExecutorSupplier) {
|
||||
Function<DataSource, Map<String, ?>> jpaPropertiesFactory = (dataSource) -> Collections.emptyMap();
|
||||
return new EntityManagerFactoryBuilder(new TestJpaVendorAdapter(), jpaPropertiesFactory, null, null,
|
||||
fallbackBootstrapExecutor);
|
||||
fallbackBootstrapExecutorSupplier);
|
||||
}
|
||||
|
||||
static class TestJpaVendorAdapter extends AbstractJpaVendorAdapter {
|
||||
|
||||
Reference in New Issue
Block a user