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 d1ddd7e00972..ac0fc1c9f77e 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 @@ -46,6 +46,7 @@ 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; @@ -75,6 +76,7 @@ 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; @@ -508,6 +510,88 @@ void registersHintsForNamingClasses() { } } + @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/asciidoc/anchor-rewrite.properties b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/anchor-rewrite.properties index e29b38c8e5fe..1b940ae7f852 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/anchor-rewrite.properties +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/anchor-rewrite.properties @@ -1041,3 +1041,6 @@ features.testing.testcontainers.at-development-time=features.testcontainers.at-d features.testing.testcontainers.at-development-time.dynamic-properties=features.testcontainers.at-development-time.dynamic-properties features.testing.testcontainers.at-development-time.importing-container-declarations=features.testcontainers.at-development-time.importing-container-declarations features.testing.testcontainers.at-development-time.devtools=features.testcontainers.at-development-time.devtools + +# gh-40503 +howto.data-initialization.using-jpa=howto.data-initialization.using-hibernate diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/data/sql.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/data/sql.adoc index 8d59561c5f1c..a460857d2ab7 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/data/sql.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/data/sql.adoc @@ -282,7 +282,6 @@ The following line shows an example of setting JPA properties for Hibernate: 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. diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/howto/data-initialization.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/howto/data-initialization.adoc index fcb20a4e5b79..12d0c0af1364 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/howto/data-initialization.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/howto/data-initialization.adoc @@ -6,24 +6,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 -You can set `spring.jpa.hibernate.ddl-auto` explicitly and the standard Hibernate property values 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, and others are not. +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`. + 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.