From 0207b816d9579da4bc1f5d5d5916220978fa69f8 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Wed, 7 Mar 2018 12:00:00 +0100 Subject: [PATCH] Attempt to detect ddl-auto mode only if necessary This commit defers the resolution of the default ddl auto mode only when it is absolutely necessary. This prevents Spring Boot to attempt to get a connection when it isn't necessary Closes gh-12374 --- .../jpa/DataSourceInitializedPublisher.java | 8 ++-- .../orm/jpa/HibernateJpaConfiguration.java | 3 +- .../orm/jpa/HibernateSettings.java | 19 ++++++-- .../autoconfigure/orm/jpa/JpaProperties.java | 15 ++++--- ...tomHibernateJpaAutoConfigurationTests.java | 2 +- .../orm/jpa/JpaPropertiesTests.java | 44 +++++++++++++++---- 6 files changed, 69 insertions(+), 22 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/DataSourceInitializedPublisher.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/DataSourceInitializedPublisher.java index 7b7ceea125..2c7b6b2300 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/DataSourceInitializedPublisher.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/DataSourceInitializedPublisher.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 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. @@ -17,6 +17,7 @@ package org.springframework.boot.autoconfigure.orm.jpa; import java.util.Map; +import java.util.function.Supplier; import javax.persistence.EntityManagerFactory; import javax.sql.DataSource; @@ -90,8 +91,9 @@ class DataSourceInitializedPublisher implements BeanPostProcessor { if (this.properties == null) { return true; // better safe than sorry } - String defaultDdlAuto = (EmbeddedDatabaseConnection.isEmbedded(dataSource) - ? "create-drop" : "none"); + Supplier defaultDdlAuto = () -> + EmbeddedDatabaseConnection.isEmbedded(dataSource) ? "create-drop" + : "none"; Map hibernate = this.properties .getHibernateProperties(new HibernateSettings().ddlAuto(defaultDdlAuto)); if (hibernate.containsKey("hibernate.hbm2ddl.auto")) { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.java index fc81cb1f49..8c4bf82d3e 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.java @@ -21,6 +21,7 @@ import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.function.Supplier; import javax.sql.DataSource; @@ -116,7 +117,7 @@ class HibernateJpaConfiguration extends JpaBaseConfiguration { @Override protected Map getVendorProperties() { - String defaultDdlMode = this.defaultDdlAutoProvider + Supplier defaultDdlMode = () -> this.defaultDdlAutoProvider .getDefaultDdlAuto(getDataSource()); return new LinkedHashMap<>(getProperties() .getHibernateProperties(new HibernateSettings().ddlAuto(defaultDdlMode) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateSettings.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateSettings.java index 5c9bd721dc..052946a1fe 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateSettings.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateSettings.java @@ -18,6 +18,7 @@ package org.springframework.boot.autoconfigure.orm.jpa; import java.util.ArrayList; import java.util.Collection; +import java.util.function.Supplier; import org.hibernate.boot.model.naming.ImplicitNamingStrategy; import org.hibernate.boot.model.naming.PhysicalNamingStrategy; @@ -30,7 +31,7 @@ import org.hibernate.boot.model.naming.PhysicalNamingStrategy; */ public class HibernateSettings { - private String ddlAuto; + private Supplier ddlAuto; private ImplicitNamingStrategy implicitNamingStrategy; @@ -38,13 +39,25 @@ public class HibernateSettings { private Collection hibernatePropertiesCustomizers; - public HibernateSettings ddlAuto(String ddlAuto) { + public HibernateSettings ddlAuto(Supplier ddlAuto) { this.ddlAuto = ddlAuto; return this; } + /** + * Specify the default ddl auto value to use. + * @param ddlAuto the default ddl auto if none is provided + * @return this instance + * @see #ddlAuto(Supplier) + * @deprecated as of 2.0.1 in favour of {@link #ddlAuto(Supplier)} + */ + @Deprecated + public HibernateSettings ddlAuto(String ddlAuto) { + return ddlAuto(() -> ddlAuto); + } + public String getDdlAuto() { - return this.ddlAuto; + return (this.ddlAuto != null ? this.ddlAuto.get() : null); } public HibernateSettings implicitNamingStrategy( diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaProperties.java index 93b358dd52..a5b1b3347d 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaProperties.java @@ -22,6 +22,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import javax.inject.Provider; import javax.sql.DataSource; import org.hibernate.boot.model.naming.ImplicitNamingStrategy; @@ -217,7 +218,7 @@ public class JpaProperties { getNaming().applyNamingStrategies(result, settings.getImplicitNamingStrategy(), settings.getPhysicalNamingStrategy()); - String ddlAuto = determineDdlAuto(existing, settings.getDdlAuto()); + String ddlAuto = determineDdlAuto(existing, settings::getDdlAuto); if (StringUtils.hasText(ddlAuto) && !"none".equals(ddlAuto)) { result.put("hibernate.hbm2ddl.auto", ddlAuto); } @@ -243,11 +244,13 @@ public class JpaProperties { } private String determineDdlAuto(Map existing, - String defaultDdlAuto) { - String ddlAuto = (this.ddlAuto != null ? this.ddlAuto : defaultDdlAuto); - if (!existing.containsKey("hibernate.hbm2ddl.auto") - && !"none".equals(ddlAuto)) { - return ddlAuto; + Provider defaultDdlAuto) { + if (!existing.containsKey("hibernate.hbm2ddl.auto")) { + String ddlAuto = (this.ddlAuto != null ? this.ddlAuto + : defaultDdlAuto.get()); + if (!"none".equals(ddlAuto)) { + return ddlAuto; + } } if (existing.containsKey("hibernate.hbm2ddl.auto")) { return existing.get("hibernate.hbm2ddl.auto"); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/CustomHibernateJpaAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/CustomHibernateJpaAutoConfigurationTests.java index f2cb44a63f..4cb1fc737b 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/CustomHibernateJpaAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/CustomHibernateJpaAutoConfigurationTests.java @@ -69,7 +69,7 @@ public class CustomHibernateJpaAutoConfigurationTests { .run((context) -> { JpaProperties bean = context.getBean(JpaProperties.class); Map hibernateProperties = bean.getHibernateProperties( - new HibernateSettings().ddlAuto("create-drop")); + new HibernateSettings()); assertThat(hibernateProperties.get("hibernate.ejb.naming_strategy")) .isNull(); }); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/JpaPropertiesTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/JpaPropertiesTests.java index a862a23f56..c9d008362b 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/JpaPropertiesTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/JpaPropertiesTests.java @@ -22,6 +22,7 @@ import java.sql.SQLException; import java.util.Collections; import java.util.Map; import java.util.function.Consumer; +import java.util.function.Supplier; import javax.sql.DataSource; @@ -60,7 +61,7 @@ public class JpaPropertiesTests { public void noCustomNamingStrategy() { this.contextRunner.run(assertJpaProperties((properties) -> { Map hibernateProperties = properties - .getHibernateProperties(new HibernateSettings().ddlAuto("none")); + .getHibernateProperties(new HibernateSettings()); assertThat(hibernateProperties) .doesNotContainKeys("hibernate.ejb.naming_strategy"); assertThat(hibernateProperties).containsEntry( @@ -80,7 +81,7 @@ public class JpaPropertiesTests { .run(assertJpaProperties((properties) -> { Map hibernateProperties = properties .getHibernateProperties( - new HibernateSettings().ddlAuto("none")); + new HibernateSettings()); assertThat(hibernateProperties).contains( entry("hibernate.implicit_naming_strategy", "com.example.Implicit"), @@ -97,7 +98,7 @@ public class JpaPropertiesTests { ImplicitNamingStrategy implicitStrategy = mock(ImplicitNamingStrategy.class); PhysicalNamingStrategy physicalStrategy = mock(PhysicalNamingStrategy.class); Map hibernateProperties = properties - .getHibernateProperties(new HibernateSettings().ddlAuto("none") + .getHibernateProperties(new HibernateSettings() .implicitNamingStrategy(implicitStrategy) .physicalNamingStrategy(physicalStrategy)); assertThat(hibernateProperties).contains( @@ -120,7 +121,7 @@ public class JpaPropertiesTests { PhysicalNamingStrategy.class); Map hibernateProperties = properties .getHibernateProperties( - new HibernateSettings().ddlAuto("none") + new HibernateSettings() .implicitNamingStrategy(implicitStrategy) .physicalNamingStrategy(physicalStrategy)); assertThat(hibernateProperties).contains( @@ -154,7 +155,7 @@ public class JpaPropertiesTests { }; Map hibernateProperties = properties .getHibernateProperties( - new HibernateSettings().ddlAuto("none") + new HibernateSettings() .implicitNamingStrategy(implicitStrategy) .physicalNamingStrategy(physicalStrategy) .hibernatePropertiesCustomizers( @@ -177,7 +178,7 @@ public class JpaPropertiesTests { .run(assertJpaProperties((properties) -> { Map hibernateProperties = properties .getHibernateProperties( - new HibernateSettings().ddlAuto("none")); + new HibernateSettings()); // You can override them as we don't provide any default assertThat(hibernateProperties).contains( entry("hibernate.implicit_naming_strategy", @@ -193,7 +194,7 @@ public class JpaPropertiesTests { public void useNewIdGeneratorMappingsDefault() { this.contextRunner.run(assertJpaProperties((properties) -> { Map hibernateProperties = properties - .getHibernateProperties(new HibernateSettings().ddlAuto("none")); + .getHibernateProperties(new HibernateSettings()); assertThat(hibernateProperties).containsEntry( AvailableSettings.USE_NEW_ID_GENERATOR_MAPPINGS, "true"); })); @@ -207,7 +208,7 @@ public class JpaPropertiesTests { .run(assertJpaProperties((properties) -> { Map hibernateProperties = properties .getHibernateProperties( - new HibernateSettings().ddlAuto("none")); + new HibernateSettings()); assertThat(hibernateProperties).containsEntry( AvailableSettings.USE_NEW_ID_GENERATOR_MAPPINGS, "false"); })); @@ -248,6 +249,33 @@ public class JpaPropertiesTests { })); } + @Test + public void defaultDdlAutoIsNotInvokedIfPropertyIsSet() { + this.contextRunner + .withPropertyValues("spring.jpa.hibernate.ddl-auto=validate") + .run(assertDefaultDdlAutoNotInvoked("validate")); + } + + @Test + public void defaultDdlAutoIsNotInvokedIfHibernateSpecificPropertyIsSet() { + this.contextRunner + .withPropertyValues("spring.jpa.properties.hibernate.hbm2ddl.auto=create") + .run(assertDefaultDdlAutoNotInvoked("create")); + } + + private ContextConsumer assertDefaultDdlAutoNotInvoked( + String expectedDdlAuto) { + return assertJpaProperties((properties) -> { + Supplier ddlAutoSupplier = mock(Supplier.class); + Map hibernateProperties = properties + .getHibernateProperties(new HibernateSettings() + .ddlAuto(ddlAutoSupplier)); + assertThat(hibernateProperties).containsEntry( + "hibernate.hbm2ddl.auto", expectedDdlAuto); + verify(ddlAutoSupplier, never()).get(); + }); + } + @Test public void determineDatabaseWithUnknownUrl() { this.contextRunner.run(assertJpaProperties((properties) -> {