diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfigurationTests.java index b5c385699f9..fddc389e31e 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfigurationTests.java @@ -47,6 +47,7 @@ import org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform; import org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform; import org.hibernate.internal.SessionFactoryImpl; import org.hibernate.jpa.HibernatePersistenceProvider; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.aot.hint.MemberCategory; @@ -76,6 +77,7 @@ 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.jdbc.core.JdbcTemplate; import org.springframework.orm.jpa.JpaTransactionManager; import org.springframework.orm.jpa.JpaVendorAdapter; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; @@ -512,6 +514,84 @@ class HibernateJpaAutoConfigurationTests extends AbstractJpaAutoConfigurationTes } } + @Test + @Disabled("gh-40177") + void whenSpringJpaGenerateDdlIsNotSetThenTableIsNotCreated() { + // spring.jpa.generated-ddl defaults to false but this test still fails because + // we're using an embedded database which means that HibernateProperties defaults + // hibernate.hbm2ddl.auto to create-drop, replacing the + // hibernate.hbm2ddl.auto=none that comes from generate-ddl being false. + contextRunner().run((context) -> assertThat(tablesFrom(context)).doesNotContain("CITY")); + } + + @Test + void whenSpringJpaGenerateDdlIsTrueThenTableIsCreated() { + contextRunner().withPropertyValues("spring.jpa.generate-ddl=true") + .run((context) -> assertThat(tablesFrom(context)).contains("CITY")); + } + + @Test + @Disabled("gh-40177") + void whenSpringJpaGenerateDdlIsFalseThenTableIsNotCreated() { + // This test fails because we're using an embedded database which means that + // HibernateProperties defaults hibernate.hbm2ddl.auto to create-drop, replacing + // the hibernate.hbm2ddl.auto=none that comes from setting generate-ddl to false. + contextRunner().withPropertyValues("spring.jpa.generate-ddl=false") + .run((context) -> assertThat(tablesFrom(context)).doesNotContain("CITY")); + } + + @Test + void whenHbm2DdlAutoIsNoneThenTableIsNotCreated() { + contextRunner().withPropertyValues("spring.jpa.properties.hibernate.hbm2ddl.auto=none") + .run((context) -> assertThat(tablesFrom(context)).doesNotContain("CITY")); + } + + @Test + void whenSpringJpaHibernateDdlAutoIsNoneThenTableIsNotCreated() { + contextRunner().withPropertyValues("spring.jpa.hibernate.ddl-auto=none") + .run((context) -> assertThat(tablesFrom(context)).doesNotContain("CITY")); + } + + @Test + @Disabled("gh-40177") + void whenSpringJpaGenerateDdlIsTrueAndSpringJpaHibernateDdlAutoIsNoneThenTableIsNotCreated() { + // This test fails because when ddl-auto is set to none, we remove + // hibernate.hbm2ddl.auto from Hibernate properties. This then allows + // spring.jpa.generate-ddl to set it to create-drop + contextRunner().withPropertyValues("spring.jpa.generate-ddl=true", "spring.jpa.hibernate.ddl-auto=none") + .run((context) -> assertThat(tablesFrom(context)).doesNotContain("CITY")); + } + + @Test + void whenSpringJpaGenerateDdlIsTrueAndSpringJpaHibernateDdlAutoIsDropThenTableIsNotCreated() { + contextRunner().withPropertyValues("spring.jpa.generate-ddl=true", "spring.jpa.hibernate.ddl-auto=drop") + .run((context) -> assertThat(tablesFrom(context)).doesNotContain("CITY")); + } + + @Test + void whenSpringJpaGenerateDdlIsTrueAndJakartaSchemaGenerationIsNoneThenTableIsNotCreated() { + contextRunner() + .withPropertyValues("spring.jpa.generate-ddl=true", + "spring.jpa.properties.jakarta.persistence.schema-generation.database.action=none") + .run((context) -> assertThat(tablesFrom(context)).doesNotContain("CITY")); + } + + @Test + void whenSpringJpaGenerateDdlIsTrueSpringJpaHibernateDdlAutoIsCreateAndJakartaSchemaGenerationIsNoneThenTableIsNotCreated() { + contextRunner() + .withPropertyValues("spring.jpa.generate-ddl=true", "spring.jpa.hibernate.ddl-auto=create", + "spring.jpa.properties.jakarta.persistence.schema-generation.database.action=none") + .run((context) -> assertThat(tablesFrom(context)).doesNotContain("CITY")); + } + + private List tablesFrom(AssertableApplicationContext context) { + DataSource dataSource = context.getBean(DataSource.class); + JdbcTemplate jdbc = new JdbcTemplate(dataSource); + List tables = jdbc.query("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES", + (results, row) -> results.getString(1)); + return tables; + } + @Configuration(proxyBeanMethods = false) @TestAutoConfigurationPackage(City.class) @DependsOnDatabaseInitialization diff --git a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/how-to/pages/data-initialization.adoc b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/how-to/pages/data-initialization.adoc index 93248381cf8..893d89e93dc 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/how-to/pages/data-initialization.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/how-to/pages/data-initialization.adoc @@ -7,26 +7,17 @@ It is recommended to use a single mechanism for schema generation. -[[howto.data-initialization.using-jpa]] -== Initialize a Database Using JPA - -JPA has features for DDL generation, and these can be set up to run on startup against the database. -This is controlled through two external properties: - -* `spring.jpa.generate-ddl` (boolean) switches the feature on and off and is vendor independent. -* `spring.jpa.hibernate.ddl-auto` (enum) is a Hibernate feature that controls the behavior in a more fine-grained way. - This feature is described in more detail later in this guide. - - - [[howto.data-initialization.using-hibernate]] -== Initialize a Database Using Hibernate +=== Initialize a Database Using Hibernate + +You can set configprop:spring.jpa.hibernate.ddl-auto[] to control Hibernate's database initialization. +Supported values are `none`, `validate`, `update`, `create`, and `create-drop`. +Spring Boot chooses a default value for you based on whether you are using an embedded database. +An embedded database is identified by looking at the `Connection` type and JDBC url. +`hsqldb`, `h2`, or `derby` are embedded databases and others are not. +If an embedded database is identified and no schema manager (Flyway or Liquibase) has been detected, `ddl-auto` defaults to `create-drop`. +In all other cases, it defaults to `none`. -You can set `spring.jpa.hibernate.ddl-auto` explicitly to one of the standard Hibernate property values which are `none`, `validate`, `update`, `create`, and `create-drop`. -Spring Boot chooses a default value for you based on whether it thinks your database is embedded. -It defaults to `create-drop` if no schema manager has been detected or `none` in all other cases. -An embedded database is detected by looking at the `Connection` type and JDBC url. -`hsqldb`, `h2`, and `derby` are candidates, while others are not. Be careful when switching from in-memory to a '`real`' database that you do not make assumptions about the existence of the tables and data in the new platform. You either have to set `ddl-auto` explicitly or use one of the other mechanisms to initialize the database. diff --git a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/data/sql.adoc b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/data/sql.adoc index 71bb7b1f3de..25fa713ca52 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/data/sql.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/data/sql.adoc @@ -307,7 +307,6 @@ spring: The line in the preceding example passes a value of `true` for the `hibernate.globally_quoted_identifiers` property to the Hibernate entity manager. By default, the DDL execution (or validation) is deferred until the `ApplicationContext` has started. -There is also a `spring.jpa.generate-ddl` flag, but it is not used if Hibernate auto-configuration is active, because the `ddl-auto` settings are more fine-grained.