Browse Source
Previously, DataSource initialization was triggered via a BeanPostProcessor or a schema created event from JPA. This caused numerous problems with circular dependencies, bean lifecycle, etc and added significant complexity. This commit reworks DataSource initialization to remove the use of a BeanPostProcessor entirely. In its place, DataSource initialization is now driven by an InitializingBean with dependency relationships between beans ensuring that initialization has been performed before the DataSource is used. This aligns with the approach that's worked well with Flyway and Liquibase. More changes are planned to further simplify DataSource initialization. The changes in this commit are a foundation for those changes. Any new public API in this commit is highly likely to change before the next GA. Fixes gh-13042 Fixes gh-23736pull/25369/head
19 changed files with 539 additions and 538 deletions
@ -0,0 +1,61 @@
@@ -0,0 +1,61 @@
|
||||
/* |
||||
* Copyright 2012-2021 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.autoconfigure.jdbc; |
||||
|
||||
import javax.sql.DataSource; |
||||
|
||||
import org.springframework.beans.factory.InitializingBean; |
||||
import org.springframework.context.ResourceLoaderAware; |
||||
import org.springframework.core.io.ResourceLoader; |
||||
|
||||
/** |
||||
* {@link InitializingBean} that performs {@link DataSource} initialization using DDL and |
||||
* DML scripts. |
||||
* |
||||
* @author Andy Wilkinson |
||||
* @since 2.5.0 |
||||
*/ |
||||
public class DataSourceInitialization implements InitializingBean, ResourceLoaderAware { |
||||
|
||||
private final DataSource dataSource; |
||||
|
||||
private final DataSourceProperties properies; |
||||
|
||||
private volatile ResourceLoader resourceLoader; |
||||
|
||||
/** |
||||
* Creates a new {@link DataSourceInitialization} that will initialize the given |
||||
* {@code DataSource} using the settings from the given {@code properties}. |
||||
* @param dataSource the DataSource to initialize |
||||
* @param properies the properties containing the initialization settings |
||||
*/ |
||||
public DataSourceInitialization(DataSource dataSource, DataSourceProperties properies) { |
||||
this.dataSource = dataSource; |
||||
this.properies = properies; |
||||
} |
||||
|
||||
@Override |
||||
public void afterPropertiesSet() throws Exception { |
||||
new DataSourceInitializer(this.dataSource, this.properies, this.resourceLoader).initializeDataSource(); |
||||
} |
||||
|
||||
@Override |
||||
public void setResourceLoader(ResourceLoader resourceLoader) { |
||||
this.resourceLoader = resourceLoader; |
||||
} |
||||
|
||||
} |
||||
@ -1,106 +0,0 @@
@@ -1,106 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2019 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.autoconfigure.jdbc; |
||||
|
||||
import javax.sql.DataSource; |
||||
|
||||
import org.apache.commons.logging.Log; |
||||
import org.apache.commons.logging.LogFactory; |
||||
|
||||
import org.springframework.beans.factory.InitializingBean; |
||||
import org.springframework.beans.factory.ObjectProvider; |
||||
import org.springframework.context.ApplicationContext; |
||||
import org.springframework.context.ApplicationListener; |
||||
import org.springframework.core.log.LogMessage; |
||||
|
||||
/** |
||||
* Bean to handle {@link DataSource} initialization by running {@literal schema-*.sql} on |
||||
* {@link InitializingBean#afterPropertiesSet()} and {@literal data-*.sql} SQL scripts on |
||||
* a {@link DataSourceSchemaCreatedEvent}. |
||||
* |
||||
* @author Stephane Nicoll |
||||
* @see DataSourceAutoConfiguration |
||||
*/ |
||||
class DataSourceInitializerInvoker implements ApplicationListener<DataSourceSchemaCreatedEvent>, InitializingBean { |
||||
|
||||
private static final Log logger = LogFactory.getLog(DataSourceInitializerInvoker.class); |
||||
|
||||
private final ObjectProvider<DataSource> dataSource; |
||||
|
||||
private final DataSourceProperties properties; |
||||
|
||||
private final ApplicationContext applicationContext; |
||||
|
||||
private DataSourceInitializer dataSourceInitializer; |
||||
|
||||
private boolean initialized; |
||||
|
||||
DataSourceInitializerInvoker(ObjectProvider<DataSource> dataSource, DataSourceProperties properties, |
||||
ApplicationContext applicationContext) { |
||||
this.dataSource = dataSource; |
||||
this.properties = properties; |
||||
this.applicationContext = applicationContext; |
||||
} |
||||
|
||||
@Override |
||||
public void afterPropertiesSet() { |
||||
DataSourceInitializer initializer = getDataSourceInitializer(); |
||||
if (initializer != null) { |
||||
boolean schemaCreated = this.dataSourceInitializer.createSchema(); |
||||
if (schemaCreated) { |
||||
initialize(initializer); |
||||
} |
||||
} |
||||
} |
||||
|
||||
private void initialize(DataSourceInitializer initializer) { |
||||
try { |
||||
this.applicationContext.publishEvent(new DataSourceSchemaCreatedEvent(initializer.getDataSource())); |
||||
// The listener might not be registered yet, so don't rely on it.
|
||||
if (!this.initialized) { |
||||
this.dataSourceInitializer.initSchema(); |
||||
this.initialized = true; |
||||
} |
||||
} |
||||
catch (IllegalStateException ex) { |
||||
logger.warn(LogMessage.format("Could not send event to complete DataSource initialization (%s)", |
||||
ex.getMessage())); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void onApplicationEvent(DataSourceSchemaCreatedEvent event) { |
||||
// NOTE the event can happen more than once and
|
||||
// the event datasource is not used here
|
||||
DataSourceInitializer initializer = getDataSourceInitializer(); |
||||
if (!this.initialized && initializer != null) { |
||||
initializer.initSchema(); |
||||
this.initialized = true; |
||||
} |
||||
} |
||||
|
||||
private DataSourceInitializer getDataSourceInitializer() { |
||||
if (this.dataSourceInitializer == null) { |
||||
DataSource ds = this.dataSource.getIfUnique(); |
||||
if (ds != null) { |
||||
this.dataSourceInitializer = new DataSourceInitializer(ds, this.properties, this.applicationContext); |
||||
} |
||||
} |
||||
return this.dataSourceInitializer; |
||||
} |
||||
|
||||
} |
||||
@ -1,61 +0,0 @@
@@ -1,61 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2020 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.autoconfigure.jdbc; |
||||
|
||||
import javax.sql.DataSource; |
||||
|
||||
import org.springframework.beans.BeansException; |
||||
import org.springframework.beans.factory.BeanFactory; |
||||
import org.springframework.beans.factory.BeanFactoryAware; |
||||
import org.springframework.beans.factory.config.BeanPostProcessor; |
||||
import org.springframework.core.Ordered; |
||||
|
||||
/** |
||||
* {@link BeanPostProcessor} used to ensure that {@link DataSourceInitializer} is |
||||
* initialized as soon as a {@link DataSource} is. |
||||
* |
||||
* @author Dave Syer |
||||
*/ |
||||
class DataSourceInitializerPostProcessor implements BeanPostProcessor, Ordered, BeanFactoryAware { |
||||
|
||||
@Override |
||||
public int getOrder() { |
||||
return Ordered.HIGHEST_PRECEDENCE + 1; |
||||
} |
||||
|
||||
private BeanFactory beanFactory; |
||||
|
||||
@Override |
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { |
||||
return bean; |
||||
} |
||||
|
||||
@Override |
||||
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { |
||||
if (bean instanceof DataSource) { |
||||
// force initialization of this bean as soon as we see a DataSource
|
||||
this.beanFactory.getBean(DataSourceInitializerInvoker.class); |
||||
} |
||||
return bean; |
||||
} |
||||
|
||||
@Override |
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException { |
||||
this.beanFactory = beanFactory; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,43 @@
@@ -0,0 +1,43 @@
|
||||
/* |
||||
* Copyright 2012-2021 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.autoconfigure.jdbc; |
||||
|
||||
import java.lang.annotation.Documented; |
||||
import java.lang.annotation.ElementType; |
||||
import java.lang.annotation.Retention; |
||||
import java.lang.annotation.RetentionPolicy; |
||||
import java.lang.annotation.Target; |
||||
|
||||
import javax.sql.DataSource; |
||||
|
||||
import org.springframework.context.annotation.DependsOn; |
||||
|
||||
/** |
||||
* Annotation used to indicate that a bean depends upon the auto-configured DataSource |
||||
* initialization having completed before the {@link DataSource} is injected. If the |
||||
* DataSource is not used during startup, no such dependency is required. |
||||
* |
||||
* @author Andy Wilkinson |
||||
* @since 2.5.0 |
||||
*/ |
||||
@Target({ ElementType.TYPE, ElementType.METHOD }) |
||||
@Retention(RetentionPolicy.RUNTIME) |
||||
@Documented |
||||
@DependsOn("dataSourceInitialization") |
||||
public @interface DependsOnDataSourceInitialization { |
||||
|
||||
} |
||||
@ -1,271 +0,0 @@
@@ -1,271 +0,0 @@
|
||||
/* |
||||
* Copyright 2012-2020 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.autoconfigure.orm.jpa; |
||||
|
||||
import java.util.Map; |
||||
import java.util.concurrent.Future; |
||||
import java.util.function.Supplier; |
||||
|
||||
import javax.persistence.EntityManager; |
||||
import javax.persistence.EntityManagerFactory; |
||||
import javax.persistence.spi.PersistenceProvider; |
||||
import javax.persistence.spi.PersistenceUnitInfo; |
||||
import javax.sql.DataSource; |
||||
|
||||
import org.springframework.beans.BeansException; |
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.beans.factory.config.BeanDefinition; |
||||
import org.springframework.beans.factory.config.BeanPostProcessor; |
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition; |
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder; |
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry; |
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceSchemaCreatedEvent; |
||||
import org.springframework.boot.jdbc.EmbeddedDatabaseConnection; |
||||
import org.springframework.context.ApplicationContext; |
||||
import org.springframework.context.ApplicationContextAware; |
||||
import org.springframework.context.ApplicationListener; |
||||
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; |
||||
import org.springframework.context.event.ContextRefreshedEvent; |
||||
import org.springframework.core.Ordered; |
||||
import org.springframework.core.task.AsyncTaskExecutor; |
||||
import org.springframework.core.type.AnnotationMetadata; |
||||
import org.springframework.orm.jpa.JpaDialect; |
||||
import org.springframework.orm.jpa.JpaVendorAdapter; |
||||
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; |
||||
|
||||
/** |
||||
* {@link BeanPostProcessor} used to fire {@link DataSourceSchemaCreatedEvent}s. Should |
||||
* only be registered via the inner {@link Registrar} class. |
||||
* |
||||
* @author Dave Syer |
||||
*/ |
||||
class DataSourceInitializedPublisher implements BeanPostProcessor { |
||||
|
||||
@Autowired |
||||
private ApplicationContext applicationContext; |
||||
|
||||
private DataSource dataSource; |
||||
|
||||
private JpaProperties jpaProperties; |
||||
|
||||
private HibernateProperties hibernateProperties; |
||||
|
||||
private DataSourceSchemaCreatedPublisher schemaCreatedPublisher; |
||||
|
||||
private DataSourceInitializationCompletionListener initializationCompletionListener; |
||||
|
||||
DataSourceInitializedPublisher(DataSourceInitializationCompletionListener completionListener) { |
||||
this.initializationCompletionListener = completionListener; |
||||
} |
||||
|
||||
@Override |
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { |
||||
if (bean instanceof LocalContainerEntityManagerFactoryBean) { |
||||
LocalContainerEntityManagerFactoryBean factory = (LocalContainerEntityManagerFactoryBean) bean; |
||||
if (factory.getBootstrapExecutor() != null && factory.getJpaVendorAdapter() != null) { |
||||
this.schemaCreatedPublisher = new DataSourceSchemaCreatedPublisher(factory); |
||||
factory.setJpaVendorAdapter(this.schemaCreatedPublisher); |
||||
} |
||||
} |
||||
return bean; |
||||
} |
||||
|
||||
@Override |
||||
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { |
||||
if (bean instanceof DataSource) { |
||||
// Normally this will be the right DataSource
|
||||
this.dataSource = (DataSource) bean; |
||||
} |
||||
if (bean instanceof JpaProperties) { |
||||
this.jpaProperties = (JpaProperties) bean; |
||||
} |
||||
if (bean instanceof HibernateProperties) { |
||||
this.hibernateProperties = (HibernateProperties) bean; |
||||
} |
||||
if (bean instanceof LocalContainerEntityManagerFactoryBean && this.schemaCreatedPublisher == null) { |
||||
LocalContainerEntityManagerFactoryBean factoryBean = (LocalContainerEntityManagerFactoryBean) bean; |
||||
EntityManagerFactory entityManagerFactory = factoryBean.getNativeEntityManagerFactory(); |
||||
publishEventIfRequired(factoryBean, entityManagerFactory); |
||||
} |
||||
return bean; |
||||
} |
||||
|
||||
private void publishEventIfRequired(LocalContainerEntityManagerFactoryBean factoryBean, |
||||
EntityManagerFactory entityManagerFactory) { |
||||
DataSource dataSource = findDataSource(factoryBean, entityManagerFactory); |
||||
if (dataSource != null && isInitializingDatabase(dataSource)) { |
||||
this.applicationContext.publishEvent(new DataSourceSchemaCreatedEvent(dataSource)); |
||||
} |
||||
} |
||||
|
||||
private DataSource findDataSource(LocalContainerEntityManagerFactoryBean factoryBean, |
||||
EntityManagerFactory entityManagerFactory) { |
||||
Object dataSource = entityManagerFactory.getProperties().get("javax.persistence.nonJtaDataSource"); |
||||
if (dataSource == null) { |
||||
dataSource = factoryBean.getPersistenceUnitInfo().getNonJtaDataSource(); |
||||
} |
||||
return (dataSource instanceof DataSource) ? (DataSource) dataSource : this.dataSource; |
||||
} |
||||
|
||||
private boolean isInitializingDatabase(DataSource dataSource) { |
||||
if (this.jpaProperties == null || this.hibernateProperties == null) { |
||||
return true; // better safe than sorry
|
||||
} |
||||
Supplier<String> defaultDdlAuto = () -> (EmbeddedDatabaseConnection.isEmbedded(dataSource) ? "create-drop" |
||||
: "none"); |
||||
Map<String, Object> hibernate = this.hibernateProperties.determineHibernateProperties( |
||||
this.jpaProperties.getProperties(), new HibernateSettings().ddlAuto(defaultDdlAuto)); |
||||
return hibernate.containsKey("hibernate.hbm2ddl.auto"); |
||||
} |
||||
|
||||
/** |
||||
* {@link ApplicationListener} that, upon receiving {@link ContextRefreshedEvent}, |
||||
* blocks until any asynchronous DataSource initialization has completed. |
||||
*/ |
||||
static class DataSourceInitializationCompletionListener |
||||
implements ApplicationListener<ContextRefreshedEvent>, Ordered, ApplicationContextAware { |
||||
|
||||
private volatile ApplicationContext applicationContext; |
||||
|
||||
private volatile Future<?> dataSourceInitialization; |
||||
|
||||
@Override |
||||
public void onApplicationEvent(ContextRefreshedEvent event) { |
||||
if (!event.getApplicationContext().equals(this.applicationContext)) { |
||||
return; |
||||
} |
||||
Future<?> dataSourceInitialization = this.dataSourceInitialization; |
||||
if (dataSourceInitialization != null) { |
||||
try { |
||||
dataSourceInitialization.get(); |
||||
} |
||||
catch (Exception ex) { |
||||
throw new RuntimeException(ex); |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public int getOrder() { |
||||
return Ordered.HIGHEST_PRECEDENCE; |
||||
} |
||||
|
||||
@Override |
||||
public void setApplicationContext(ApplicationContext applicationContext) { |
||||
this.applicationContext = applicationContext; |
||||
} |
||||
|
||||
} |
||||
|
||||
/** |
||||
* {@link ImportBeanDefinitionRegistrar} to register the |
||||
* {@link DataSourceInitializedPublisher} without causing early bean instantiation |
||||
* issues. |
||||
*/ |
||||
static class Registrar implements ImportBeanDefinitionRegistrar { |
||||
|
||||
private static final String PUBLISHER_BEAN_NAME = "dataSourceInitializedPublisher"; |
||||
|
||||
private static final String COMPLETION_LISTENER_BEAN_BEAN = DataSourceInitializationCompletionListener.class |
||||
.getName(); |
||||
|
||||
@Override |
||||
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, |
||||
BeanDefinitionRegistry registry) { |
||||
if (!registry.containsBeanDefinition(PUBLISHER_BEAN_NAME)) { |
||||
DataSourceInitializationCompletionListener completionListener = new DataSourceInitializationCompletionListener(); |
||||
DataSourceInitializedPublisher publisher = new DataSourceInitializedPublisher(completionListener); |
||||
AbstractBeanDefinition publisherDefinition = BeanDefinitionBuilder |
||||
.genericBeanDefinition(DataSourceInitializedPublisher.class, () -> publisher) |
||||
.getBeanDefinition(); |
||||
publisherDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); |
||||
// We don't need this one to be post processed otherwise it can cause a
|
||||
// cascade of bean instantiation that we would rather avoid.
|
||||
publisherDefinition.setSynthetic(true); |
||||
registry.registerBeanDefinition(PUBLISHER_BEAN_NAME, publisherDefinition); |
||||
AbstractBeanDefinition listenerDefinition = BeanDefinitionBuilder.genericBeanDefinition( |
||||
DataSourceInitializationCompletionListener.class, () -> completionListener).getBeanDefinition(); |
||||
listenerDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); |
||||
// We don't need this one to be post processed otherwise it can cause a
|
||||
// cascade of bean instantiation that we would rather avoid.
|
||||
listenerDefinition.setSynthetic(true); |
||||
registry.registerBeanDefinition(COMPLETION_LISTENER_BEAN_BEAN, listenerDefinition); |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
final class DataSourceSchemaCreatedPublisher implements JpaVendorAdapter { |
||||
|
||||
private final LocalContainerEntityManagerFactoryBean factoryBean; |
||||
|
||||
private final JpaVendorAdapter delegate; |
||||
|
||||
private DataSourceSchemaCreatedPublisher(LocalContainerEntityManagerFactoryBean factoryBean) { |
||||
this.factoryBean = factoryBean; |
||||
this.delegate = factoryBean.getJpaVendorAdapter(); |
||||
} |
||||
|
||||
@Override |
||||
public PersistenceProvider getPersistenceProvider() { |
||||
return this.delegate.getPersistenceProvider(); |
||||
} |
||||
|
||||
@Override |
||||
public String getPersistenceProviderRootPackage() { |
||||
return this.delegate.getPersistenceProviderRootPackage(); |
||||
} |
||||
|
||||
@Override |
||||
public Map<String, ?> getJpaPropertyMap(PersistenceUnitInfo pui) { |
||||
return this.delegate.getJpaPropertyMap(pui); |
||||
} |
||||
|
||||
@Override |
||||
public Map<String, ?> getJpaPropertyMap() { |
||||
return this.delegate.getJpaPropertyMap(); |
||||
} |
||||
|
||||
@Override |
||||
public JpaDialect getJpaDialect() { |
||||
return this.delegate.getJpaDialect(); |
||||
} |
||||
|
||||
@Override |
||||
public Class<? extends EntityManagerFactory> getEntityManagerFactoryInterface() { |
||||
return this.delegate.getEntityManagerFactoryInterface(); |
||||
} |
||||
|
||||
@Override |
||||
public Class<? extends EntityManager> getEntityManagerInterface() { |
||||
return this.delegate.getEntityManagerInterface(); |
||||
} |
||||
|
||||
@Override |
||||
public void postProcessEntityManagerFactory(EntityManagerFactory entityManagerFactory) { |
||||
this.delegate.postProcessEntityManagerFactory(entityManagerFactory); |
||||
AsyncTaskExecutor bootstrapExecutor = this.factoryBean.getBootstrapExecutor(); |
||||
if (bootstrapExecutor != null) { |
||||
DataSourceInitializedPublisher.this.initializationCompletionListener.dataSourceInitialization = bootstrapExecutor |
||||
.submit(() -> DataSourceInitializedPublisher.this.publishEventIfRequired(this.factoryBean, |
||||
entityManagerFactory)); |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,87 @@
@@ -0,0 +1,87 @@
|
||||
/* |
||||
* Copyright 2012-2021 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.autoconfigure.orm.jpa; |
||||
|
||||
import org.hibernate.boot.Metadata; |
||||
import org.hibernate.internal.CoreLogging; |
||||
import org.hibernate.internal.CoreMessageLogger; |
||||
import org.hibernate.resource.transaction.spi.DdlTransactionIsolator; |
||||
import org.hibernate.tool.schema.TargetType; |
||||
import org.hibernate.tool.schema.internal.HibernateSchemaManagementTool; |
||||
import org.hibernate.tool.schema.internal.exec.GenerationTarget; |
||||
import org.hibernate.tool.schema.internal.exec.GenerationTargetToDatabase; |
||||
import org.hibernate.tool.schema.internal.exec.JdbcContext; |
||||
import org.hibernate.tool.schema.spi.ExecutionOptions; |
||||
import org.hibernate.tool.schema.spi.SchemaCreator; |
||||
import org.hibernate.tool.schema.spi.SourceDescriptor; |
||||
import org.hibernate.tool.schema.spi.TargetDescriptor; |
||||
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceInitializer; |
||||
|
||||
/** |
||||
* Spring Boot {@link SchemaCreator}. |
||||
* |
||||
* @author Phillip Webb |
||||
* @author Andy Wilkinson |
||||
*/ |
||||
class SpringBootSchemaCreator implements SchemaCreator { |
||||
|
||||
private static final CoreMessageLogger log = CoreLogging.messageLogger(SpringBootSchemaCreator.class); |
||||
|
||||
private final HibernateSchemaManagementTool tool; |
||||
|
||||
private final DataSourceInitializer dataSourceInitializer; |
||||
|
||||
private final SchemaCreator creator; |
||||
|
||||
SpringBootSchemaCreator(HibernateSchemaManagementTool tool, SchemaCreator creator, |
||||
DataSourceInitializer dataSourceInitializer) { |
||||
this.tool = tool; |
||||
this.creator = creator; |
||||
this.dataSourceInitializer = dataSourceInitializer; |
||||
} |
||||
|
||||
@Override |
||||
public void doCreation(Metadata metadata, ExecutionOptions options, SourceDescriptor sourceDescriptor, |
||||
TargetDescriptor targetDescriptor) { |
||||
if (!targetDescriptor.getTargetTypes().contains(TargetType.DATABASE)) { |
||||
this.creator.doCreation(metadata, options, sourceDescriptor, targetDescriptor); |
||||
return; |
||||
} |
||||
GenerationTarget databaseTarget = getDatabaseTarget(options, targetDescriptor); |
||||
databaseTarget.prepare(); |
||||
try { |
||||
this.creator.doCreation(metadata, options, sourceDescriptor, targetDescriptor); |
||||
this.dataSourceInitializer.initializeDataSource(); |
||||
} |
||||
finally { |
||||
try { |
||||
databaseTarget.release(); |
||||
} |
||||
catch (Exception ex) { |
||||
log.debugf("Problem releasing GenerationTarget [%s] : %s", databaseTarget, ex.getMessage()); |
||||
} |
||||
} |
||||
} |
||||
|
||||
private GenerationTarget getDatabaseTarget(ExecutionOptions options, TargetDescriptor targetDescriptor) { |
||||
JdbcContext jdbcContext = this.tool.resolveJdbcContext(options.getConfigurationValues()); |
||||
DdlTransactionIsolator ddlTransactionIsolator = this.tool.getDdlTransactionIsolator(jdbcContext); |
||||
return new GenerationTargetToDatabase(ddlTransactionIsolator); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,48 @@
@@ -0,0 +1,48 @@
|
||||
/* |
||||
* Copyright 2012-2021 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.autoconfigure.orm.jpa; |
||||
|
||||
import java.util.Map; |
||||
|
||||
import org.hibernate.tool.schema.internal.HibernateSchemaManagementTool; |
||||
import org.hibernate.tool.schema.spi.SchemaCreator; |
||||
import org.hibernate.tool.schema.spi.SchemaManagementTool; |
||||
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceInitializer; |
||||
|
||||
/** |
||||
* Spring Boot {@link SchemaManagementTool}. |
||||
* |
||||
* @author Phillip Webb |
||||
* @author Andy Wilkinson |
||||
*/ |
||||
class SpringBootSchemaManagementTool extends HibernateSchemaManagementTool { |
||||
|
||||
private final DataSourceInitializer dataSourceInitializer; |
||||
|
||||
SpringBootSchemaManagementTool(DataSourceInitializer dataSourceInitializer) { |
||||
this.dataSourceInitializer = dataSourceInitializer; |
||||
} |
||||
|
||||
@Override |
||||
@SuppressWarnings("rawtypes") |
||||
public SchemaCreator getSchemaCreator(Map options) { |
||||
SchemaCreator creator = super.getSchemaCreator(options); |
||||
return new SpringBootSchemaCreator(this, creator, this.dataSourceInitializer); |
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue