Commit f4df9d97 authored by Phillip Webb's avatar Phillip Webb

Use lexical ordered DataSourceInitializer patterns

Align DataSourceInitializer to Spring Framework by lexically sorting
resolved resource patterns.

Fixes gh-6316
parent d6bf9775
...@@ -16,8 +16,8 @@ ...@@ -16,8 +16,8 @@
package org.springframework.boot.autoconfigure.jdbc; package org.springframework.boot.autoconfigure.jdbc;
import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
...@@ -30,6 +30,7 @@ import org.springframework.beans.factory.annotation.Autowired; ...@@ -30,6 +30,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener; import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.jdbc.config.SortedResourcesFactoryBean;
import org.springframework.jdbc.datasource.init.DatabasePopulatorUtils; import org.springframework.jdbc.datasource.init.DatabasePopulatorUtils;
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator; import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
...@@ -129,21 +130,27 @@ class DataSourceInitializer implements ApplicationListener<DataSourceInitialized ...@@ -129,21 +130,27 @@ class DataSourceInitializer implements ApplicationListener<DataSourceInitialized
} }
private List<Resource> getResources(String locations) { private List<Resource> getResources(String locations) {
List<Resource> resources = new ArrayList<Resource>(); return getResources(
for (String location : StringUtils.commaDelimitedListToStringArray(locations)) { Arrays.asList(StringUtils.commaDelimitedListToStringArray(locations)));
try { }
for (Resource resource : this.applicationContext.getResources(location)) {
if (resource.exists()) { private List<Resource> getResources(List<String> locations) {
resources.add(resource); SortedResourcesFactoryBean factory = new SortedResourcesFactoryBean(
} this.applicationContext, locations);
try {
factory.afterPropertiesSet();
List<Resource> resources = new ArrayList<Resource>();
for (Resource resource : factory.getObject()) {
if (resource.exists()) {
resources.add(resource);
} }
} }
catch (IOException ex) { return resources;
throw new IllegalStateException( }
"Unable to load resource from " + location, ex); catch (Exception ex) {
} throw new IllegalStateException("Unable to load resources from " + locations,
ex);
} }
return resources;
} }
private void runScripts(List<Resource> resources, String username, String password) { private void runScripts(List<Resource> resources, String username, String password) {
......
...@@ -16,7 +16,10 @@ ...@@ -16,7 +16,10 @@
package org.springframework.boot.autoconfigure.jdbc; package org.springframework.boot.autoconfigure.jdbc;
import java.io.IOException;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Random; import java.util.Random;
import javax.sql.DataSource; import javax.sql.DataSource;
...@@ -34,6 +37,11 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext ...@@ -34,6 +37,11 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary; import org.springframework.context.annotation.Primary;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternUtils;
import org.springframework.jdbc.BadSqlGrammarException; import org.springframework.jdbc.BadSqlGrammarException;
import org.springframework.jdbc.core.JdbcOperations; import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.JdbcTemplate;
...@@ -181,15 +189,11 @@ public class DataSourceInitializerTests { ...@@ -181,15 +189,11 @@ public class DataSourceInitializerTests {
this.context.register(DataSourceAutoConfiguration.class, this.context.register(DataSourceAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class); PropertyPlaceholderAutoConfiguration.class);
this.context.refresh(); this.context.refresh();
DataSource dataSource = this.context.getBean(DataSource.class); DataSource dataSource = this.context.getBean(DataSource.class);
this.context.publishEvent(new DataSourceInitializedEvent(dataSource)); this.context.publishEvent(new DataSourceInitializedEvent(dataSource));
assertThat(dataSource instanceof org.apache.tomcat.jdbc.pool.DataSource).isTrue(); assertThat(dataSource instanceof org.apache.tomcat.jdbc.pool.DataSource).isTrue();
assertThat(dataSource).isNotNull(); assertThat(dataSource).isNotNull();
JdbcOperations template = new JdbcTemplate(dataSource); JdbcOperations template = new JdbcTemplate(dataSource);
try { try {
template.queryForObject("SELECT COUNT(*) from BAR", Integer.class); template.queryForObject("SELECT COUNT(*) from BAR", Integer.class);
fail("Query should have failed as BAR table does not exist"); fail("Query should have failed as BAR table does not exist");
...@@ -245,6 +249,28 @@ public class DataSourceInitializerTests { ...@@ -245,6 +249,28 @@ public class DataSourceInitializerTests {
} }
} }
@Test
public void multipleScriptsAppliedInLexicalOrder() throws Exception {
EnvironmentTestUtils.addEnvironment(this.context,
"spring.datasource.initialize:true",
"spring.datasource.schema:" + ClassUtils
.addResourcePathToPackagePath(getClass(), "lexical-schema-*.sql"),
"spring.datasource.data:" + ClassUtils
.addResourcePathToPackagePath(getClass(), "data.sql"));
this.context.register(DataSourceAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
ReverseOrderResourceLoader resourceLoader = new ReverseOrderResourceLoader(
new DefaultResourceLoader());
this.context.setResourceLoader(resourceLoader);
this.context.refresh();
DataSource dataSource = this.context.getBean(DataSource.class);
assertThat(dataSource instanceof org.apache.tomcat.jdbc.pool.DataSource).isTrue();
assertThat(dataSource).isNotNull();
JdbcOperations template = new JdbcTemplate(dataSource);
assertThat(template.queryForObject("SELECT COUNT(*) from FOO", Integer.class))
.isEqualTo(1);
}
@Configuration @Configuration
@EnableConfigurationProperties @EnableConfigurationProperties
protected static class TwoDataSources { protected static class TwoDataSources {
...@@ -264,4 +290,42 @@ public class DataSourceInitializerTests { ...@@ -264,4 +290,42 @@ public class DataSourceInitializerTests {
} }
/**
* {@link ResourcePatternResolver} used to ensure consistently wrong resource
* ordering.
*/
private static class ReverseOrderResourceLoader implements ResourcePatternResolver {
private final ResourcePatternResolver resolver;
ReverseOrderResourceLoader(ResourceLoader loader) {
this.resolver = ResourcePatternUtils.getResourcePatternResolver(loader);
}
@Override
public Resource getResource(String location) {
return this.resolver.getResource(location);
}
@Override
public ClassLoader getClassLoader() {
return this.resolver.getClassLoader();
}
@Override
public Resource[] getResources(String locationPattern) throws IOException {
Resource[] resources = this.resolver.getResources(locationPattern);
Arrays.sort(resources, new Comparator<Resource>() {
@Override
public int compare(Resource r1, Resource r2) {
return r2.getFilename().compareTo(r1.getFilename());
}
});
return resources;
}
}
} }
ALTER TABLE FOO DROP COLUMN todrop;
ALTER TABLE FOO ADD COLUMN name VARCHAR(30);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment