From 8a9b31aa690ee673e4c702d37ac47a86ea78bf26 Mon Sep 17 00:00:00 2001 From: Evgeniy Cheban Date: Thu, 29 Oct 2020 06:01:34 +0300 Subject: [PATCH 1/2] Add liquibase driver class name property See gh-23958 --- .../liquibase/LiquibaseAutoConfiguration.java | 9 ++++++++- .../liquibase/LiquibaseProperties.java | 14 ++++++++++++++ .../LiquibaseAutoConfigurationTests.java | 18 ++++++++++++++++++ 3 files changed, 40 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 60442156538..5f4bab8ad57 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 @@ -44,6 +44,7 @@ import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfigurati import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.jdbc.DataSourceBuilder; +import org.springframework.boot.jdbc.DatabaseDriver; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; @@ -66,6 +67,7 @@ import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; * @author Dan Zheng * @author András Deák * @author Ferenc Gratzer + * @author Evgeniy Cheban * @since 1.1.0 */ @Configuration(proxyBeanMethods = false) @@ -147,7 +149,7 @@ public class LiquibaseAutoConfiguration { String user = getProperty(this.properties::getUser, dataSourceProperties::determineUsername); String password = getProperty(this.properties::getPassword, dataSourceProperties::determinePassword); return DataSourceBuilder.create().type(determineDataSourceType()).url(url).username(user).password(password) - .build(); + .driverClassName(determineDriverClassName(url)).build(); } private Class determineDataSourceType() { @@ -155,6 +157,11 @@ public class LiquibaseAutoConfiguration { return (type != null) ? type : SimpleDriverDataSource.class; } + private String determineDriverClassName(String url) { + String driverClassName = this.properties.getDriverClassName(); + return (driverClassName != null) ? driverClassName : DatabaseDriver.fromJdbcUrl(url).getDriverClassName(); + } + private String getProperty(Supplier property, Supplier defaultValue) { String value = property.get(); return (value != null) ? value : defaultValue.get(); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseProperties.java index dd183c9ef72..7dea8fc6866 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseProperties.java @@ -30,6 +30,7 @@ import org.springframework.util.Assert; * @author Marcel Overdijk * @author Eddú Meléndez * @author Ferenc Gratzer + * @author Evgeniy Cheban * @since 1.1.0 */ @ConfigurationProperties(prefix = "spring.liquibase", ignoreUnknownFields = false) @@ -96,6 +97,11 @@ public class LiquibaseProperties { */ private String password; + /** + * Fully qualified name of the JDBC driver. Auto-detected based on the URL by default. + */ + private String driverClassName; + /** * JDBC URL of the database to migrate. If not set, the primary configured data source * is used. @@ -226,6 +232,14 @@ public class LiquibaseProperties { this.password = password; } + public String getDriverClassName() { + return this.driverClassName; + } + + public void setDriverClassName(String driverClassName) { + this.driverClassName = driverClassName; + } + public String getUrl() { return this.url; } 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 d8190d03c9d..f251b58758d 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 @@ -73,6 +73,7 @@ import static org.assertj.core.api.Assertions.contentOf; * @author András Deák * @author Andrii Hrytsiuk * @author Ferenc Gratzer + * @author Evgeniy Cheban */ @ExtendWith(OutputCaptureExtension.class) class LiquibaseAutoConfigurationTests { @@ -222,6 +223,23 @@ class LiquibaseAutoConfigurationTests { DataSource dataSource = liquibase.getDataSource(); assertThat(((HikariDataSource) dataSource).isClosed()).isTrue(); assertThat(((HikariDataSource) dataSource).getJdbcUrl()).isEqualTo("jdbc:hsqldb:mem:liquibase"); + assertThat(((HikariDataSource) dataSource).getDriverClassName()) + .isEqualTo("org.hsqldb.jdbc.JDBCDriver"); + })); + } + + @Test + void overrideDataSourceAndDriverClassName() { + String jdbcUrl = "jdbc:hsqldb:mem:liquibase"; + String driverClassName = "org.hsqldb.jdbcDriver"; + this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class) + .withPropertyValues("spring.liquibase.url:" + jdbcUrl, + "spring.liquibase.driver-class-name:" + driverClassName) + .run(assertLiquibase((liquibase) -> { + DataSource dataSource = liquibase.getDataSource(); + assertThat(((HikariDataSource) dataSource).isClosed()).isTrue(); + assertThat(((HikariDataSource) dataSource).getJdbcUrl()).isEqualTo(jdbcUrl); + assertThat(((HikariDataSource) dataSource).getDriverClassName()).isEqualTo(driverClassName); })); } From 2db8e7eebe9ed1df5b9745a31adaf8f4e9d5430b Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 29 Oct 2020 07:26:59 +0000 Subject: [PATCH 2/2] Polish "Add liquibase driver class name property" See gh-23958 --- .../liquibase/LiquibaseAutoConfiguration.java | 19 +++++++++++++------ .../LiquibaseAutoConfigurationTests.java | 15 +++++++++++++++ .../src/docs/asciidoc/howto.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 5f4bab8ad57..fe92c5db8a0 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 @@ -54,6 +54,7 @@ import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; import org.springframework.jdbc.datasource.SimpleDriverDataSource; import org.springframework.orm.jpa.AbstractEntityManagerFactoryBean; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; +import org.springframework.util.StringUtils; /** * {@link EnableAutoConfiguration Auto-configuration} for Liquibase. @@ -148,8 +149,19 @@ public class LiquibaseAutoConfiguration { String url = getProperty(this.properties::getUrl, dataSourceProperties::determineUrl); String user = getProperty(this.properties::getUser, dataSourceProperties::determineUsername); String password = getProperty(this.properties::getPassword, dataSourceProperties::determinePassword); + String driverClassName = determineDriverClassName(dataSourceProperties, url); return DataSourceBuilder.create().type(determineDataSourceType()).url(url).username(user).password(password) - .driverClassName(determineDriverClassName(url)).build(); + .driverClassName(driverClassName).build(); + } + + private String determineDriverClassName(DataSourceProperties dataSourceProperties, String url) { + if (StringUtils.hasText(this.properties.getDriverClassName())) { + return this.properties.getDriverClassName(); + } + if (StringUtils.hasText(dataSourceProperties.getDriverClassName())) { + return dataSourceProperties.getDriverClassName(); + } + return StringUtils.hasText(url) ? DatabaseDriver.fromJdbcUrl(url).getDriverClassName() : null; } private Class determineDataSourceType() { @@ -157,11 +169,6 @@ public class LiquibaseAutoConfiguration { return (type != null) ? type : SimpleDriverDataSource.class; } - private String determineDriverClassName(String url) { - String driverClassName = this.properties.getDriverClassName(); - return (driverClassName != null) ? driverClassName : DatabaseDriver.fromJdbcUrl(url).getDriverClassName(); - } - private String getProperty(Supplier property, Supplier defaultValue) { String value = property.get(); return (value != null) ? value : defaultValue.get(); 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 f251b58758d..7d0c94cd51c 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 @@ -243,6 +243,21 @@ class LiquibaseAutoConfigurationTests { })); } + @Test + void overrideDataSourceWithFallbackDriverClassName() { + String jdbcUrl = "jdbc:hsqldb:mem:liquibase"; + String driverClassName = "org.hsqldb.jdbcDriver"; + this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class) + .withPropertyValues("spring.liquibase.url:" + jdbcUrl, + "spring.datasource.driver-class-name:" + driverClassName) + .run(assertLiquibase((liquibase) -> { + DataSource dataSource = liquibase.getDataSource(); + assertThat(((HikariDataSource) dataSource).isClosed()).isTrue(); + assertThat(((HikariDataSource) dataSource).getJdbcUrl()).isEqualTo(jdbcUrl); + assertThat(((HikariDataSource) dataSource).getDriverClassName()).isEqualTo(driverClassName); + })); + } + @Test void overrideUser() { String jdbcUrl = "jdbc:hsqldb:mem:normal"; diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/howto.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/howto.adoc index 35253fd787d..4699e34cc9e 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/howto.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/howto.adoc @@ -2217,7 +2217,7 @@ In addition to YAML, Liquibase also supports JSON, XML, and SQL change log forma By default, Liquibase autowires the (`@Primary`) `DataSource` in your context and uses that for migrations. If you need to use a different `DataSource`, you can create one and mark its `@Bean` as `@LiquibaseDataSource`. If you do so and you want two data sources, remember to create another one and mark it as `@Primary`. -Alternatively, you can use Liquibase's native `DataSource` by setting `spring.liquibase.[url,user,password]` in external properties. +Alternatively, you can use Liquibase's native `DataSource` by setting `spring.liquibase.[driver-class-name,url,user,password]` in external properties. Setting either `spring.liquibase.url` or `spring.liquibase.user` is sufficient to cause Liquibase to use its own `DataSource`. If any of the three properties has not been set, the value of its equivalent `spring.datasource` property will be used.