Background bootstrapping via "bootstrapExecutor" for LocalContainerEntityManagerFactoryBean and LocalSessionFactoryBean/Builder

Issue: SPR-13732
This commit is contained in:
Juergen Hoeller
2015-12-19 15:13:46 +01:00
parent 64bd8b7f34
commit db1171d5c4
6 changed files with 212 additions and 33 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2015 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.
@@ -30,6 +30,9 @@ import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceException;
@@ -48,6 +51,7 @@ import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.util.Assert;
@@ -102,6 +106,8 @@ public abstract class AbstractEntityManagerFactoryBean implements
private JpaVendorAdapter jpaVendorAdapter;
private AsyncTaskExecutor bootstrapExecutor;
private ClassLoader beanClassLoader = getClass().getClassLoader();
private BeanFactory beanFactory;
@@ -109,8 +115,12 @@ public abstract class AbstractEntityManagerFactoryBean implements
private String beanName;
/** Raw EntityManagerFactory as returned by the PersistenceProvider */
public EntityManagerFactory nativeEntityManagerFactory;
private EntityManagerFactory nativeEntityManagerFactory;
/** Future for lazily initializing raw target EntityManagerFactory */
private Future<EntityManagerFactory> nativeEntityManagerFactoryFuture;
/** Exposed client-level EntityManagerFactory proxy */
private EntityManagerFactory entityManagerFactory;
@@ -263,6 +273,30 @@ public abstract class AbstractEntityManagerFactoryBean implements
return this.jpaVendorAdapter;
}
/**
* Specify an asynchronous executor for background bootstrapping,
* e.g. a {@link org.springframework.core.task.SimpleAsyncTaskExecutor}.
* <p>{@code EntityManagerFactory} initialization will then switch into background
* bootstrap mode, with a {@code EntityManagerFactory} proxy immediately returned for
* injection purposes instead of waiting for the JPA provider's bootstrapping to complete.
* However, note that the first actual call to a {@code EntityManagerFactory} method will
* 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
* in init methods of related beans, even for metadata introspection purposes.
* @since 4.3
*/
public void setBootstrapExecutor(AsyncTaskExecutor bootstrapExecutor) {
this.bootstrapExecutor = bootstrapExecutor;
}
/**
* Return the asynchronous executor for background bootstrapping, if any.
* @since 4.3
*/
public AsyncTaskExecutor getBootstrapExecutor() {
return this.bootstrapExecutor;
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.beanClassLoader = classLoader;
@@ -315,13 +349,16 @@ public abstract class AbstractEntityManagerFactoryBean implements
}
}
this.nativeEntityManagerFactory = createNativeEntityManagerFactory();
if (this.nativeEntityManagerFactory == null) {
throw new IllegalStateException(
"JPA PersistenceProvider returned null EntityManagerFactory - check your JPA provider setup!");
if (this.bootstrapExecutor != null) {
this.nativeEntityManagerFactoryFuture = this.bootstrapExecutor.submit(new Callable<EntityManagerFactory>() {
@Override
public EntityManagerFactory call() {
return buildNativeEntityManagerFactory();
}
});
}
if (this.jpaVendorAdapter != null) {
this.jpaVendorAdapter.postProcessEntityManagerFactory(this.nativeEntityManagerFactory);
else {
this.nativeEntityManagerFactory = buildNativeEntityManagerFactory();
}
// Wrap the EntityManagerFactory in a factory implementing all its interfaces.
@@ -329,6 +366,23 @@ public abstract class AbstractEntityManagerFactoryBean implements
// application-managed EntityManager proxy that automatically joins
// existing transactions.
this.entityManagerFactory = createEntityManagerFactoryProxy(this.nativeEntityManagerFactory);
System.out.println("Returning: " + System.currentTimeMillis());
}
private EntityManagerFactory buildNativeEntityManagerFactory() {
EntityManagerFactory emf = createNativeEntityManagerFactory();
if (emf == null) {
throw new IllegalStateException(
"JPA PersistenceProvider returned null EntityManagerFactory - check your JPA provider setup!");
}
if (this.jpaVendorAdapter != null) {
this.jpaVendorAdapter.postProcessEntityManagerFactory(emf);
}
if (logger.isInfoEnabled()) {
logger.info("Initialized JPA EntityManagerFactory for persistence unit '" + getPersistenceUnitName() + "'");
}
System.out.println("Done: " + System.currentTimeMillis());
return emf;
}
/**
@@ -343,9 +397,12 @@ public abstract class AbstractEntityManagerFactoryBean implements
if (this.entityManagerFactoryInterface != null) {
ifcs.add(this.entityManagerFactoryInterface);
}
else {
else if (emf != null) {
ifcs.addAll(ClassUtils.getAllInterfacesForClassAsSet(emf.getClass(), this.beanClassLoader));
}
else {
ifcs.add(EntityManagerFactory.class);
}
ifcs.add(EntityManagerFactoryInfo.class);
try {
return (EntityManagerFactory) Proxy.newProxyInstance(
@@ -379,13 +436,13 @@ public abstract class AbstractEntityManagerFactoryBean implements
// JPA 2.1's createEntityManager(SynchronizationType, Map)
// Redirect to plain createEntityManager and add synchronization semantics through Spring proxy
EntityManager rawEntityManager = (args.length > 1 ?
this.nativeEntityManagerFactory.createEntityManager((Map<?, ?>) args[1]) :
this.nativeEntityManagerFactory.createEntityManager());
getNativeEntityManagerFactory().createEntityManager((Map<?, ?>) args[1]) :
getNativeEntityManagerFactory().createEntityManager());
return ExtendedEntityManagerCreator.createApplicationManagedEntityManager(rawEntityManager, this, true);
}
// Standard delegation to the native factory, just post-processing EntityManager return values
Object retVal = method.invoke(this.nativeEntityManagerFactory, args);
Object retVal = method.invoke(getNativeEntityManagerFactory(), args);
if (retVal instanceof EntityManager) {
// Any other createEntityManager variant - expecting non-synchronized semantics
EntityManager rawEntityManager = (EntityManager) retVal;
@@ -420,7 +477,21 @@ public abstract class AbstractEntityManagerFactoryBean implements
@Override
public EntityManagerFactory getNativeEntityManagerFactory() {
return this.nativeEntityManagerFactory;
if (this.nativeEntityManagerFactory != null) {
return this.nativeEntityManagerFactory;
}
else {
System.out.println("Requested: " + System.currentTimeMillis());
try {
return this.nativeEntityManagerFactoryFuture.get();
}
catch (InterruptedException ex) {
throw new IllegalStateException("Interrupted during initialization of native EntityManagerFactory", ex);
}
catch (ExecutionException ex) {
throw new IllegalStateException("Failed to asynchronously initialize native EntityManagerFactory", ex);
}
}
}
@Override

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2015 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.
@@ -43,8 +43,8 @@ import org.springframework.util.ClassUtils;
* up a shared JPA EntityManagerFactory in a Spring application context;
* the EntityManagerFactory can then be passed to JPA-based DAOs via
* dependency injection. Note that switching to a JNDI lookup or to a
* {@link LocalEntityManagerFactoryBean}
* definition is just a matter of configuration!
* {@link LocalEntityManagerFactoryBean} definition is just a matter of
* configuration!
*
* <p>As with {@link LocalEntityManagerFactoryBean}, configuration settings
* are usually read in from a {@code META-INF/persistence.xml} config file,
@@ -329,21 +329,16 @@ public class LocalContainerEntityManagerFactoryBean extends AbstractEntityManage
Class<?> providerClass = ClassUtils.resolveClassName(providerClassName, getBeanClassLoader());
provider = (PersistenceProvider) BeanUtils.instantiateClass(providerClass);
}
if (provider == null) {
throw new IllegalStateException("Unable to determine persistence provider. " +
"Please check configuration of " + getClass().getName() + "; " +
"ideally specify the appropriate JpaVendorAdapter class for this provider.");
}
if (logger.isInfoEnabled()) {
logger.info("Building JPA container EntityManagerFactory for persistence unit '" +
this.persistenceUnitInfo.getPersistenceUnitName() + "'");
}
this.nativeEntityManagerFactory =
EntityManagerFactory emf =
provider.createContainerEntityManagerFactory(this.persistenceUnitInfo, getJpaPropertyMap());
postProcessEntityManagerFactory(this.nativeEntityManagerFactory, this.persistenceUnitInfo);
postProcessEntityManagerFactory(emf, this.persistenceUnitInfo);
return this.nativeEntityManagerFactory;
return emf;
}