diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration.java index 24cbb48272..c3fa2442d4 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration.java @@ -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 getEntityManagerFactoryBeanNames( + ListableBeanFactory beanFactory) { + + Set names = new HashSet(); + names.addAll(asList(beanNamesForTypeIncludingAncestors(beanFactory, + EntityManagerFactory.class, true, false))); + + for (String factoryBeanName : beanNamesForTypeIncludingAncestors(beanFactory, + AbstractEntityManagerFactoryBean.class, true, false)) { + names.add(transformedBeanName(factoryBeanName)); + } + + return names; + } + + } + } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceInitialization.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceInitialization.java index d61eb5242b..4adac0ee2a 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceInitialization.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceInitialization.java @@ -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 { +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 dataSourceInitializedListener() { + public ApplicationListener 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 { + // 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(); diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceProperties.java index 2f81c58454..1e888798b8 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceProperties.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceProperties.java @@ -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; } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfiguration.java index 05f14c10ff..596916662d 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfiguration.java @@ -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 getEntityManagerFactoryBeanNames( + ListableBeanFactory beanFactory) { + + Set names = new HashSet(); + names.addAll(asList(beanNamesForTypeIncludingAncestors(beanFactory, + EntityManagerFactory.class, true, false))); + + for (String factoryBeanName : beanNamesForTypeIncludingAncestors(beanFactory, + AbstractEntityManagerFactoryBean.class, true, false)) { + names.add(transformedBeanName(factoryBeanName)); + } + + return names; + } + + } + } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/DataSourceInitializedPublisher.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/DataSourceInitializedPublisher.java new file mode 100644 index 0000000000..751421c382 --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/DataSourceInitializedPublisher.java @@ -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; + } +} \ No newline at end of file diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfiguration.java index a9e998f685..3948858c8f 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfiguration.java @@ -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 getVendorProperties() { - return this.properties.getInitialHibernateProperties(this.dataSource); - } - - @Override - protected EntityManagerFactoryBeanCallback getVendorCallback() { - final Map 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 { - - private Map map; - private LocalContainerEntityManagerFactoryBean factory; - - public DeferredSchemaAction(LocalContainerEntityManagerFactoryBean factory, - Map 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 { diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaBaseConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaBaseConfiguration.java index e6acf57fcd..370dce5e2a 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaBaseConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaBaseConfiguration.java @@ -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 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); + } + } + } + } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaProperties.java index 085923b648..463802315c 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaProperties.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaProperties.java @@ -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 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 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 existing, + private String getActualDdlAuto(Map 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 getDeferredAdditionalProperties( - Map properties, DataSource dataSource) { - Map 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 getAdditionalProperties(Map existing) { + private Map getAdditionalProperties(Map existing, + DataSource dataSource) { Map result = new HashMap(); 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; } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfigurationTests.java index a4561af517..b81bdfc5ba 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfigurationTests.java @@ -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(); + } + } diff --git a/spring-boot-autoconfigure/src/test/resources/db/changelog/db.changelog-city.yaml b/spring-boot-autoconfigure/src/test/resources/db/changelog/db.changelog-city.yaml new file mode 100644 index 0000000000..ae263b108a --- /dev/null +++ b/spring-boot-autoconfigure/src/test/resources/db/changelog/db.changelog-city.yaml @@ -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 + \ No newline at end of file diff --git a/spring-boot-autoconfigure/src/test/resources/db/city/V1__init.sql b/spring-boot-autoconfigure/src/test/resources/db/city/V1__init.sql new file mode 100644 index 0000000000..3f9f9d079f --- /dev/null +++ b/spring-boot-autoconfigure/src/test/resources/db/city/V1__init.sql @@ -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) +); \ No newline at end of file diff --git a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc index b36e9417d2..5c1c815a90 100644 --- a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc +++ b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc @@ -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}])