Default Hibernate DDL auto to none with Flyway/Liquibase

This commit adds a strategy interface to specific if a given DataSource
has its schema managed. The Hibernate auto-configuration uses it to set
it to "none" if a mechanism to initialize the DataSource is
found and "create-drop" otherwise.

Both Flyway and Liquibase implements that strategy interface and
register it in the context accordingly.

Closes gh-9262
This commit is contained in:
Stephane Nicoll 2017-08-21 17:48:42 +02:00
parent 8babd5d4c5
commit afda0ec129
18 changed files with 427 additions and 78 deletions

View File

@ -80,6 +80,13 @@ public class FlywayAutoConfiguration {
return new StringOrNumberToMigrationVersionConverter(); return new StringOrNumberToMigrationVersionConverter();
} }
@Bean
public FlywaySchemaManagementProvider flywayDefaultDdlModeProvider(
ObjectProvider<List<Flyway>> flyways) {
return new FlywaySchemaManagementProvider(
flyways.getIfAvailable(Collections::emptyList));
}
@Configuration @Configuration
@ConditionalOnMissingBean(Flyway.class) @ConditionalOnMissingBean(Flyway.class)
@EnableConfigurationProperties(FlywayProperties.class) @EnableConfigurationProperties(FlywayProperties.class)

View File

@ -0,0 +1,52 @@
/*
* Copyright 2012-2017 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.flyway;
import java.util.List;
import javax.sql.DataSource;
import org.flywaydb.core.Flyway;
import org.springframework.boot.jdbc.SchemaManagement;
import org.springframework.boot.jdbc.SchemaManagementProvider;
/**
* A Flyway {@link SchemaManagementProvider} that determines if the schema is managed by
* looking at available {@link Flyway} instances.
*
* @author Stephane Nicoll
*/
class FlywaySchemaManagementProvider implements SchemaManagementProvider {
private final List<Flyway> flyways;
FlywaySchemaManagementProvider(List<Flyway> flyways) {
this.flyways = flyways;
}
@Override
public SchemaManagement getSchemaManagement(DataSource dataSource) {
for (Flyway flyway : this.flyways) {
if (dataSource.equals(flyway.getDataSource())) {
return SchemaManagement.MANAGED;
}
}
return SchemaManagement.UNMANAGED;
}
}

View File

