Skip to content

Commit a6f8351

Browse files
committed
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 spring-projectsgh-9218
1 parent 4056542 commit a6f8351

File tree

2 files changed

+48
-7
lines changed

2 files changed

+48
-7
lines changed

spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfiguration.java

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,13 @@
1616

1717
package org.springframework.boot.autoconfigure.liquibase;
1818

19+
import java.lang.reflect.Method;
20+
1921
import javax.annotation.PostConstruct;
2022
import javax.persistence.EntityManagerFactory;
2123
import javax.sql.DataSource;
2224

25+
import liquibase.exception.LiquibaseException;
2326
import liquibase.integration.spring.SpringLiquibase;
2427

2528
import org.springframework.beans.factory.ObjectProvider;
@@ -42,6 +45,7 @@
4245
import org.springframework.orm.jpa.AbstractEntityManagerFactoryBean;
4346
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
4447
import org.springframework.util.Assert;
48+
import org.springframework.util.ReflectionUtils;
4549

4650
/**
4751
* {@link EnableAutoConfiguration Auto-configuration} for Liquibase.
@@ -99,10 +103,9 @@ public void checkChangelogExists() {
99103

100104
@Bean
101105
public SpringLiquibase liquibase() {
102-
SpringLiquibase liquibase = new SpringLiquibase();
106+
SpringLiquibase liquibase = createSpringLiquibase();
103107
liquibase.setChangeLog(this.properties.getChangeLog());
104108
liquibase.setContexts(this.properties.getContexts());
105-
liquibase.setDataSource(getDataSource());
106109
liquibase.setDefaultSchema(this.properties.getDefaultSchema());
107110
liquibase.setDropFirst(this.properties.isDropFirst());
108111
liquibase.setShouldRun(this.properties.isEnabled());
@@ -112,16 +115,30 @@ public SpringLiquibase liquibase() {
112115
return liquibase;
113116
}
114117

118+
private SpringLiquibase createSpringLiquibase() {
119+
SpringLiquibase liquibase;
120+
DataSource dataSource = getDataSource();
121+
if (dataSource == null) {
122+
dataSource = DataSourceBuilder.create().url(this.properties.getUrl())
123+
.username(this.properties.getUser())
124+
.password(this.properties.getPassword()).build();
125+
liquibase = new DataSourceClosingSpringLiquibase();
126+
}
127+
else {
128+
liquibase = new SpringLiquibase();
129+
}
130+
liquibase.setDataSource(dataSource);
131+
return liquibase;
132+
}
133+
115134
private DataSource getDataSource() {
116135
if (this.liquibaseDataSource != null) {
117136
return this.liquibaseDataSource;
118137
}
119138
else if (this.properties.getUrl() == null) {
120139
return this.dataSource;
121140
}
122-
return DataSourceBuilder.create().url(this.properties.getUrl())
123-
.username(this.properties.getUser())
124-
.password(this.properties.getPassword()).build();
141+
return null;
125142
}
126143

127144
}
@@ -142,4 +159,26 @@ public LiquibaseJpaDependencyConfiguration() {
142159

143160
}
144161

162+
/**
163+
* A custom {@link SpringLiquibase} extension that close the underlying
164+
* {@link DataSource} once the database has been migrated.
165+
*/
166+
private static final class DataSourceClosingSpringLiquibase extends SpringLiquibase {
167+
168+
@Override
169+
public void afterPropertiesSet() throws LiquibaseException {
170+
super.afterPropertiesSet();
171+
closeDataSource();
172+
}
173+
174+
private void closeDataSource() {
175+
Method closeMethod = ReflectionUtils.findMethod(getDataSource().getClass(),
176+
"close");
177+
if (closeMethod != null) {
178+
ReflectionUtils.invokeMethod(closeMethod, getDataSource());
179+
}
180+
}
181+
182+
}
183+
145184
}

spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfigurationTests.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2016 the original author or authors.
2+
* Copyright 2012-2017 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -186,7 +186,9 @@ public void testOverrideDataSource() throws Exception {
186186
PropertyPlaceholderAutoConfiguration.class);
187187
this.context.refresh();
188188
SpringLiquibase liquibase = this.context.getBean(SpringLiquibase.class);
189-
assertThat(liquibase.getDataSource().getConnection().getMetaData().getURL())
189+
DataSource dataSource = liquibase.getDataSource();
190+
assertThat(ReflectionTestUtils.getField(dataSource, "pool")).isNull();
191+
assertThat(dataSource.getConnection().getMetaData().getURL())
190192
.isEqualTo("jdbc:hsqldb:mem:liquibase");
191193
}
192194

0 commit comments

Comments
 (0)