Revert deferred DDL changes and re-oreder database migrations
Schema initialization now happens in @PostConstruct (effectively) whether it is via the Hibernate EntityManagerFactory or the Boot DataSourceInitialization (in addition or instead). The data.sql script if it exists is still executed on an event fired from the other places, so those tests are passing. Flyway and liquibase have bean factory post processors (like the one they use to order the audit aspect in Spring Data) that enforce a dependency on those components from the EntityManagerFactory. So Hibernate validation is still happy (and there are 2 tests to prove it now as well). Fixes gh-1022
This commit is contained in:
parent
f7d1aab9f3
commit
74166e770a
|
|
@ -16,11 +16,21 @@
|
|||
|
||||
package org.springframework.boot.autoconfigure.flyway;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.persistence.EntityManagerFactory;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.flywaydb.core.Flyway;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
|
|
@ -32,10 +42,18 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
|
|||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.orm.jpa.AbstractEntityManagerFactoryBean;
|
||||
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.springframework.beans.factory.BeanFactoryUtils.beanNamesForTypeIncludingAncestors;
|
||||
import static org.springframework.beans.factory.BeanFactoryUtils.transformedBeanName;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for Flyway database migrations.
|
||||
|
|
@ -53,6 +71,7 @@ public class FlywayAutoConfiguration {
|
|||
@Configuration
|
||||
@ConditionalOnMissingBean(Flyway.class)
|
||||
@EnableConfigurationProperties(FlywayProperties.class)
|
||||
@Import(FlywayJpaDependencyConfiguration.class)
|
||||
public static class FlywayConfiguration {
|
||||
|
||||
@Autowired
|
||||
|
|
@ -105,4 +124,57 @@ public class FlywayAutoConfiguration {
|
|||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(LocalContainerEntityManagerFactoryBean.class)
|
||||
@ConditionalOnBean(AbstractEntityManagerFactoryBean.class)
|
||||
protected static class FlywayJpaDependencyConfiguration implements
|
||||
BeanFactoryPostProcessor {
|
||||
|
||||
public static final String FLYWAY_JPA_BEAN_NAME = "flyway";
|
||||
|
||||
@Override
|
||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
|
||||
|
||||
for (String beanName : getEntityManagerFactoryBeanNames(beanFactory)) {
|
||||
BeanDefinition definition = getBeanDefinition(beanName, beanFactory);
|
||||
definition.setDependsOn(StringUtils.addStringToArray(
|
||||
definition.getDependsOn(), FLYWAY_JPA_BEAN_NAME));
|
||||
}
|
||||
}
|
||||
|
||||
private static BeanDefinition getBeanDefinition(String beanName,
|
||||
ConfigurableListableBeanFactory beanFactory) {
|
||||
try {
|
||||
return beanFactory.getBeanDefinition(beanName);
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException e) {
|
||||
|
||||
BeanFactory parentBeanFactory = beanFactory.getParentBeanFactory();
|
||||
|
||||
if (parentBeanFactory instanceof ConfigurableListableBeanFactory) {
|
||||
return getBeanDefinition(beanName,
|
||||
(ConfigurableListableBeanFactory) parentBeanFactory);
|
||||
}
|
||||
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private static Iterable<String> getEntityManagerFactoryBeanNames(
|
||||
ListableBeanFactory beanFactory) {
|
||||
|
||||
Set<String> names = new HashSet<String>();
|
||||
names.addAll(asList(beanNamesForTypeIncludingAncestors(beanFactory,
|
||||
EntityManagerFactory.class, true, false)));
|
||||
|
||||
for (String factoryBeanName : beanNamesForTypeIncludingAncestors(beanFactory,
|
||||
AbstractEntityManagerFactoryBean.class, true, false)) {
|
||||
names.add(transformedBeanName(factoryBeanName));
|
||||
}
|
||||
|
||||
return names;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@ import org.springframework.context.ApplicationEvent;
|
|||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.event.ContextRefreshedEvent;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.jdbc.datasource.init.DatabasePopulatorUtils;
|
||||
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
|
||||
|
|
@ -44,12 +43,11 @@ import org.springframework.util.StringUtils;
|
|||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(DataSourceProperties.class)
|
||||
public class DataSourceInitialization implements
|
||||
ApplicationListener<ContextRefreshedEvent> {
|
||||
public class DataSourceInitialization {
|
||||
|
||||
private static Log logger = LogFactory.getLog(DataSourceAutoConfiguration.class);
|
||||
|
||||
@Autowired(required = false)
|
||||
@Autowired
|
||||
private DataSource dataSource;
|
||||
|
||||
@Autowired
|
||||
|
|
@ -61,34 +59,11 @@ public class DataSourceInitialization implements
|
|||
private boolean initialized = false;
|
||||
|
||||
@Bean
|
||||
public ApplicationListener<DataSourceInitializedEvent> dataSourceInitializedListener() {
|
||||
public ApplicationListener<DataSourceInitializedEvent> dataSourceInitializedListener(
|
||||
DataSource dataSource) {
|
||||
return new DataSourceInitializedListener();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(ContextRefreshedEvent event) {
|
||||
if (this.properties.isDeferDdl()) {
|
||||
boolean initialize = this.properties.isInitialize();
|
||||
if (!initialize) {
|
||||
logger.debug("Initialization disabled (not running DDL scripts)");
|
||||
return;
|
||||
}
|
||||
runSchemaScripts();
|
||||
}
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
protected void initialize() {
|
||||
if (!this.properties.isDeferDdl()) {
|
||||
boolean initialize = this.properties.isInitialize();
|
||||
if (!initialize) {
|
||||
logger.debug("Initialization disabled (not running DDL scripts)");
|
||||
return;
|
||||
}
|
||||
runSchemaScripts();
|
||||
}
|
||||
}
|
||||
|
||||
private void runSchemaScripts() {
|
||||
String schema = this.properties.getSchema();
|
||||
if (schema == null) {
|
||||
|
|
@ -172,6 +147,18 @@ public class DataSourceInitialization implements
|
|||
private class DataSourceInitializedListener implements
|
||||
ApplicationListener<DataSourceInitializedEvent> {
|
||||
|
||||
// Keep this in the nested class so that it doesn't have to be called before the
|
||||
// listener is instantiated (ordering problems otherwise)
|
||||
@PostConstruct
|
||||
protected void initialize() {
|
||||
boolean initialize = DataSourceInitialization.this.properties.isInitialize();
|
||||
if (!initialize) {
|
||||
logger.debug("Initialization disabled (not running DDL scripts)");
|
||||
return;
|
||||
}
|
||||
runSchemaScripts();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(DataSourceInitializedEvent event) {
|
||||
runDataScripts();
|
||||
|
|
|
|||
|
|
@ -46,8 +46,6 @@ public class DataSourceProperties implements BeanClassLoaderAware, InitializingB
|
|||
|
||||
private boolean initialize = true;
|
||||
|
||||
private boolean deferDdl = false;
|
||||
|
||||
private String platform = "all";
|
||||
|
||||
private String schema;
|
||||
|
|
@ -158,14 +156,6 @@ public class DataSourceProperties implements BeanClassLoaderAware, InitializingB
|
|||
this.initialize = initialize;
|
||||
}
|
||||
|
||||
public void setDeferDdl(boolean deferDdl) {
|
||||
this.deferDdl = deferDdl;
|
||||
}
|
||||
|
||||
public boolean isDeferDdl() {
|
||||
return this.deferDdl;
|
||||
}
|
||||
|
||||
public String getPlatform() {
|
||||
return this.platform;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,12 +16,22 @@
|
|||
|
||||
package org.springframework.boot.autoconfigure.liquibase;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.persistence.EntityManagerFactory;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import liquibase.integration.spring.SpringLiquibase;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
|
|
@ -32,15 +42,24 @@ import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
|||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.orm.jpa.AbstractEntityManagerFactoryBean;
|
||||
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.springframework.beans.factory.BeanFactoryUtils.beanNamesForTypeIncludingAncestors;
|
||||
import static org.springframework.beans.factory.BeanFactoryUtils.transformedBeanName;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for Liquibase.
|
||||
*
|
||||
* @author Marcel Overdijk
|
||||
* @author Dave Syer
|
||||
* @since 1.1.0
|
||||
*/
|
||||
@Configuration
|
||||
|
|
@ -53,6 +72,7 @@ public class LiquibaseAutoConfiguration {
|
|||
@Configuration
|
||||
@ConditionalOnMissingBean(SpringLiquibase.class)
|
||||
@EnableConfigurationProperties(LiquibaseProperties.class)
|
||||
@Import(LiquibaseJpaDependencyConfiguration.class)
|
||||
public static class LiquibaseConfiguration {
|
||||
|
||||
@Autowired
|
||||
|
|
@ -87,4 +107,58 @@ public class LiquibaseAutoConfiguration {
|
|||
return liquibase;
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(LocalContainerEntityManagerFactoryBean.class)
|
||||
@ConditionalOnBean(AbstractEntityManagerFactoryBean.class)
|
||||
protected static class LiquibaseJpaDependencyConfiguration implements
|
||||
BeanFactoryPostProcessor {
|
||||
|
||||
public static final String LIQUIBASE_JPA_BEAN_NAME = "liquibase";
|
||||
|
||||
@Override
|
||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
|
||||
|
||||
for (String beanName : getEntityManagerFactoryBeanNames(beanFactory)) {
|
||||
BeanDefinition definition = getBeanDefinition(beanName, beanFactory);
|
||||
definition.setDependsOn(StringUtils.addStringToArray(
|
||||
definition.getDependsOn(), LIQUIBASE_JPA_BEAN_NAME));
|
||||
}
|
||||
}
|
||||
|
||||
private static BeanDefinition getBeanDefinition(String beanName,
|
||||
ConfigurableListableBeanFactory beanFactory) {
|
||||
try {
|
||||
return beanFactory.getBeanDefinition(beanName);
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException e) {
|
||||
|
||||
BeanFactory parentBeanFactory = beanFactory.getParentBeanFactory();
|
||||
|
||||
if (parentBeanFactory instanceof ConfigurableListableBeanFactory) {
|
||||
return getBeanDefinition(beanName,
|
||||
(ConfigurableListableBeanFactory) parentBeanFactory);
|
||||
}
|
||||
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private static Iterable<String> getEntityManagerFactoryBeanNames(
|
||||
ListableBeanFactory beanFactory) {
|
||||
|
||||
Set<String> names = new HashSet<String>();
|
||||
names.addAll(asList(beanNamesForTypeIncludingAncestors(beanFactory,
|
||||
EntityManagerFactory.class, true, false)));
|
||||
|
||||
for (String factoryBeanName : beanNamesForTypeIncludingAncestors(beanFactory,
|
||||
AbstractEntityManagerFactoryBean.class, true, false)) {
|
||||
names.add(transformedBeanName(factoryBeanName));
|
||||
}
|
||||
|
||||
return names;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright 2012-2013 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
|
||||
*
|
||||
* http://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 javax.persistence.EntityManagerFactory;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceInitialization.DataSourceInitializedEvent;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
||||
public class DataSourceInitializedPublisher implements BeanPostProcessor {
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext applicationContext;
|
||||
private DataSource dataSource;
|
||||
|
||||
@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) {
|
||||
// Normally this will be the right DataSource
|
||||
this.dataSource = (DataSource) bean;
|
||||
}
|
||||
if (bean instanceof EntityManagerFactory && this.dataSource != null) {
|
||||
this.applicationContext.publishEvent(new DataSourceInitializedEvent(
|
||||
this.dataSource));
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
}
|
||||
|
|
@ -21,7 +21,6 @@ import java.util.Map;
|
|||
import javax.persistence.EntityManager;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.hibernate.jpa.boot.spi.Bootstrap;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
|
|
@ -29,15 +28,11 @@ import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
|
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceInitialization.DataSourceInitializedEvent;
|
||||
import org.springframework.boot.autoconfigure.orm.jpa.EntityManagerFactoryBuilder.EntityManagerFactoryBeanCallback;
|
||||
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration.HibernateEntityManagerCondition;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.event.ContextRefreshedEvent;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
|
||||
import org.springframework.orm.jpa.vendor.AbstractJpaVendorAdapter;
|
||||
|
|
@ -73,48 +68,7 @@ public class HibernateJpaAutoConfiguration extends JpaBaseConfiguration {
|
|||
|
||||
@Override
|
||||
protected Map<String, String> getVendorProperties() {
|
||||
return this.properties.getInitialHibernateProperties(this.dataSource);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected EntityManagerFactoryBeanCallback getVendorCallback() {
|
||||
final Map<String, String> map = this.properties
|
||||
.getHibernateProperties(this.dataSource);
|
||||
return new EntityManagerFactoryBeanCallback() {
|
||||
@Override
|
||||
public void execute(
|
||||
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean) {
|
||||
HibernateJpaAutoConfiguration.this.applicationContext
|
||||
.addApplicationListener(new DeferredSchemaAction(
|
||||
entityManagerFactoryBean, map));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private class DeferredSchemaAction implements
|
||||
ApplicationListener<ContextRefreshedEvent> {
|
||||
|
||||
private Map<String, String> map;
|
||||
private LocalContainerEntityManagerFactoryBean factory;
|
||||
|
||||
public DeferredSchemaAction(LocalContainerEntityManagerFactoryBean factory,
|
||||
Map<String, String> map) {
|
||||
this.factory = factory;
|
||||
this.map = map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(ContextRefreshedEvent event) {
|
||||
String ddlAuto = this.map.get("hibernate.hbm2ddl.auto");
|
||||
if (ddlAuto == null || "none".equals(ddlAuto) || "".equals(ddlAuto)) {
|
||||
return;
|
||||
}
|
||||
Bootstrap.getEntityManagerFactoryBuilder(
|
||||
this.factory.getPersistenceUnitInfo(), this.map).generateSchema();
|
||||
HibernateJpaAutoConfiguration.this.applicationContext
|
||||
.publishEvent(new DataSourceInitializedEvent(
|
||||
HibernateJpaAutoConfiguration.this.dataSource));
|
||||
}
|
||||
return this.properties.getHibernateProperties(this.dataSource);
|
||||
}
|
||||
|
||||
static class HibernateEntityManagerCondition extends SpringBootCondition {
|
||||
|
|
|
|||
|
|
@ -25,16 +25,23 @@ import org.springframework.beans.BeansException;
|
|||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.GenericBeanDefinition;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurationPackages;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration.DataSourceInitializedRegistrar;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.orm.jpa.JpaTransactionManager;
|
||||
import org.springframework.orm.jpa.JpaVendorAdapter;
|
||||
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
|
||||
|
|
@ -54,6 +61,7 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter
|
|||
* @author Oliver Gierke
|
||||
*/
|
||||
@EnableConfigurationProperties(JpaProperties.class)
|
||||
@Import(DataSourceInitializedRegistrar.class)
|
||||
public abstract class JpaBaseConfiguration implements BeanFactoryAware {
|
||||
|
||||
private ConfigurableListableBeanFactory beanFactory;
|
||||
|
|
@ -107,7 +115,9 @@ public abstract class JpaBaseConfiguration implements BeanFactoryAware {
|
|||
|
||||
protected abstract Map<String, String> getVendorProperties();
|
||||
|
||||
protected abstract EntityManagerFactoryBuilder.EntityManagerFactoryBeanCallback getVendorCallback();
|
||||
protected EntityManagerFactoryBuilder.EntityManagerFactoryBeanCallback getVendorCallback() {
|
||||
return null;
|
||||
}
|
||||
|
||||
protected String[] getPackagesToScan() {
|
||||
if (AutoConfigurationPackages.has(this.beanFactory)) {
|
||||
|
|
@ -145,4 +155,24 @@ public abstract class JpaBaseConfiguration implements BeanFactoryAware {
|
|||
|
||||
}
|
||||
|
||||
protected static class DataSourceInitializedRegistrar implements
|
||||
ImportBeanDefinitionRegistrar {
|
||||
|
||||
private static final String BEAN_NAME = "dataSourceInitializedPublisher";
|
||||
|
||||
@Override
|
||||
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
|
||||
BeanDefinitionRegistry registry) {
|
||||
if (!registry.containsBeanDefinition(BEAN_NAME)) {
|
||||
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
|
||||
beanDefinition.setBeanClass(DataSourceInitializedPublisher.class);
|
||||
beanDefinition.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.
|
||||
beanDefinition.setSynthetic(true);
|
||||
registry.registerBeanDefinition(BEAN_NAME, beanDefinition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -103,23 +103,13 @@ public class JpaProperties {
|
|||
|
||||
/**
|
||||
* Get configuration properties for the initialization of the main Hibernate
|
||||
* EntityManagerFactory. The result will always have ddl-auto=none, so that the schema
|
||||
* generation or validation can be deferred to a later stage.
|
||||
* @param dataSource the DataSource in case it is needed to determine the properties
|
||||
* @return some Hibernate properties for configuration
|
||||
*/
|
||||
public Map<String, String> getInitialHibernateProperties(DataSource dataSource) {
|
||||
return this.hibernate.getAdditionalProperties(this.properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the full configuration properties for the Hibernate EntityManagerFactory.
|
||||
* EntityManagerFactory.
|
||||
*
|
||||
* @param dataSource the DataSource in case it is needed to determine the properties
|
||||
* @return some Hibernate properties for configuration
|
||||
*/
|
||||
public Map<String, String> getHibernateProperties(DataSource dataSource) {
|
||||
return this.hibernate
|
||||
.getDeferredAdditionalProperties(this.properties, dataSource);
|
||||
return this.hibernate.getAdditionalProperties(this.properties, dataSource);
|
||||
}
|
||||
|
||||
public static class Hibernate {
|
||||
|
|
@ -130,8 +120,6 @@ public class JpaProperties {
|
|||
|
||||
private String ddlAuto;
|
||||
|
||||
private boolean deferDdl = true;
|
||||
|
||||
public Class<?> getNamingStrategy() {
|
||||
return this.namingStrategy;
|
||||
}
|
||||
|
|
@ -143,7 +131,7 @@ public class JpaProperties {
|
|||
@Deprecated
|
||||
public void setNamingstrategy(Class<?> namingStrategy) {
|
||||
logger.warn("The property spring.jpa.namingstrategy has been renamed, "
|
||||
+ "please update your configuration to use naming-strategy");
|
||||
+ "please update your configuration to use namingStrategy or naming-strategy or naming_strategy");
|
||||
this.setNamingStrategy(namingStrategy);
|
||||
}
|
||||
|
||||
|
|
@ -151,19 +139,8 @@ public class JpaProperties {
|
|||
return this.ddlAuto;
|
||||
}
|
||||
|
||||
public void setDeferDdl(boolean deferDdl) {
|
||||
this.deferDdl = deferDdl;
|
||||
}
|
||||
|
||||
public boolean isDeferDdl() {
|
||||
return this.deferDdl;
|
||||
}
|
||||
|
||||
private String getDeferredDdlAuto(Map<String, String> existing,
|
||||
private String getActualDdlAuto(Map<String, String> existing,
|
||||
DataSource dataSource) {
|
||||
if (!this.deferDdl) {
|
||||
return "none";
|
||||
}
|
||||
String ddlAuto = this.ddlAuto != null ? this.ddlAuto
|
||||
: getDefaultDdlAuto(dataSource);
|
||||
if (!isAlreadyProvided(existing, "hbm2ddl.auto") && !"none".equals(ddlAuto)) {
|
||||
|
|
@ -179,19 +156,8 @@ public class JpaProperties {
|
|||
this.ddlAuto = ddlAuto;
|
||||
}
|
||||
|
||||
private Map<String, String> getDeferredAdditionalProperties(
|
||||
Map<String, String> properties, DataSource dataSource) {
|
||||
Map<String, String> deferred = getAdditionalProperties(properties);
|
||||
String ddlAuto = getDeferredDdlAuto(properties, dataSource);
|
||||
if (StringUtils.hasText(ddlAuto) && !"none".equals(ddlAuto)) {
|
||||
deferred.put("hibernate.hbm2ddl.auto", ddlAuto);
|
||||
} else {
|
||||
deferred.remove("hibernate.hbm2ddl.auto");
|
||||
}
|
||||
return deferred;
|
||||
}
|
||||
|
||||
private Map<String, String> getAdditionalProperties(Map<String, String> existing) {
|
||||
private Map<String, String> getAdditionalProperties(Map<String, String> existing,
|
||||
DataSource dataSource) {
|
||||
Map<String, String> result = new HashMap<String, String>();
|
||||
if (!isAlreadyProvided(existing, "ejb.naming_strategy")
|
||||
&& this.namingStrategy != null) {
|
||||
|
|
@ -201,11 +167,12 @@ public class JpaProperties {
|
|||
result.put("hibernate.ejb.naming_strategy",
|
||||
DEFAULT_NAMING_STRATEGY.getName());
|
||||
}
|
||||
if (this.deferDdl) {
|
||||
result.remove("hibernate.hbm2ddl.auto");
|
||||
String ddlAuto = getActualDdlAuto(existing, dataSource);
|
||||
if (StringUtils.hasText(ddlAuto) && !"none".equals(ddlAuto)) {
|
||||
result.put("hibernate.hbm2ddl.auto", ddlAuto);
|
||||
}
|
||||
else {
|
||||
result.put("hibernate.hbm2ddl.auto", this.ddlAuto);
|
||||
result.remove("hibernate.hbm2ddl.auto");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ package org.springframework.boot.autoconfigure.orm.jpa;
|
|||
import javax.sql.DataSource;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration;
|
||||
import org.springframework.boot.test.EnvironmentTestUtils;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
|
||||
|
|
@ -43,9 +45,10 @@ public class HibernateJpaAutoConfigurationTests extends AbstractJpaAutoConfigura
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testDataScriptWithDdlAuto() throws Exception {
|
||||
public void testDataScriptWithMissingDdl() throws Exception {
|
||||
EnvironmentTestUtils.addEnvironment(this.context,
|
||||
"spring.datasource.data:classpath:/city.sql",
|
||||
// Missing:
|
||||
"spring.datasource.schema:classpath:/ddl.sql");
|
||||
setupTestConfiguration();
|
||||
this.context.refresh();
|
||||
|
|
@ -55,10 +58,9 @@ public class HibernateJpaAutoConfigurationTests extends AbstractJpaAutoConfigura
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testDataScriptWithDeferredDdl() throws Exception {
|
||||
public void testDataScript() throws Exception {
|
||||
EnvironmentTestUtils.addEnvironment(this.context,
|
||||
"spring.datasource.data:classpath:/city.sql",
|
||||
"spring.datasource.deferDdl:true");
|
||||
"spring.datasource.data:classpath:/city.sql");
|
||||
setupTestConfiguration();
|
||||
this.context.refresh();
|
||||
assertEquals(new Integer(1),
|
||||
|
|
@ -111,4 +113,26 @@ public class HibernateJpaAutoConfigurationTests extends AbstractJpaAutoConfigura
|
|||
assertThat(actual, not(equalTo("org.hibernate.cfg.EJB3NamingStrategy")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFlywayPlusValidation() throws Exception {
|
||||
EnvironmentTestUtils.addEnvironment(this.context,
|
||||
"spring.datasource.initialize:false",
|
||||
"flyway.locations:classpath:db/city",
|
||||
"spring.jpa.hibernate.ddl-auto:validate");
|
||||
setupTestConfiguration();
|
||||
this.context.register(FlywayAutoConfiguration.class);
|
||||
this.context.refresh();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLiquibasePlusValidation() throws Exception {
|
||||
EnvironmentTestUtils.addEnvironment(this.context,
|
||||
"spring.datasource.initialize:false",
|
||||
"liquibase.changeLog:classpath:db/changelog/db.changelog-city.yaml",
|
||||
"spring.jpa.hibernate.ddl-auto:validate");
|
||||
setupTestConfiguration();
|
||||
this.context.register(LiquibaseAutoConfiguration.class);
|
||||
this.context.refresh();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
databaseChangeLog:
|
||||
- changeSet:
|
||||
id: 1
|
||||
author: dsyer
|
||||
changes:
|
||||
- createTable:
|
||||
tableName: city
|
||||
columns:
|
||||
- column:
|
||||
name: id
|
||||
type: bigint
|
||||
autoIncrement: true
|
||||
constraints:
|
||||
primaryKey: true
|
||||
nullable: false
|
||||
- column:
|
||||
name: name
|
||||
type: varchar(50)
|
||||
constraints:
|
||||
nullable: false
|
||||
- column:
|
||||
name: state
|
||||
type: varchar(50)
|
||||
constraints:
|
||||
nullable: false
|
||||
- column:
|
||||
name: country
|
||||
type: varchar(50)
|
||||
constraints:
|
||||
nullable: false
|
||||
- column:
|
||||
name: map
|
||||
type: varchar(50)
|
||||
constraints:
|
||||
nullable: true
|
||||
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
CREATE TABLE CITY (
|
||||
id BIGINT GENERATED BY DEFAULT AS IDENTITY,
|
||||
name VARCHAR(30),
|
||||
state VARCHAR(30),
|
||||
country VARCHAR(30),
|
||||
map VARCHAR(30)
|
||||
);
|
||||
|
|
@ -156,7 +156,6 @@ content into your application; rather pick only the properties that you need.
|
|||
# DATASOURCE ({sc-spring-boot-autoconfigure}/jdbc/DataSourceAutoConfiguration.{sc-ext}[DataSourceAutoConfiguration] & {sc-spring-boot-autoconfigure}//jdbc/AbstractDataSourceConfiguration.{sc-ext}[AbstractDataSourceConfiguration])
|
||||
spring.datasource.name= # name of the data source
|
||||
spring.datasource.initialize=true # populate using data.sql
|
||||
spring.datasource.deferDdl= # flag to indicate that schema scripts will run after the application starts (default false)
|
||||
spring.datasource.schema= # a schema (DDL) script resource reference
|
||||
spring.datasource.data= # a data (DML) script resource reference
|
||||
spring.datasource.platform= # the platform to use in the schema resource (schema-${platform}.sql)
|
||||
|
|
@ -191,7 +190,6 @@ content into your application; rather pick only the properties that you need.
|
|||
spring.jpa.database=
|
||||
spring.jpa.generate-ddl=false # ignored by Hibernate, might be useful for other vendors
|
||||
spring.jpa.hibernate.naming-strategy= # naming classname
|
||||
spring.jpa.hibernate.defer-ddl=true # defer processing of DDL until application is running
|
||||
spring.jpa.hibernate.ddl-auto= # defaults to create-drop for embedded dbs
|
||||
|
||||
# SOLR ({sc-spring-boot-autoconfigure}/solr/SolrProperties.{sc-ext}[SolrProperties}])
|
||||
|
|
|
|||
Loading…
Reference in New Issue