@ -17,6 +17,8 @@
package org.springframework.boot.autoconfigure.liquibase; package org.springframework.boot.autoconfigure.liquibase;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import javax.persistence.EntityManagerFactory; import javax.persistence.EntityManagerFactory;
@ -65,6 +67,13 @@ import org.springframework.util.ReflectionUtils;
HibernateJpaAutoConfiguration.class }) HibernateJpaAutoConfiguration.class })
public class LiquibaseAutoConfiguration { public class LiquibaseAutoConfiguration {
@Bean
public LiquibaseSchemaManagementProvider liquibaseDefaultDdlModeProvider(
ObjectProvider<List<SpringLiquibase>> liquibases) {
return new LiquibaseSchemaManagementProvider(
liquibases.getIfAvailable(Collections::emptyList));
}
@Configuration @Configuration
@ConditionalOnMissingBean(SpringLiquibase.class) @ConditionalOnMissingBean(SpringLiquibase.class)
@EnableConfigurationProperties(LiquibaseProperties.class) @EnableConfigurationProperties(LiquibaseProperties.class)

View File

@ -0,0 +1,52 @@
/*
* Copyright 2012-2017 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.liquibase;
import java.util.List;
import javax.sql.DataSource;
import liquibase.integration.spring.SpringLiquibase;
import org.springframework.boot.jdbc.SchemaManagement;
import org.springframework.boot.jdbc.SchemaManagementProvider;
/**
* A Liquibase {@link SchemaManagementProvider} that determines if the schema is managed
* by looking at available {@link SpringLiquibase} instances.
*
* @author Stephane Nicoll
*/
class LiquibaseSchemaManagementProvider implements SchemaManagementProvider {
private final List<SpringLiquibase> liquibases;
LiquibaseSchemaManagementProvider(List<SpringLiquibase> liquibases) {
this.liquibases = liquibases;
}
@Override
public SchemaManagement getSchemaManagement(DataSource dataSource) {
for (SpringLiquibase liquibase : this.liquibases) {
if (dataSource.equals(liquibase.getDataSource())) {
return SchemaManagement.MANAGED;
}
}
return SchemaManagement.UNMANAGED;
}
}

View File

@ -28,6 +28,7 @@ import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition; import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.boot.autoconfigure.jdbc.DataSourceInitializedEvent; import org.springframework.boot.autoconfigure.jdbc.DataSourceInitializedEvent;
import org.springframework.boot.jdbc.EmbeddedDatabaseConnection;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata; import org.springframework.core.type.AnnotationMetadata;
@ -89,8 +90,10 @@ class DataSourceInitializedPublisher implements BeanPostProcessor {
if (this.properties == null) { if (this.properties == null) {
return true; // better safe than sorry return true; // better safe than sorry
} }
Map<String, String> hibernate = this.properties String defaultDdlAuto = (EmbeddedDatabaseConnection.isEmbedded(dataSource)
.getHibernateProperties(dataSource); ? "create-drop" : "none");
Map<String, String> hibernate = this.properties.getHibernateProperties(
defaultDdlAuto);
if (hibernate.containsKey("hibernate.hbm2ddl.auto")) { if (hibernate.containsKey("hibernate.hbm2ddl.auto")) {
return true; return true;
} }

View File

@ -0,0 +1,64 @@
/*
* Copyright 2012-2017 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 java.util.List;
import javax.sql.DataSource;
import org.springframework.boot.jdbc.EmbeddedDatabaseConnection;
import org.springframework.boot.jdbc.SchemaManagement;
import org.springframework.boot.jdbc.SchemaManagementProvider;
/**
* A {@link SchemaManagementProvider} that invokes a configurable number of
* {@link SchemaManagementProvider} instances for embedded data sources only.
*
* @author Stephane Nicoll
*/
class HibernateDefaultDdlAutoProvider implements SchemaManagementProvider {
private final List<SchemaManagementProvider> providers;
HibernateDefaultDdlAutoProvider(List<SchemaManagementProvider> providers) {
this.providers = providers;
}
public String getDefaultDdlAuto(DataSource dataSource) {
if (!EmbeddedDatabaseConnection.isEmbedded(dataSource)) {
return "none";
}
SchemaManagement schemaManagement = getSchemaManagement(dataSource);
if (SchemaManagement.MANAGED.equals(schemaManagement)) {
return "none";
}
return "create-drop";
}
@Override
public SchemaManagement getSchemaManagement(DataSource dataSource) {
for (SchemaManagementProvider provider : this.providers) {
SchemaManagement schemaManagement = provider.getSchemaManagement(dataSource);
if (SchemaManagement.MANAGED.equals(schemaManagement)) {
return schemaManagement;
}
}
return SchemaManagement.UNMANAGED;
}
}

View File

@ -17,7 +17,9 @@
package org.springframework.boot.autoconfigure.orm.jpa; package org.springframework.boot.autoconfigure.orm.jpa;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import javax.persistence.EntityManager; import javax.persistence.EntityManager;
@ -37,6 +39,7 @@ import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration.HibernateEntityManagerCondition; import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration.HibernateEntityManagerCondition;
import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers; import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers;
import org.springframework.boot.jdbc.SchemaManagementProvider;
import org.springframework.boot.orm.jpa.hibernate.SpringJtaPlatform; import org.springframework.boot.orm.jpa.hibernate.SpringJtaPlatform;
import org.springframework.context.annotation.ConditionContext; import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Conditional;
@ -85,12 +88,17 @@ public class HibernateJpaAutoConfiguration extends JpaBaseConfiguration {
"org.hibernate.engine.transaction.jta.platform.internal.WebSphereExtendedJtaPlatform", "org.hibernate.engine.transaction.jta.platform.internal.WebSphereExtendedJtaPlatform",
"org.hibernate.service.jta.platform.internal.WebSphereExtendedJtaPlatform", }; "org.hibernate.service.jta.platform.internal.WebSphereExtendedJtaPlatform", };
private final HibernateDefaultDdlAutoProvider defaultDdlAutoProvider;
public HibernateJpaAutoConfiguration(DataSource dataSource, public HibernateJpaAutoConfiguration(DataSource dataSource,
JpaProperties jpaProperties, JpaProperties jpaProperties,
ObjectProvider<JtaTransactionManager> jtaTransactionManager, ObjectProvider<JtaTransactionManager> jtaTransactionManager,
ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) { ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers,
ObjectProvider<List<SchemaManagementProvider>> providers) {
super(dataSource, jpaProperties, jtaTransactionManager, super(dataSource, jpaProperties, jtaTransactionManager,
transactionManagerCustomizers); transactionManagerCustomizers);
this.defaultDdlAutoProvider = new HibernateDefaultDdlAutoProvider(
providers.getIfAvailable(Collections::emptyList));
} }
@Override @Override
@ -101,7 +109,9 @@ public class HibernateJpaAutoConfiguration extends JpaBaseConfiguration {
@Override @Override
protected Map<String, Object> getVendorProperties() { protected Map<String, Object> getVendorProperties() {
Map<String, Object> vendorProperties = new LinkedHashMap<>(); Map<String, Object> vendorProperties = new LinkedHashMap<>();
vendorProperties.putAll(getProperties().getHibernateProperties(getDataSource())); String defaultDdlMode = this.defaultDdlAutoProvider.getDefaultDdlAuto(
getDataSource());
vendorProperties.putAll(getProperties().getHibernateProperties(defaultDdlMode));
return vendorProperties; return vendorProperties;
} }

View File

@ -22,7 +22,6 @@ import java.util.Map;
import javax.sql.DataSource; import javax.sql.DataSource;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.EmbeddedDatabaseConnection;
import org.springframework.orm.jpa.vendor.Database; import org.springframework.orm.jpa.vendor.Database;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
@ -119,11 +118,11 @@ public class JpaProperties {
/** /**
* Get configuration properties for the initialization of the main Hibernate * Get configuration properties for the initialization of the main Hibernate
* EntityManagerFactory. * EntityManagerFactory.
* @param dataSource the DataSource in case it is needed to determine the properties * @param defaultDdlAuto the default DDL auto (can be {@code null})
* @return some Hibernate properties for configuration * @return some Hibernate properties for configuration
*/ */
public Map<String, String> getHibernateProperties(DataSource dataSource) { public Map<String, String> getHibernateProperties(String defaultDdlAuto) {
return this.hibernate.getAdditionalProperties(this.properties, dataSource); return this.hibernate.getAdditionalProperties(this.properties, defaultDdlAuto);
} }
/** /**
@ -146,8 +145,8 @@ public class JpaProperties {
/** /**
* DDL mode. This is actually a shortcut for the "hibernate.hbm2ddl.auto" * DDL mode. This is actually a shortcut for the "hibernate.hbm2ddl.auto"
* property. Default to "create-drop" when using an embedded database, "none" * property. Default to "create-drop" when using an embedded database and no
* otherwise. * schema manager was detected, "none" otherwise.
*/ */
private String ddlAuto; private String ddlAuto;
@ -181,11 +180,11 @@ public class JpaProperties {
} }
private Map<String, String> getAdditionalProperties(Map<String, String> existing, private Map<String, String> getAdditionalProperties(Map<String, String> existing,
DataSource dataSource) { String defaultDdlAuto) {
Map<String, String> result = new HashMap<>(existing); Map<String, String> result = new HashMap<>(existing);
applyNewIdGeneratorMappings(result); applyNewIdGeneratorMappings(result);
getNaming().applyNamingStrategies(result); getNaming().applyNamingStrategies(result);
String ddlAuto = getOrDeduceDdlAuto(existing, dataSource); String ddlAuto = determineDdlAuto(existing, defaultDdlAuto);
if (StringUtils.hasText(ddlAuto) && !"none".equals(ddlAuto)) { if (StringUtils.hasText(ddlAuto) && !"none".equals(ddlAuto)) {
result.put("hibernate.hbm2ddl.auto", ddlAuto); result.put("hibernate.hbm2ddl.auto", ddlAuto);
} }
@ -205,10 +204,9 @@ public class JpaProperties {
} }
} }
private String getOrDeduceDdlAuto(Map<String, String> existing, private String determineDdlAuto(Map<String, String> existing,
DataSource dataSource) { String defaultDdlAuto) {
String ddlAuto = (this.ddlAuto != null ? this.ddlAuto String ddlAuto = (this.ddlAuto != null ? this.ddlAuto : defaultDdlAuto);
: getDefaultDdlAuto(dataSource));
if (!existing.containsKey("hibernate." + "hbm2ddl.auto") if (!existing.containsKey("hibernate." + "hbm2ddl.auto")
&& !"none".equals(ddlAuto)) { && !"none".equals(ddlAuto)) {
return ddlAuto; return ddlAuto;
@ -219,13 +217,6 @@ public class JpaProperties {
return "none"; return "none";
} }
private String getDefaultDdlAuto(DataSource dataSource) {
if (EmbeddedDatabaseConnection.isEmbedded(dataSource)) {
return "create-drop";
}
return "none";
}
} }
public static class Naming { public static class Naming {

View File

@ -40,6 +40,7 @@ import org.springframework.beans.factory.BeanCreationException;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder; import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration; import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration;
import org.springframework.boot.jdbc.SchemaManagement;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder; import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.boot.test.util.TestPropertyValues; import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext;
@ -114,6 +115,19 @@ public class FlywayAutoConfigurationTests {
.isEqualTo(this.context.getBean("flywayDataSource")); .isEqualTo(this.context.getBean("flywayDataSource"));
} }
@Test
public void schemaManagementProviderDetectsDataSource() throws Exception {
registerAndRefresh(FlywayDataSourceConfiguration.class,
EmbeddedDataSourceConfiguration.class, FlywayAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
FlywaySchemaManagementProvider schemaManagementProvider = this.context.getBean(
FlywaySchemaManagementProvider.class);
assertThat(schemaManagementProvider.getSchemaManagement(this.context.getBean(
DataSource.class))).isEqualTo(SchemaManagement.UNMANAGED);
assertThat(schemaManagementProvider.getSchemaManagement(this.context.getBean(
"flywayDataSource", DataSource.class))).isEqualTo(SchemaManagement.MANAGED);
}
@Test @Test
public void defaultFlyway() throws Exception { public void defaultFlyway() throws Exception {
registerAndRefresh(EmbeddedDataSourceConfiguration.class, registerAndRefresh(EmbeddedDataSourceConfiguration.class,

View File

@ -59,44 +59,6 @@ public class CustomHibernateJpaAutoConfigurationTests {
this.context.close(); this.context.close();
} }
@Test
public void testDefaultDdlAutoForMySql() throws Exception {
// Set up environment so we get a MySQL database but don't require server to be
// running...
TestPropertyValues
.of("spring.datasource.type:"
+ org.apache.tomcat.jdbc.pool.DataSource.class.getName(),
"spring.datasource.database:mysql",
"spring.datasource.url:jdbc:mysql://localhost/nonexistent",
"spring.datasource.initialize:false", "spring.jpa.database:MYSQL")
.applyTo(this.context);
this.context.register(TestConfiguration.class, DataSourceAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class,
HibernateJpaAutoConfiguration.class);
this.context.refresh();
JpaProperties bean = this.context.getBean(JpaProperties.class);
DataSource dataSource = this.context.getBean(DataSource.class);
String actual = bean.getHibernateProperties(dataSource)
.get("hibernate.hbm2ddl.auto");
// Default is generic and safe
assertThat(actual).isNull();
}
@Test
public void testDefaultDdlAutoForEmbedded() throws Exception {
TestPropertyValues.of("spring.datasource.initialize:false").applyTo(this.context);
this.context.register(TestConfiguration.class,
EmbeddedDataSourceConfiguration.class,
PropertyPlaceholderAutoConfiguration.class,
HibernateJpaAutoConfiguration.class);
this.context.refresh();
JpaProperties bean = this.context.getBean(JpaProperties.class);
DataSource dataSource = this.context.getBean(DataSource.class);
String actual = bean.getHibernateProperties(dataSource)
.get("hibernate.hbm2ddl.auto");
assertThat(actual).isEqualTo("create-drop");
}
@Test @Test
public void testNamingStrategyDelegatorTakesPrecedence() { public void testNamingStrategyDelegatorTakesPrecedence() {
TestPropertyValues TestPropertyValues
@ -108,8 +70,7 @@ public class CustomHibernateJpaAutoConfigurationTests {
HibernateJpaAutoConfiguration.class); HibernateJpaAutoConfiguration.class);
this.context.refresh(); this.context.refresh();
JpaProperties bean = this.context.getBean(JpaProperties.class); JpaProperties bean = this.context.getBean(JpaProperties.class);
DataSource dataSource = this.context.getBean(DataSource.class); Map<String, String> hibernateProperties = bean.getHibernateProperties("create-drop");
Map<String, String> hibernateProperties = bean.getHibernateProperties(dataSource);
assertThat(hibernateProperties.get("hibernate.ejb.naming_strategy")).isNull(); assertThat(hibernateProperties.get("hibernate.ejb.naming_strategy")).isNull();
} }

View File

@ -0,0 +1,104 @@
/*
* Copyright 2012-2017 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 java.util.Collections;
import javax.sql.DataSource;
import org.junit.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.jdbc.SchemaManagement;
import org.springframework.boot.jdbc.SchemaManagementProvider;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link HibernateDefaultDdlAutoProvider}.
*
* @author Stephane Nicoll
*/
public class HibernateDefaultDdlAutoProviderTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class,
HibernateJpaAutoConfiguration.class))
.withPropertyValues("spring.datasource.initialize:false");
@Test
public void defaultDdlAutoForMysql() {
// Set up environment so we get a MySQL database but don't require server to be
// running...
this.contextRunner.withPropertyValues(
"spring.datasource.type:"
+ org.apache.tomcat.jdbc.pool.DataSource.class.getName(),
"spring.datasource.database:mysql",
"spring.datasource.url:jdbc:mysql://localhost/nonexistent",
"spring.jpa.database:MYSQL"
).run((context) -> {
HibernateDefaultDdlAutoProvider ddlAutoProvider = new HibernateDefaultDdlAutoProvider(
Collections.emptyList());
assertThat(ddlAutoProvider.getDefaultDdlAuto(
context.getBean(DataSource.class))).isEqualTo("none");
});
}
@Test
public void defaultDDlAutoForEmbedded() {
this.contextRunner.run((context) -> {
HibernateDefaultDdlAutoProvider ddlAutoProvider = new HibernateDefaultDdlAutoProvider(
Collections.emptyList());
assertThat(ddlAutoProvider.getDefaultDdlAuto(
context.getBean(DataSource.class))).isEqualTo("create-drop");
});
}
@Test
public void defaultDDlAutoForEmbeddedWithPositiveContributor() {
this.contextRunner.run((context) -> {
DataSource dataSource = context.getBean(DataSource.class);
SchemaManagementProvider provider = mock(SchemaManagementProvider.class);
given(provider.getSchemaManagement(dataSource))
.willReturn(SchemaManagement.MANAGED);
HibernateDefaultDdlAutoProvider ddlAutoProvider = new HibernateDefaultDdlAutoProvider(
Collections.singletonList(provider));
assertThat(ddlAutoProvider.getDefaultDdlAuto(
dataSource)).isEqualTo("none");
});
}
@Test
public void defaultDDlAutoForEmbeddedWithNegativeContributor() {
this.contextRunner.run((context) -> {
DataSource dataSource = context.getBean(DataSource.class);
SchemaManagementProvider provider = mock(SchemaManagementProvider.class);
given(provider.getSchemaManagement(dataSource))
.willReturn(SchemaManagement.UNMANAGED);
HibernateDefaultDdlAutoProvider ddlAutoProvider = new HibernateDefaultDdlAutoProvider(
Collections.singletonList(provider));
assertThat(ddlAutoProvider.getDefaultDdlAuto(
dataSource)).isEqualTo("create-drop");
});
}
}

View File

@ -101,6 +101,13 @@ public class HibernateJpaAutoConfigurationTests
.isTrue(); .isTrue();
} }
@Test
public void testFlywaySwitchOffDdlAuto() throws Exception {
load(new Class<?>[0], new Class<?>[] { FlywayAutoConfiguration.class },
"spring.datasource.initialize:false",
"spring.flyway.locations:classpath:db/city");
}
@Test @Test
public void testFlywayPlusValidation() throws Exception { public void testFlywayPlusValidation() throws Exception {
load(new Class<?>[0], new Class<?>[] { FlywayAutoConfiguration.class }, load(new Class<?>[0], new Class<?>[] { FlywayAutoConfiguration.class },

View File

@ -62,7 +62,7 @@ public class JpaPropertiesTests {
public void noCustomNamingStrategy() throws Exception { public void noCustomNamingStrategy() throws Exception {
JpaProperties properties = load(); JpaProperties properties = load();
Map<String, String> hibernateProperties = properties Map<String, String> hibernateProperties = properties
.getHibernateProperties(mockStandaloneDataSource()); .getHibernateProperties("none");
assertThat(hibernateProperties) assertThat(hibernateProperties)
.doesNotContainKeys("hibernate.ejb.naming_strategy"); .doesNotContainKeys("hibernate.ejb.naming_strategy");
assertThat(hibernateProperties).containsEntry( assertThat(hibernateProperties).containsEntry(
@ -79,7 +79,7 @@ public class JpaPropertiesTests {
"spring.jpa.hibernate.naming.implicit-strategy:com.example.Implicit", "spring.jpa.hibernate.naming.implicit-strategy:com.example.Implicit",
"spring.jpa.hibernate.naming.physical-strategy:com.example.Physical"); "spring.jpa.hibernate.naming.physical-strategy:com.example.Physical");
Map<String, String> hibernateProperties = properties Map<String, String> hibernateProperties = properties
.getHibernateProperties(mockStandaloneDataSource()); .getHibernateProperties("none");
assertThat(hibernateProperties).contains( assertThat(hibernateProperties).contains(
entry("hibernate.implicit_naming_strategy", "com.example.Implicit"), entry("hibernate.implicit_naming_strategy", "com.example.Implicit"),
entry("hibernate.physical_naming_strategy", "com.example.Physical")); entry("hibernate.physical_naming_strategy", "com.example.Physical"));
@ -93,7 +93,7 @@ public class JpaPropertiesTests {
"spring.jpa.properties.hibernate.implicit_naming_strategy:com.example.Implicit", "spring.jpa.properties.hibernate.implicit_naming_strategy:com.example.Implicit",
"spring.jpa.properties.hibernate.physical_naming_strategy:com.example.Physical"); "spring.jpa.properties.hibernate.physical_naming_strategy:com.example.Physical");
Map<String, String> hibernateProperties = properties Map<String, String> hibernateProperties = properties
.getHibernateProperties(mockStandaloneDataSource()); .getHibernateProperties("none");
// You can override them as we don't provide any default // You can override them as we don't provide any default
assertThat(hibernateProperties).contains( assertThat(hibernateProperties).contains(
entry("hibernate.implicit_naming_strategy", "com.example.Implicit"), entry("hibernate.implicit_naming_strategy", "com.example.Implicit"),
@ -106,7 +106,7 @@ public class JpaPropertiesTests {
public void useNewIdGeneratorMappingsDefault() throws Exception { public void useNewIdGeneratorMappingsDefault() throws Exception {
JpaProperties properties = load(); JpaProperties properties = load();
Map<String, String> hibernateProperties = properties Map<String, String> hibernateProperties = properties
.getHibernateProperties(mockStandaloneDataSource()); .getHibernateProperties("none");
assertThat(hibernateProperties) assertThat(hibernateProperties)
.containsEntry(AvailableSettings.USE_NEW_ID_GENERATOR_MAPPINGS, "true"); .containsEntry(AvailableSettings.USE_NEW_ID_GENERATOR_MAPPINGS, "true");
} }
@ -116,7 +116,7 @@ public class JpaPropertiesTests {
JpaProperties properties = load( JpaProperties properties = load(
"spring.jpa.hibernate.use-new-id-generator-mappings:false"); "spring.jpa.hibernate.use-new-id-generator-mappings:false");
Map<String, String> hibernateProperties = properties Map<String, String> hibernateProperties = properties
.getHibernateProperties(mockStandaloneDataSource()); .getHibernateProperties("none");
assertThat(hibernateProperties) assertThat(hibernateProperties)
.containsEntry(AvailableSettings.USE_NEW_ID_GENERATOR_MAPPINGS, "false"); .containsEntry(AvailableSettings.USE_NEW_ID_GENERATOR_MAPPINGS, "false");
} }

View File

@ -740,7 +740,7 @@ content into your application; rather pick only the properties that you need.
spring.jpa.database= # Target database to operate on, auto-detected by default. Can be alternatively set using the "databasePlatform" property. spring.jpa.database= # Target database to operate on, auto-detected by default. Can be alternatively set using the "databasePlatform" property.
spring.jpa.database-platform= # Name of the target database to operate on, auto-detected by default. Can be alternatively set using the "Database" enum. spring.jpa.database-platform= # Name of the target database to operate on, auto-detected by default. Can be alternatively set using the "Database" enum.
spring.jpa.generate-ddl=false # Initialize the schema on startup. spring.jpa.generate-ddl=false # Initialize the schema on startup.
spring.jpa.hibernate.ddl-auto= # DDL mode. This is actually a shortcut for the "hibernate.hbm2ddl.auto" property. Default to "create-drop" when using an embedded database, "none" otherwise. spring.jpa.hibernate.ddl-auto= # DDL mode. This is actually a shortcut for the "hibernate.hbm2ddl.auto" property. Default to "create-drop" when using an embedded database and no schema manager was detected, "none" otherwise.
spring.jpa.hibernate.naming.implicit-strategy= # Hibernate 5 implicit naming strategy fully qualified name. spring.jpa.hibernate.naming.implicit-strategy= # Hibernate 5 implicit naming strategy fully qualified name.
spring.jpa.hibernate.naming.physical-strategy= # Hibernate 5 physical naming strategy fully qualified name. spring.jpa.hibernate.naming.physical-strategy= # Hibernate 5 physical naming strategy fully qualified name.
spring.jpa.hibernate.use-new-id-generator-mappings= # Use Hibernate's newer IdentifierGenerator for AUTO, TABLE and SEQUENCE. spring.jpa.hibernate.use-new-id-generator-mappings= # Use Hibernate's newer IdentifierGenerator for AUTO, TABLE and SEQUENCE.

View File

@ -1671,7 +1671,10 @@ configuration properties. Some of them are automatically detected according to t
so you shouldn't have to set them. so you shouldn't have to set them.
The `spring.jpa.hibernate.ddl-auto` is a special case in that it has different defaults The `spring.jpa.hibernate.ddl-auto` is a special case in that it has different defaults
depending on whether you are using an embedded database (`create-drop`) or not (`none`). depending on runtime conditions. If an embedded database is used and no schema manager,
such as Liquibase or Flyway, is handling the `DataSource` it then defaults to
`create-drop`. In all other cases it defaults to `none`.
The dialect to use is also automatically detected based on the current `DataSource` but The dialect to use is also automatically detected based on the current `DataSource` but
you can set `spring.jpa.database` yourself if you want to be explicit and bypass that you can set `spring.jpa.database` yourself if you want to be explicit and bypass that
check on startup. check on startup.
@ -1890,12 +1893,13 @@ database. This is controlled through two external properties:
=== Initialize a database using Hibernate === Initialize a database using Hibernate
You can set `spring.jpa.hibernate.ddl-auto` explicitly and the standard Hibernate property You can set `spring.jpa.hibernate.ddl-auto` explicitly and the standard Hibernate property
values are `none`, `validate`, `update`, `create`, `create-drop`. Spring Boot chooses a values are `none`, `validate`, `update`, `create`, `create-drop`. Spring Boot chooses a
default value for you based on whether it thinks your database is embedded (default default value for you based on whether it thinks your database is embedded: default to
`create-drop`) or not (default `none`). An embedded database is detected by looking at the `create-drop` if no schema manager has been detected, `none` in all other cases. An
`Connection` type: `hsqldb`, `h2` and `derby` are embedded, the rest are not. Be careful embedded database is detected by looking at the `Connection` type: `hsqldb`, `h2` and
when switching from in-memory to a '`real`' database that you don't make assumptions about `derby` are embedded, the rest are not. Be careful when switching from in-memory to a
the existence of the tables and data in the new platform. You either have to set `ddl-auto` '`real`' database that you don't make assumptions about the existence of the tables and
explicitly, or use one of the other mechanisms to initialize the database. data in the new platform. You either have to set `ddl-auto` explicitly, or use one of the
other mechanisms to initialize the database.
NOTE: You can output the schema creation by enabling the `org.hibernate.SQL` logger. This NOTE: You can output the schema creation by enabling the `org.hibernate.SQL` logger. This
is done for you automatically if you enable the <<boot-features-logging-console-output,debug mode>>. is done for you automatically if you enable the <<boot-features-logging-console-output,debug mode>>.

View File

@ -1,5 +1,3 @@
management.security.enabled=false management.security.enabled=false
spring.jpa.hibernate.ddl-auto=none
spring.h2.console.enabled=true spring.h2.console.enabled=true

View File

@ -0,0 +1,37 @@
/*
* Copyright 2012-2017 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.jdbc;
/**
* An enumeration of the available schema management options.
*
* @author Stephane Nicoll
* @since 2.0.0
*/
public enum SchemaManagement {
/**
* The schema is managed and will be created at the appropriate time.
*/
MANAGED,
/**
* The schema is not managed.
*/
UNMANAGED
}

View File

@ -0,0 +1,36 @@
/*
* Copyright 2012-2017 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.jdbc;
import javax.sql.DataSource;
/**
* Strategy interface to determine the {@link SchemaManagement} of a {@link DataSource}.
*
* @author Stephane Nicoll
* @since 2.0.0
*/
public interface SchemaManagementProvider {
/**
* Return the {@link SchemaManagement} for the specified {@link DataSource}.
* @param dataSource the dataSource to handle
* @return the {@link SchemaManagement} for the {@link DataSource}.
*/
SchemaManagement getSchemaManagement(DataSource dataSource);
}