From 32f509c0c484dc83cfc1e7faf490ba89f76adbe4 Mon Sep 17 00:00:00 2001 From: Ahmed Ashour Date: Thu, 30 May 2024 20:56:57 +0200 Subject: [PATCH 1/2] Auto-configure SpringLiquibase with Liquibase Customizer See gh-40986 --- .../liquibase/LiquibaseAutoConfiguration.java | 6 +++++- .../LiquibaseAutoConfigurationTests.java | 21 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfiguration.java index 2a45b65aa54..9780f92050d 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfiguration.java @@ -18,9 +18,11 @@ package org.springframework.boot.autoconfigure.liquibase; import javax.sql.DataSource; +import liquibase.Liquibase; import liquibase.UpdateSummaryEnum; import liquibase.UpdateSummaryOutputEnum; import liquibase.change.DatabaseChange; +import liquibase.integration.spring.Customizer; import liquibase.integration.spring.SpringLiquibase; import liquibase.ui.UIServiceEnum; @@ -66,6 +68,7 @@ import org.springframework.util.StringUtils; * @author Ferenc Gratzer * @author Evgeniy Cheban * @author Moritz Halbritter + * @author Ahmed Ashour * @since 1.1.0 */ @AutoConfiguration(after = { DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class }) @@ -97,7 +100,7 @@ public class LiquibaseAutoConfiguration { @Bean public SpringLiquibase liquibase(ObjectProvider dataSource, @LiquibaseDataSource ObjectProvider liquibaseDataSource, LiquibaseProperties properties, - LiquibaseConnectionDetails connectionDetails) { + ObjectProvider> customizer, LiquibaseConnectionDetails connectionDetails) { SpringLiquibase liquibase = createSpringLiquibase(liquibaseDataSource.getIfAvailable(), dataSource.getIfUnique(), connectionDetails); liquibase.setChangeLog(properties.getChangeLog()); @@ -125,6 +128,7 @@ public class LiquibaseAutoConfiguration { if (properties.getUiService() != null) { liquibase.setUiService(UIServiceEnum.valueOf(properties.getUiService().name())); } + customizer.ifAvailable(liquibase::setCustomizer); return liquibase; } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfigurationTests.java index 83d2f7d94aa..21dd788e141 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfigurationTests.java @@ -29,9 +29,11 @@ import java.util.function.Consumer; import javax.sql.DataSource; import com.zaxxer.hikari.HikariDataSource; +import liquibase.Liquibase; import liquibase.UpdateSummaryEnum; import liquibase.UpdateSummaryOutputEnum; import liquibase.command.core.helpers.ShowSummaryArgument; +import liquibase.integration.spring.Customizer; import liquibase.integration.spring.SpringLiquibase; import liquibase.ui.UIServiceEnum; import org.junit.jupiter.api.Test; @@ -83,6 +85,7 @@ import static org.assertj.core.api.Assertions.contentOf; * @author Evgeniy Cheban * @author Moritz Halbritter * @author Phillip Webb + * @author Ahmed Ashour */ @ExtendWith(OutputCaptureExtension.class) class LiquibaseAutoConfigurationTests { @@ -532,6 +535,14 @@ class LiquibaseAutoConfigurationTests { assertThat(RuntimeHintsPredicates.resource().forResource("db/changelog/tables/init.sql")).accepts(hints); } + @Test + void whenCustomizerBeanIsDefinedThenItIsConfiguredOnSpringLiquibase() { + this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class, CustomizerConfiguration.class) + .run(assertLiquibase((liquibase) -> { + assertThat(liquibase.getCustomizer()).isNotNull(); + })); + } + private ContextConsumer assertLiquibase(Consumer consumer) { return (context) -> { assertThat(context).hasSingleBean(SpringLiquibase.class); @@ -668,6 +679,16 @@ class LiquibaseAutoConfigurationTests { } + @Configuration(proxyBeanMethods = false) + static class CustomizerConfiguration { + + @Bean + Customizer customizer() { + return liquibase -> liquibase.setChangeLogParameter("some key", "some value"); + } + + } + static class CustomH2Driver extends org.h2.Driver { } From 6aeab4461e7fb821c3954efe5efdaec0885cd3f6 Mon Sep 17 00:00:00 2001 From: Moritz Halbritter Date: Fri, 21 Jun 2024 11:54:50 +0200 Subject: [PATCH 2/2] Polish "Auto-configure SpringLiquibase with Liquibase Customizer" See gh-40986 --- .../liquibase/LiquibaseAutoConfiguration.java | 28 +++++++++++++++++-- .../LiquibaseAutoConfigurationTests.java | 6 ++-- .../how-to/pages/data-initialization.adoc | 2 ++ 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfiguration.java index 9780f92050d..e82a99b479d 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfiguration.java @@ -98,9 +98,9 @@ public class LiquibaseAutoConfiguration { } @Bean - public SpringLiquibase liquibase(ObjectProvider dataSource, + SpringLiquibase liquibase(ObjectProvider dataSource, @LiquibaseDataSource ObjectProvider liquibaseDataSource, LiquibaseProperties properties, - ObjectProvider> customizer, LiquibaseConnectionDetails connectionDetails) { + ObjectProvider customizers, LiquibaseConnectionDetails connectionDetails) { SpringLiquibase liquibase = createSpringLiquibase(liquibaseDataSource.getIfAvailable(), dataSource.getIfUnique(), connectionDetails); liquibase.setChangeLog(properties.getChangeLog()); @@ -128,7 +128,7 @@ public class LiquibaseAutoConfiguration { if (properties.getUiService() != null) { liquibase.setUiService(UIServiceEnum.valueOf(properties.getUiService().name())); } - customizer.ifAvailable(liquibase::setCustomizer); + customizers.orderedStream().forEach((customizer) -> customizer.customize(liquibase)); return liquibase; } @@ -177,6 +177,17 @@ public class LiquibaseAutoConfiguration { } + @ConditionalOnClass(Customizer.class) + static class CustomizerConfiguration { + + @Bean + @ConditionalOnBean(Customizer.class) + SpringLiquibaseCustomizer customizerSpringLiquibaseCustomizer(Customizer customizer) { + return (springLiquibase) -> springLiquibase.setCustomizer(customizer); + } + + } + static final class LiquibaseDataSourceCondition extends AnyNestedCondition { LiquibaseDataSourceCondition() { @@ -243,4 +254,15 @@ public class LiquibaseAutoConfiguration { } + @FunctionalInterface + private interface SpringLiquibaseCustomizer { + + /** + * Customize the given {@link SpringLiquibase} instance. + * @param springLiquibase the instance to configure + */ + void customize(SpringLiquibase springLiquibase); + + } + } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfigurationTests.java index 21dd788e141..cc546cc1e9a 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfigurationTests.java @@ -538,9 +538,7 @@ class LiquibaseAutoConfigurationTests { @Test void whenCustomizerBeanIsDefinedThenItIsConfiguredOnSpringLiquibase() { this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class, CustomizerConfiguration.class) - .run(assertLiquibase((liquibase) -> { - assertThat(liquibase.getCustomizer()).isNotNull(); - })); + .run(assertLiquibase((liquibase) -> assertThat(liquibase.getCustomizer()).isNotNull())); } private ContextConsumer assertLiquibase(Consumer consumer) { @@ -684,7 +682,7 @@ class LiquibaseAutoConfigurationTests { @Bean Customizer customizer() { - return liquibase -> liquibase.setChangeLogParameter("some key", "some value"); + return (liquibase) -> liquibase.setChangeLogParameter("some key", "some value"); } } 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 69d3e997808..e9f80a0ab60 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 @@ -187,6 +187,8 @@ If any of the three properties has not been set, the value of its equivalent `sp See xref:api:java/org/springframework/boot/autoconfigure/liquibase/LiquibaseProperties.html[`LiquibaseProperties`] for details about available settings such as contexts, the default schema, and others. +You can also use a `Customizer` bean if you want to customize the `Liquibase` instance before it is being used. + [[howto.data-initialization.migration-tool.flyway-tests]]