Use lexical ordered DataSourceInitializer patterns
Align DataSourceInitializer to Spring Framework by lexically sorting resolved resource patterns. Fixes gh-6316
This commit is contained in:
@@ -16,8 +16,8 @@
|
||||
|
||||
package org.springframework.boot.autoconfigure.jdbc;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
@@ -30,6 +30,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
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.ResourceDatabasePopulator;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -129,21 +130,27 @@ class DataSourceInitializer implements ApplicationListener<DataSourceInitialized
|
||||
}
|
||||
|
||||
private List<Resource> getResources(String locations) {
|
||||
List<Resource> resources = new ArrayList<Resource>();
|
||||
for (String location : StringUtils.commaDelimitedListToStringArray(locations)) {
|
||||
try {
|
||||
for (Resource resource : this.applicationContext.getResources(location)) {
|
||||
if (resource.exists()) {
|
||||
resources.add(resource);
|
||||
}
|
||||
return getResources(
|
||||
Arrays.asList(StringUtils.commaDelimitedListToStringArray(locations)));
|
||||
}
|
||||
|
||||
private List<Resource> getResources(List<String> locations) {
|
||||
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) {
|
||||
throw new IllegalStateException(
|
||||
"Unable to load resource from " + location, ex);
|
||||
}
|
||||
return resources;
|
||||
}
|
||||
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) {
|
||||
|
||||
@@ -16,7 +16,10 @@
|
||||
|
||||
package org.springframework.boot.autoconfigure.jdbc;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.Random;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
@@ -34,6 +37,11 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
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.core.JdbcOperations;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
@@ -181,15 +189,11 @@ public class DataSourceInitializerTests {
|
||||
this.context.register(DataSourceAutoConfiguration.class,
|
||||
PropertyPlaceholderAutoConfiguration.class);
|
||||
this.context.refresh();
|
||||
|
||||
DataSource dataSource = this.context.getBean(DataSource.class);
|
||||
|
||||
this.context.publishEvent(new DataSourceInitializedEvent(dataSource));
|
||||
|
||||
assertThat(dataSource instanceof org.apache.tomcat.jdbc.pool.DataSource).isTrue();
|
||||
assertThat(dataSource).isNotNull();
|
||||
JdbcOperations template = new JdbcTemplate(dataSource);
|
||||
|
||||
try {
|
||||
template.queryForObject("SELECT COUNT(*) from BAR", Integer.class);
|
||||
fail("Query should have failed as BAR table does not exist");
|
||||
@@ -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
|
||||
@EnableConfigurationProperties
|
||||
protected static class TwoDataSources {
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
CREATE TABLE FOO (
|
||||
id INTEGER IDENTITY PRIMARY KEY,
|
||||
todrop VARCHAR(30),
|
||||
);
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE FOO DROP COLUMN todrop;
|
||||
ALTER TABLE FOO ADD COLUMN name VARCHAR(30);
|
||||
Reference in New Issue
Block a user