Enforce JPA/Hibernate initialization before context refresh completion

Closes gh-21868
This commit is contained in:
Juergen Hoeller 2024-03-07 11:03:08 +01:00
parent c9e85ec297
commit dedc6a7e9b
4 changed files with 29 additions and 6 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2023 the original author or authors. * Copyright 2002-2024 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -40,8 +40,10 @@ import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ResourceLoaderAware; import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.InfrastructureProxy;
import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader; import org.springframework.core.io.ResourceLoader;
@ -79,7 +81,8 @@ import org.springframework.lang.Nullable;
* @see org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean * @see org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean
*/ */
public class LocalSessionFactoryBean extends HibernateExceptionTranslator public class LocalSessionFactoryBean extends HibernateExceptionTranslator
implements FactoryBean<SessionFactory>, ResourceLoaderAware, BeanFactoryAware, InitializingBean, DisposableBean { implements FactoryBean<SessionFactory>, ResourceLoaderAware, BeanFactoryAware,
InitializingBean, SmartInitializingSingleton, DisposableBean {
@Nullable @Nullable
private DataSource dataSource; private DataSource dataSource;
@ -390,6 +393,8 @@ public class LocalSessionFactoryBean extends HibernateExceptionTranslator
* then block until Hibernate's bootstrapping completed, if not ready by then. * then block until Hibernate's bootstrapping completed, if not ready by then.
* For maximum benefit, make sure to avoid early {@code SessionFactory} calls * For maximum benefit, make sure to avoid early {@code SessionFactory} calls
* in init methods of related beans, even for metadata introspection purposes. * in init methods of related beans, even for metadata introspection purposes.
* <p>As of 6.2, Hibernate initialization is enforced before context refresh
* completion, waiting for asynchronous bootstrapping to complete by then.
* @since 4.3 * @since 4.3
* @see LocalSessionFactoryBuilder#buildSessionFactory(AsyncTaskExecutor) * @see LocalSessionFactoryBuilder#buildSessionFactory(AsyncTaskExecutor)
*/ */
@ -600,6 +605,14 @@ public class LocalSessionFactoryBean extends HibernateExceptionTranslator
this.sessionFactory = buildSessionFactory(sfb); this.sessionFactory = buildSessionFactory(sfb);
} }
@Override
public void afterSingletonsInstantiated() {
// Enforce completion of asynchronous Hibernate initialization before context refresh completion.
if (this.sessionFactory instanceof InfrastructureProxy proxy) {
proxy.getWrappedObject();
}
}
/** /**
* Subclasses can override this method to perform custom initialization * Subclasses can override this method to perform custom initialization
* of the SessionFactory instance, creating it via the given Configuration * of the SessionFactory instance, creating it via the given Configuration

View File

@ -54,6 +54,7 @@ import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.core.task.AsyncTaskExecutor; import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.dao.DataAccessException; import org.springframework.dao.DataAccessException;
import org.springframework.dao.support.PersistenceExceptionTranslator; import org.springframework.dao.support.PersistenceExceptionTranslator;
@ -90,8 +91,9 @@ import org.springframework.util.CollectionUtils;
*/ */
@SuppressWarnings("serial") @SuppressWarnings("serial")
public abstract class AbstractEntityManagerFactoryBean implements public abstract class AbstractEntityManagerFactoryBean implements
FactoryBean<EntityManagerFactory>, BeanClassLoaderAware, BeanFactoryAware, BeanNameAware, FactoryBean<EntityManagerFactory>, BeanClassLoaderAware, BeanFactoryAware,
InitializingBean, DisposableBean, EntityManagerFactoryInfo, PersistenceExceptionTranslator, Serializable { BeanNameAware, InitializingBean, SmartInitializingSingleton, DisposableBean,
EntityManagerFactoryInfo, PersistenceExceptionTranslator, Serializable {
/** Logger available to subclasses. */ /** Logger available to subclasses. */
protected final Log logger = LogFactory.getLog(getClass()); protected final Log logger = LogFactory.getLog(getClass());
@ -318,6 +320,8 @@ public abstract class AbstractEntityManagerFactoryBean implements
* then block until the JPA provider's bootstrapping completed, if not ready by then. * then block until the JPA provider's bootstrapping completed, if not ready by then.
* For maximum benefit, make sure to avoid early {@code EntityManagerFactory} calls * For maximum benefit, make sure to avoid early {@code EntityManagerFactory} calls
* in init methods of related beans, even for metadata introspection purposes. * in init methods of related beans, even for metadata introspection purposes.
* <p>As of 6.2, JPA initialization is enforced before context refresh completion,
* waiting for asynchronous bootstrapping to complete by then.
* @since 4.3 * @since 4.3
*/ */
public void setBootstrapExecutor(@Nullable AsyncTaskExecutor bootstrapExecutor) { public void setBootstrapExecutor(@Nullable AsyncTaskExecutor bootstrapExecutor) {
@ -403,6 +407,12 @@ public abstract class AbstractEntityManagerFactoryBean implements
this.entityManagerFactory = createEntityManagerFactoryProxy(this.nativeEntityManagerFactory); this.entityManagerFactory = createEntityManagerFactoryProxy(this.nativeEntityManagerFactory);
} }
@Override
public void afterSingletonsInstantiated() {
// Enforce completion of asynchronous JPA initialization before context refresh completion.
getNativeEntityManagerFactory();
}
private EntityManagerFactory buildNativeEntityManagerFactory() { private EntityManagerFactory buildNativeEntityManagerFactory() {
EntityManagerFactory emf; EntityManagerFactory emf;
try { try {

View File

@ -28,7 +28,7 @@
<property name="entityManagerFactory" ref="entityManagerFactory"/> <property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean> </bean>
<bean id="hibernateStatistics" factory-bean="entityManagerFactory" factory-method="getStatistics"/> <bean id="hibernateStatistics" factory-bean="entityManagerFactory" factory-method="getStatistics" lazy-init="true"/>
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/> <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>

View File

@ -9,7 +9,7 @@
<bean id="dao" class="org.springframework.orm.jpa.support.PersistenceInjectionTests$DefaultPublicPersistenceUnitSetterNamedPerson"/> <bean id="dao" class="org.springframework.orm.jpa.support.PersistenceInjectionTests$DefaultPublicPersistenceUnitSetterNamedPerson"/>
<bean class="org.springframework.orm.jpa.support.PersistenceInjectionTests$DefaultPublicPersistenceContextSetter"/> <bean class="org.springframework.orm.jpa.support.PersistenceInjectionTests$DefaultPublicPersistenceContextSetter" lazy-init="true"/>
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"> <bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor">
<property name="proxyTargetClass" value="true"/> <property name="proxyTargetClass" value="true"/>