From 0616ecfa5caf0389c8ac30785d293ca7c35c1569 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Tue, 31 Mar 2020 12:42:42 +0200 Subject: [PATCH] Make sure Liquibase works with only spring-jdbc on the classpath This commit improves the Liquibase auto-configuration to only rely on spring-jdbc when a `DataSource` should be created on-the-fly for the purpose of its initialization. Previously, a connection pool implementation must be added as well, now we're fallbacking on `SimpleDriverDataSource` if necessary. This improves the database initialization use case with R2DBC. Closes gh-20715 --- .../liquibase/LiquibaseAutoConfiguration.java | 9 ++++++++- .../LiquibaseAutoConfigurationTests.java | 16 +++++++++++++++- 2 files changed, 23 insertions(+), 2 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 004ddc5066d..60442156538 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 @@ -50,6 +50,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.jdbc.core.JdbcOperations; 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; @@ -145,7 +146,13 @@ 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); - return DataSourceBuilder.create().url(url).username(user).password(password).build(); + return DataSourceBuilder.create().type(determineDataSourceType()).url(url).username(user).password(password) + .build(); + } + + private Class determineDataSourceType() { + Class type = DataSourceBuilder.findType(null); + return (type != null) ? type : SimpleDriverDataSource.class; } private String getProperty(Supplier property, Supplier defaultValue) { 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 6ab18e54fac..7f854d2c529 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 @@ -45,6 +45,7 @@ import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfigurat import org.springframework.boot.context.event.ApplicationStartingEvent; import org.springframework.boot.jdbc.DataSourceBuilder; import org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener; +import org.springframework.boot.test.context.FilteredClassLoader; import org.springframework.boot.test.context.assertj.AssertableApplicationContext; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.boot.test.context.runner.ContextConsumer; @@ -54,6 +55,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.datasource.SimpleDriverDataSource; import org.springframework.test.util.ReflectionTestUtils; import static org.assertj.core.api.Assertions.assertThat; @@ -80,7 +82,7 @@ class LiquibaseAutoConfigurationTests { .onApplicationEvent(new ApplicationStartingEvent(new SpringApplication(Object.class), new String[0])); } - private ApplicationContextRunner contextRunner = new ApplicationContextRunner() + private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withConfiguration(AutoConfigurations.of(LiquibaseAutoConfiguration.class)) .withPropertyValues("spring.datasource.generate-unique-name=true"); @@ -99,6 +101,18 @@ class LiquibaseAutoConfigurationTests { })); } + @Test + void createsDataSourceWhenSpringJdbcOnlyAvailableWithNoDataSourceBeanAndLiquibaseUrl() { + this.contextRunner.withPropertyValues("spring.liquibase.url:jdbc:hsqldb:mem:liquibase") + .withClassLoader( + new FilteredClassLoader("org.apache.tomcat", "com.zaxxer.hikari", "org.apache.commons.dbcp2")) + .run(assertLiquibase((liquibase) -> { + DataSource dataSource = liquibase.getDataSource(); + assertThat(dataSource).isInstanceOf(SimpleDriverDataSource.class); + assertThat(((SimpleDriverDataSource) dataSource).getUrl()).isEqualTo("jdbc:hsqldb:mem:liquibase"); + })); + } + @Test void defaultSpringLiquibase() { this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)