Skip to content

Commit

Permalink
Switch default to fail on error in SQL initialization
Browse files Browse the repository at this point in the history
User can switch the behaviour on and off with
spring.datasource.continueOnError:true|false. I decided
not to add an extra nested level of property resolution
because of the existing spring.datasource.schema
(and other properties relating to initialization) because
concision seemed like a good thing with those more common
settings.

Fixes spring-projectsgh-374
  • Loading branch information
Dave Syer committed Feb 24, 2014
1 parent 766da91 commit 8d9c26b
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 21 deletions.
18 changes: 10 additions & 8 deletions docs/howto.md
Original file line number Diff line number Diff line change
Expand Up @@ -901,14 +901,16 @@ enables it by default and loads SQL from the standard locations
`schema.sql` and `data.sql` (in the root of the classpath). In
addition Spring Boot will load a file `schema-${platform}.sql` where
`platform` is the vendor name of the database (`hsqldb`, `h2,
`oracle`, `mysql`, `postgresql` etc.). Spring Boot *disables* the
failfast feature of the Spring JDBC initializer, so if the scripts
cause exceptions they will be logged, but the application will still
start. This is so that they can be used as "poor man's migrations"
(inserts that fail mean that the data is already there, so no need to
fail for instance), but it does mean that you need to test the state
of your database on startup if you want to be sure that it was
successful (or else monitor the Spring JDBC DEBUG logs).
`oracle`, `mysql`, `postgresql` etc.). Spring Boot enables the
failfast feature of the Spring JDBC initializer by default, so if
the scripts cause exceptions the application will fail.

To disable the failfast you can set
`spring.datasource.continueOnError=true`. This can be useful once an
application has matured and been deployed a few times, since the
scripts can act as "poor man's migrations" - inserts that fail mean
that the data is already there, so there would be no need to prevent
the application from running, for instance.

### Higher Level Migration Tools

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,13 +106,15 @@ protected void initialize() throws Exception {
.getResources(schemaLocation)));
}

boolean continueOnError = this.environment.getProperty("continueOnError",
Boolean.class, false);
boolean exists = false;
ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
for (Resource resource : resources) {
if (resource.exists()) {
exists = true;
populator.addScript(resource);
populator.setContinueOnError(true);
populator.setContinueOnError(continueOnError);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,13 @@
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Random;
import java.util.logging.Logger;

import javax.sql.DataSource;

import org.apache.commons.dbcp.BasicDataSource;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
Expand Down Expand Up @@ -56,6 +58,13 @@ public class DataSourceAutoConfigurationTests {

private final AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();

@Before
public void init() {
EnvironmentTestUtils.addEnvironment(this.context,
"spring.datasource.initialize:false",
"spring.datasource.url:jdbc:hsqldb:mem:testdb-" + new Random().nextInt());
}

@Test
public void testDefaultDataSourceExists() throws Exception {
this.context.register(DataSourceAutoConfiguration.class,
Expand Down Expand Up @@ -85,8 +94,7 @@ public void testExplicitDriverClassClearsUserName() throws Exception {
.addEnvironment(
this.context,
"spring.datasource.driverClassName:org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfigurationTests$DatabaseDriver",
"spring.datasource.url:jdbc:foo://localhost",
"spring.datasource.initialize:false");
"spring.datasource.url:jdbc:foo://localhost");
this.context.register(DataSourceAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
this.context.refresh();
Expand Down Expand Up @@ -141,6 +149,8 @@ public void testNamedParameterJdbcTemplateExists() throws Exception {

@Test
public void testDataSourceInitialized() throws Exception {
EnvironmentTestUtils.addEnvironment(this.context,
"spring.datasource.initialize:true");
this.context.register(DataSourceAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
this.context.refresh();
Expand Down Expand Up @@ -172,16 +182,17 @@ public void testDataSourceInitializedWithExplicitScript() throws Exception {

@Test
public void testDataSourceInitializedWithMultipleScripts() throws Exception {
this.context.register(DataSourceAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
Map<String, Object> map = new HashMap<String, Object>();
map.put(DataSourceAutoConfiguration.CONFIGURATION_PREFIX + ".schema",
ClassUtils.addResourcePathToPackagePath(getClass(), "schema.sql")
EnvironmentTestUtils.addEnvironment(
this.context,
"spring.datasource.initialize:true",
"spring.datasource.schema:"
+ ClassUtils.addResourcePathToPackagePath(getClass(),
"schema.sql")
+ ","
+ ClassUtils.addResourcePathToPackagePath(getClass(),
"another.sql"));
this.context.getEnvironment().getPropertySources()
.addFirst(new MapPropertySource("test", map));
this.context.register(DataSourceAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
this.context.refresh();
DataSource dataSource = this.context.getBean(DataSource.class);
assertTrue(dataSource instanceof org.apache.tomcat.jdbc.pool.DataSource);
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
foo: bucket
foo: bucket
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.MutablePropertySources;

/**
* Test utilities for setting environment values.
Expand Down Expand Up @@ -54,15 +55,26 @@ public static void addEnvironment(ConfigurableApplicationContext context,
*/
public static void addEnvironment(ConfigurableEnvironment environment,
String... pairs) {
Map<String, Object> map = new HashMap<String, Object>();
MutablePropertySources sources = environment.getPropertySources();
Map<String, Object> map;
if (!sources.contains("test")) {
map = new HashMap<String, Object>();
MapPropertySource source = new MapPropertySource("test", map);
sources.addFirst(source);
}
else {
@SuppressWarnings("unchecked")
Map<String, Object> value = (Map<String, Object>) sources.get("test")
.getSource();
map = value;
}
for (String pair : pairs) {
int index = pair.indexOf(":");
index = index < 0 ? index = pair.indexOf("=") : index;
String key = pair.substring(0, index > 0 ? index : pair.length());
String value = index > 0 ? pair.substring(index + 1) : "";
map.put(key.trim(), value.trim());
}
environment.getPropertySources().addFirst(new MapPropertySource("test", map));
}

}

0 comments on commit 8d9c26b

Please sign in to comment.