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 03eb7957f6d..e4ad638d90f 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 @@ -20,9 +20,7 @@ import javax.annotation.PostConstruct; import javax.sql.DataSource; import org.flywaydb.core.Flyway; -import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; @@ -33,7 +31,6 @@ 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.DependsOn; import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; @@ -96,26 +93,6 @@ public class FlywayAutoConfiguration { return flyway; } - @Bean - @DependsOn("flyway") - protected BeanPostProcessor forceFlywayToInitialize() { - - return new BeanPostProcessor() { - - @Override - public Object postProcessAfterInitialization(Object bean, String beanName) - throws BeansException { - return bean; - } - - @Override - public Object postProcessBeforeInitialization(Object bean, String beanName) - throws BeansException { - return bean; - } - - }; - } } } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/EntityManagerFactoryBuilder.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/EntityManagerFactoryBuilder.java index 15fc27661b7..f603e422479 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/EntityManagerFactoryBuilder.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/EntityManagerFactoryBuilder.java @@ -146,9 +146,6 @@ public class EntityManagerFactoryBuilder { entityManagerFactoryBean.setPackagesToScan(this.packagesToScan); entityManagerFactoryBean.getJpaPropertyMap().putAll( EntityManagerFactoryBuilder.this.properties.getProperties()); - entityManagerFactoryBean.getJpaPropertyMap().putAll( - EntityManagerFactoryBuilder.this.properties - .getHibernateProperties(this.dataSource)); return entityManagerFactoryBean; } 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 2d059bb912e..253d25301f4 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 @@ -16,8 +16,14 @@ package org.springframework.boot.autoconfigure.orm.jpa; +import java.util.Map; + import javax.persistence.EntityManager; +import javax.sql.DataSource; +import org.hibernate.jpa.boot.spi.Bootstrap; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionOutcome; @@ -25,9 +31,11 @@ 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.orm.jpa.HibernateJpaAutoConfiguration.HibernateEntityManagerCondition; +import org.springframework.context.ApplicationListener; 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; @@ -45,13 +53,40 @@ import org.springframework.util.ClassUtils; EnableTransactionManagement.class, EntityManager.class }) @Conditional(HibernateEntityManagerCondition.class) @AutoConfigureAfter(DataSourceAutoConfiguration.class) -public class HibernateJpaAutoConfiguration extends JpaBaseConfiguration { +public class HibernateJpaAutoConfiguration extends JpaBaseConfiguration implements + ApplicationListener { + + @Autowired + private JpaProperties properties; + + @Autowired + private DataSource dataSource; + + @Autowired + private BeanFactory beanFactory; @Override protected AbstractJpaVendorAdapter createJpaVendorAdapter() { return new HibernateJpaVendorAdapter(); } + @Override + protected Map getVendorProperties() { + return this.properties.getInitialHibernateProperties(this.dataSource); + } + + @Override + public void onApplicationEvent(ContextRefreshedEvent event) { + Map map = this.properties.getHibernateProperties(this.dataSource); + if ("none".equals(map.get("hibernate.hbm2ddl.auto"))) { + return; + } + LocalContainerEntityManagerFactoryBean factory = this.beanFactory + .getBean(LocalContainerEntityManagerFactoryBean.class); + Bootstrap.getEntityManagerFactoryBuilder(factory.getPersistenceUnitInfo(), map) + .generateSchema(); + } + static class HibernateEntityManagerCondition extends SpringBootCondition { private static String[] CLASS_NAMES = { 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 4d30acaf372..8b07c87e05f 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 @@ -17,6 +17,7 @@ package org.springframework.boot.autoconfigure.orm.jpa; import java.util.List; +import java.util.Map; import javax.sql.DataSource; @@ -97,11 +98,14 @@ public abstract class JpaBaseConfiguration implements BeanFactoryAware { @ConditionalOnMissingBean public LocalContainerEntityManagerFactoryBean entityManagerFactory( EntityManagerFactoryBuilder factory) { - return factory.dataSource(this.dataSource).packages(getPackagesToScan()).build(); + return factory.dataSource(this.dataSource).packages(getPackagesToScan()) + .properties(getVendorProperties()).build(); } protected abstract AbstractJpaVendorAdapter createJpaVendorAdapter(); + protected abstract Map getVendorProperties(); + protected String[] getPackagesToScan() { List basePackages = AutoConfigurationPackages.get(this.beanFactory); return basePackages.toArray(new String[basePackages.size()]); 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 3934e44a414..cf61e897db0 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 @@ -95,8 +95,27 @@ public class JpaProperties { this.hibernate = hibernate; } + /** + * Get configuration properties for the initialization of the main + * 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 the Hibernate 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.getAdditionalProperties(this.properties, dataSource); + return this.hibernate + .getDeferredAdditionalProperties(this.properties, dataSource); } public static class Hibernate { @@ -116,15 +135,35 @@ public class JpaProperties { } public String getDdlAuto() { - return this.ddlAuto; + return "none"; + } + + private String getDeferredDdlAuto(Map existing, + DataSource dataSource) { + String ddlAuto = this.ddlAuto != null ? this.ddlAuto + : getDefaultDdlAuto(dataSource); + if (!isAlreadyProvided(existing, "hbm2ddl.auto") && !"none".equals(ddlAuto)) { + return ddlAuto; + } + if (isAlreadyProvided(existing, "hbm2ddl.auto")) { + return (String) existing.get("hibernate.hbm2ddl.auto"); + } + return "none"; } public void setDdlAuto(String ddlAuto) { this.ddlAuto = ddlAuto; } - private Map getAdditionalProperties(Map existing, - DataSource dataSource) { + private Map getDeferredAdditionalProperties( + Map properties, DataSource dataSource) { + Map deferred = getAdditionalProperties(properties); + deferred.put("hibernate.hbm2ddl.auto", + getDeferredDdlAuto(properties, dataSource)); + return deferred; + } + + private Map getAdditionalProperties(Map existing) { Map result = new HashMap(); if (!isAlreadyProvided(existing, "ejb.naming_strategy") && this.namingStrategy != null) { @@ -134,11 +173,7 @@ public class JpaProperties { result.put("hibernate.ejb.naming_strategy", DEFAULT_NAMING_STRATEGY.getName()); } - String ddlAuto = this.ddlAuto != null ? this.ddlAuto - : getDefaultDdlAuto(dataSource); - if (!isAlreadyProvided(existing, "hbm2ddl.auto") && !"none".equals(ddlAuto)) { - result.put("hibernate.hbm2ddl.auto", ddlAuto); - } + result.put("hibernate.hbm2ddl.auto", "none"); return result; } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/CustomHibernateJpaAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/CustomHibernateJpaAutoConfigurationTests.java index dd232df8efb..4b4908bba31 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/CustomHibernateJpaAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/CustomHibernateJpaAutoConfigurationTests.java @@ -16,6 +16,8 @@ package org.springframework.boot.autoconfigure.orm.jpa; +import javax.sql.DataSource; + import org.junit.After; import org.junit.Test; import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; @@ -26,7 +28,6 @@ import org.springframework.boot.autoconfigure.orm.jpa.test.City; import org.springframework.boot.test.EnvironmentTestUtils; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Configuration; -import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertThat; @@ -58,11 +59,12 @@ public class CustomHibernateJpaAutoConfigurationTests { PropertyPlaceholderAutoConfiguration.class, HibernateJpaAutoConfiguration.class); this.context.refresh(); - LocalContainerEntityManagerFactoryBean bean = this.context - .getBean(LocalContainerEntityManagerFactoryBean.class); - String actual = (String) bean.getJpaPropertyMap().get("hibernate.hbm2ddl.auto"); - // No default (let Hibernate choose) - assertThat(actual, equalTo(null)); + JpaProperties bean = this.context.getBean(JpaProperties.class); + DataSource dataSource = this.context.getBean(DataSource.class); + String actual = (String) bean.getHibernateProperties(dataSource).get( + "hibernate.hbm2ddl.auto"); + // Default is generic and safe + assertThat(actual, equalTo("none")); } @Test @@ -74,9 +76,10 @@ public class CustomHibernateJpaAutoConfigurationTests { PropertyPlaceholderAutoConfiguration.class, HibernateJpaAutoConfiguration.class); this.context.refresh(); - LocalContainerEntityManagerFactoryBean bean = this.context - .getBean(LocalContainerEntityManagerFactoryBean.class); - String actual = (String) bean.getJpaPropertyMap().get("hibernate.hbm2ddl.auto"); + JpaProperties bean = this.context.getBean(JpaProperties.class); + DataSource dataSource = this.context.getBean(DataSource.class); + String actual = (String) bean.getHibernateProperties(dataSource).get( + "hibernate.hbm2ddl.auto"); assertThat(actual, equalTo("create-drop")); }