Skip to content

Commit 30ee481

Browse files
committed
Do not block in DSInitializedPublisher when EMF is using async bootstrap
Previously, we would retrieve the EntityManagerFactory’s DataSource as soon as it was being post-processed. When the native EntityManagerFactory is being bootstrapped asynchronously, this retrieval would block until bootstrapping had completed. This negated some of the benefits of asynchronous bootstrapping. This commit updates DataSourceInitializedPublisher so that it only accesses the EntityManagerFactory’s DataSource once its bootstrapping has completed. This is achieved using a decorated JpaVendorAdapter that is called one the boostrapping has completed. Closes gh-14061
1 parent f28528a commit 30ee481

File tree

1 file changed

+75
-5
lines changed

1 file changed

+75
-5
lines changed

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/DataSourceInitializedPublisher.java

Lines changed: 75 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@
1919
import java.util.Map;
2020
import java.util.function.Supplier;
2121

22+
import javax.persistence.EntityManager;
2223
import javax.persistence.EntityManagerFactory;
24+
import javax.persistence.spi.PersistenceProvider;
25+
import javax.persistence.spi.PersistenceUnitInfo;
2326
import javax.sql.DataSource;
2427

2528
import org.springframework.beans.BeansException;
@@ -32,7 +35,11 @@
3235
import org.springframework.boot.jdbc.EmbeddedDatabaseConnection;
3336
import org.springframework.context.ApplicationContext;
3437
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
38+
import org.springframework.core.task.AsyncTaskExecutor;
3539
import org.springframework.core.type.AnnotationMetadata;
40+
import org.springframework.orm.jpa.JpaDialect;
41+
import org.springframework.orm.jpa.JpaVendorAdapter;
42+
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
3643

3744
/**
3845
* {@link BeanPostProcessor} used to fire {@link DataSourceSchemaCreatedEvent}s. Should
@@ -55,6 +62,10 @@ class DataSourceInitializedPublisher implements BeanPostProcessor {
5562
@Override
5663
public Object postProcessBeforeInitialization(Object bean, String beanName)
5764
throws BeansException {
65+
if (bean instanceof LocalContainerEntityManagerFactoryBean) {
66+
LocalContainerEntityManagerFactoryBean factory = (LocalContainerEntityManagerFactoryBean) bean;
67+
factory.setJpaVendorAdapter(new DataSourceSchemeCreatedPublisher(factory));
68+
}
5869
return bean;
5970
}
6071

@@ -71,9 +82,6 @@ public Object postProcessAfterInitialization(Object bean, String beanName)
7182
if (bean instanceof HibernateProperties) {
7283
this.hibernateProperties = (HibernateProperties) bean;
7384
}
74-
if (bean instanceof EntityManagerFactory) {
75-
publishEventIfRequired((EntityManagerFactory) bean);
76-
}
7785
return bean;
7886
}
7987

@@ -88,8 +96,8 @@ private void publishEventIfRequired(EntityManagerFactory entityManagerFactory) {
8896
private DataSource findDataSource(EntityManagerFactory entityManagerFactory) {
8997
Object dataSource = entityManagerFactory.getProperties()
9098
.get("javax.persistence.nonJtaDataSource");
91-
return (dataSource != null && dataSource instanceof DataSource)
92-
? (DataSource) dataSource : this.dataSource;
99+
return (dataSource instanceof DataSource) ? (DataSource) dataSource
100+
: this.dataSource;
93101
}
94102

95103
private boolean isInitializingDatabase(DataSource dataSource) {
@@ -132,4 +140,66 @@ public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
132140

133141
}
134142

143+
final class DataSourceSchemeCreatedPublisher implements JpaVendorAdapter {
144+
145+
private final JpaVendorAdapter delegate;
146+
147+
private final LocalContainerEntityManagerFactoryBean factory;
148+
149+
private DataSourceSchemeCreatedPublisher(
150+
LocalContainerEntityManagerFactoryBean factory) {
151+
this.delegate = factory.getJpaVendorAdapter();
152+
this.factory = factory;
153+
}
154+
155+
@Override
156+
public PersistenceProvider getPersistenceProvider() {
157+
return this.delegate.getPersistenceProvider();
158+
}
159+
160+
@Override
161+
public String getPersistenceProviderRootPackage() {
162+
return this.delegate.getPersistenceProviderRootPackage();
163+
}
164+
165+
@Override
166+
public Map<String, ?> getJpaPropertyMap(PersistenceUnitInfo pui) {
167+
return this.delegate.getJpaPropertyMap(pui);
168+
}
169+
170+
@Override
171+
public Map<String, ?> getJpaPropertyMap() {
172+
return this.delegate.getJpaPropertyMap();
173+
}
174+
175+
@Override
176+
public JpaDialect getJpaDialect() {
177+
return this.delegate.getJpaDialect();
178+
}
179+
180+
@Override
181+
public Class<? extends EntityManagerFactory> getEntityManagerFactoryInterface() {
182+
return this.delegate.getEntityManagerFactoryInterface();
183+
}
184+
185+
@Override
186+
public Class<? extends EntityManager> getEntityManagerInterface() {
187+
return this.delegate.getEntityManagerInterface();
188+
}
189+
190+
@Override
191+
public void postProcessEntityManagerFactory(EntityManagerFactory emf) {
192+
this.delegate.postProcessEntityManagerFactory(emf);
193+
AsyncTaskExecutor bootstrapExecutor = this.factory.getBootstrapExecutor();
194+
if (bootstrapExecutor != null) {
195+
bootstrapExecutor.execute(() -> DataSourceInitializedPublisher.this
196+
.publishEventIfRequired(emf));
197+
}
198+
else {
199+
DataSourceInitializedPublisher.this.publishEventIfRequired(emf);
200+
}
201+
}
202+
203+
}
204+
135205
}

0 commit comments

Comments
 (0)