Close Liquibase-specific DataSource once database has been migrated

Previously, when the liquibase.url, .username, and .password
properties were used to configure a DataSource specifically for
Liquibase that DataSource would never be explicitly closed. As it is
created by DataSourceBuilder with no explicitly configured type it
will use whichever connection pool is available and, therefore, will
create and keep open the pool's minimum number of connections. This
is an unnecessary use of resources both in the application and in the
database.

This commit updates LiquibaseAutoConfiguration so that if it uses
DataSourceBuilder to create a DataSource then it will also close that
DataSource once the database has been migrated.

Closes gh-9218
pull/8796/merge
Andy Wilkinson 8 years ago
parent 4056542bd4
commit a6f8351dd6

@ -16,10 +16,13 @@
package org.springframework.boot.autoconfigure.liquibase; package org.springframework.boot.autoconfigure.liquibase;
import java.lang.reflect.Method;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import javax.persistence.EntityManagerFactory; import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource; import javax.sql.DataSource;
import liquibase.exception.LiquibaseException;
import liquibase.integration.spring.SpringLiquibase; import liquibase.integration.spring.SpringLiquibase;
import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.ObjectProvider;
@ -42,6 +45,7 @@ import org.springframework.core.io.ResourceLoader;
import org.springframework.orm.jpa.AbstractEntityManagerFactoryBean; import org.springframework.orm.jpa.AbstractEntityManagerFactoryBean;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
/** /**
* {@link EnableAutoConfiguration Auto-configuration} for Liquibase. * {@link EnableAutoConfiguration Auto-configuration} for Liquibase.
@ -99,10 +103,9 @@ public class LiquibaseAutoConfiguration {
@Bean @Bean
public SpringLiquibase liquibase() { public SpringLiquibase liquibase() {
SpringLiquibase liquibase = new SpringLiquibase(); SpringLiquibase liquibase = createSpringLiquibase();
liquibase.setChangeLog(this.properties.getChangeLog()); liquibase.setChangeLog(this.properties.getChangeLog());
liquibase.setContexts(this.properties.getContexts()); liquibase.setContexts(this.properties.getContexts());
liquibase.setDataSource(getDataSource());
liquibase.setDefaultSchema(this.properties.getDefaultSchema()); liquibase.setDefaultSchema(this.properties.getDefaultSchema());
liquibase.setDropFirst(this.properties.isDropFirst()); liquibase.setDropFirst(this.properties.isDropFirst());
liquibase.setShouldRun(this.properties.isEnabled()); liquibase.setShouldRun(this.properties.isEnabled());
@ -112,6 +115,22 @@ public class LiquibaseAutoConfiguration {
return liquibase; return liquibase;
} }
private SpringLiquibase createSpringLiquibase() {
SpringLiquibase liquibase;
DataSource dataSource = getDataSource();
if (dataSource == null) {
dataSource = DataSourceBuilder.create().url(this.properties.getUrl())
.username(this.properties.getUser())
.password(this.properties.getPassword()).build();
liquibase = new DataSourceClosingSpringLiquibase();
}
else {
liquibase = new SpringLiquibase();
}
liquibase.setDataSource(dataSource);
return liquibase;
}
private DataSource getDataSource() { private DataSource getDataSource() {
if (this.liquibaseDataSource != null) { if (this.liquibaseDataSource != null) {
return this.liquibaseDataSource; return this.liquibaseDataSource;
@ -119,9 +138,7 @@ public class LiquibaseAutoConfiguration {
else if (this.properties.getUrl() == null) { else if (this.properties.getUrl() == null) {
return this.dataSource; return this.dataSource;
} }
return DataSourceBuilder.create().url(this.properties.getUrl()) return null;
.username(this.properties.getUser())
.password(this.properties.getPassword()).build();
} }
} }
@ -142,4 +159,26 @@ public class LiquibaseAutoConfiguration {
} }
/**
* A custom {@link SpringLiquibase} extension that close the underlying
* {@link DataSource} once the database has been migrated.
*/
private static final class DataSourceClosingSpringLiquibase extends SpringLiquibase {
@Override
public void afterPropertiesSet() throws LiquibaseException {
super.afterPropertiesSet();
closeDataSource();
}
private void closeDataSource() {
Method closeMethod = ReflectionUtils.findMethod(getDataSource().getClass(),
"close");
if (closeMethod != null) {
ReflectionUtils.invokeMethod(closeMethod, getDataSource());
}
}
}
} }

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2016 the original author or authors. * Copyright 2012-2017 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -186,7 +186,9 @@ public class LiquibaseAutoConfigurationTests {
PropertyPlaceholderAutoConfiguration.class); PropertyPlaceholderAutoConfiguration.class);
this.context.refresh(); this.context.refresh();
SpringLiquibase liquibase = this.context.getBean(SpringLiquibase.class); SpringLiquibase liquibase = this.context.getBean(SpringLiquibase.class);
assertThat(liquibase.getDataSource().getConnection().getMetaData().getURL()) DataSource dataSource = liquibase.getDataSource();
assertThat(ReflectionTestUtils.getField(dataSource, "pool")).isNull();
assertThat(dataSource.getConnection().getMetaData().getURL())
.isEqualTo("jdbc:hsqldb:mem:liquibase"); .isEqualTo("jdbc:hsqldb:mem:liquibase");
} }

Loading…
Cancel
Save