embedded database support initial commit; moved from spring-test; ResourceDatabasePopulator duplicate code with test.jdbc package that needs review
parent
6fbd198420
commit
2e45a19be3
17 changed files with 857 additions and 0 deletions
@ -0,0 +1,34 @@ |
||||
/* |
||||
* Copyright 2002-2009 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
package org.springframework.jdbc.datasource.embedded; |
||||
|
||||
import org.springframework.dao.DataAccessException; |
||||
import org.springframework.jdbc.core.JdbcTemplate; |
||||
|
||||
/** |
||||
* Strategy for populating a database with data. |
||||
* |
||||
* @see ResourceDatabasePopulator |
||||
*/ |
||||
public interface DatabasePopulator { |
||||
|
||||
/** |
||||
* Populate the database using the JDBC-based data access template provided. |
||||
* @param template the data access template to use to populate the db; already configured and ready to use |
||||
* @throws DataAccessException if an unrecoverable data access exception occurs during database population |
||||
*/ |
||||
void populate(JdbcTemplate template); |
||||
} |
@ -0,0 +1,31 @@ |
||||
/* |
||||
* Copyright 2002-2009 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
package org.springframework.jdbc.datasource.embedded; |
||||
|
||||
import javax.sql.DataSource; |
||||
|
||||
/** |
||||
* A handle to an EmbeddedDatabase instance. |
||||
* Is a {@link DataSource}. |
||||
* Adds a shutdown operation so the embedded database instance can be shutdown. |
||||
*/ |
||||
public interface EmbeddedDatabase extends DataSource { |
||||
|
||||
/** |
||||
* Shutdown this embedded database. |
||||
*/ |
||||
public void shutdown(); |
||||
} |
@ -0,0 +1,139 @@ |
||||
/* |
||||
* Copyright 2002-2009 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
package org.springframework.jdbc.datasource.embedded; |
||||
|
||||
import org.springframework.core.io.ClassPathResource; |
||||
import org.springframework.core.io.DefaultResourceLoader; |
||||
import org.springframework.core.io.Resource; |
||||
import org.springframework.core.io.ResourceLoader; |
||||
|
||||
/** |
||||
* A builder that provides a fluent API for constructing an embedded database. |
||||
* Usage example: |
||||
* <pre> |
||||
* EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder(); |
||||
* EmbeddedDatabase db = builder.schema("schema.sql").testData("test-data.sql").build(); |
||||
* db.shutdown(); |
||||
* </pre> |
||||
* |
||||
* @author Keith Donald |
||||
*/ |
||||
public class EmbeddedDatabaseBuilder { |
||||
|
||||
private EmbeddedDatabaseFactory databaseFactory; |
||||
|
||||
private ResourceDatabasePopulator databasePopulator; |
||||
|
||||
private ResourceLoader resourceLoader; |
||||
|
||||
/** |
||||
* Creates a new embedded database builder. |
||||
*/ |
||||
public EmbeddedDatabaseBuilder() { |
||||
init(new DefaultResourceLoader()); |
||||
} |
||||
|
||||
/** |
||||
* Sets the name of the embedded database |
||||
* Defaults to 'testdb' if not called. |
||||
* @param databaseType the embedded database type |
||||
* @return this, for fluent call chaining |
||||
*/ |
||||
public EmbeddedDatabaseBuilder name(String databaseName) { |
||||
databaseFactory.setDatabaseName(databaseName); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Sets the type of embedded database. |
||||
* Defaults to HSQL if not called. |
||||
* @param databaseType the embedded database type |
||||
* @return this, for fluent call chaining |
||||
*/ |
||||
public EmbeddedDatabaseBuilder type(EmbeddedDatabaseType databaseType) { |
||||
databaseFactory.setDatabaseType(databaseType); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Sets the location of the schema SQL to run to create the database structure. |
||||
* Defaults to classpath:schema.sql if not called. |
||||
* @param sqlResource the sql resource location |
||||
* @return this, for fluent call chaining |
||||
*/ |
||||
public EmbeddedDatabaseBuilder schema(String sqlResource) { |
||||
databasePopulator.setSchemaLocation(resourceLoader.getResource(sqlResource)); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Sets the location of the schema SQL to run to create the database structure. |
||||
* Defaults to classpath:test-data.sql if not called |
||||
* @param sqlResource the sql resource location |
||||
* @return this, for fluent call chaining |
||||
*/ |
||||
public EmbeddedDatabaseBuilder testData(String sqlResource) { |
||||
databasePopulator.setTestDataLocation(resourceLoader.getResource(sqlResource)); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Build the embedded database. |
||||
* @return the embedded database |
||||
*/ |
||||
public EmbeddedDatabase build() { |
||||
return databaseFactory.getDatabase(); |
||||
} |
||||
|
||||
/** |
||||
* Factory method that creates a embedded database builder that loads resources relative to the provided class. |
||||
* @param clazz the class to load relative to |
||||
* @return the embedded database builder |
||||
*/ |
||||
public static EmbeddedDatabaseBuilder relativeTo(final Class<?> clazz) { |
||||
ResourceLoader loader = new ResourceLoader() { |
||||
public ClassLoader getClassLoader() { |
||||
return getClass().getClassLoader(); |
||||
} |
||||
|
||||
public Resource getResource(String location) { |
||||
return new ClassPathResource(location, clazz); |
||||
} |
||||
}; |
||||
return new EmbeddedDatabaseBuilder(loader); |
||||
} |
||||
|
||||
/** |
||||
* Factory method that builds a default EmbeddedDatabase instance. |
||||
* The default is HSQL with a schema created from classpath:schema.sql and test-data loaded from classpatH:test-data.sql. |
||||
* @return an embedded database |
||||
*/ |
||||
public static EmbeddedDatabase buildDefault() { |
||||
return new EmbeddedDatabaseBuilder().build(); |
||||
} |
||||
|
||||
private EmbeddedDatabaseBuilder(ResourceLoader loader) { |
||||
init(loader); |
||||
} |
||||
|
||||
private void init(ResourceLoader loader) { |
||||
databaseFactory = new EmbeddedDatabaseFactory(); |
||||
databasePopulator = new ResourceDatabasePopulator(); |
||||
databaseFactory.setDatabasePopulator(databasePopulator); |
||||
resourceLoader = loader; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,43 @@ |
||||
/* |
||||
* Copyright 2002-2009 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
package org.springframework.jdbc.datasource.embedded; |
||||
|
||||
import javax.sql.DataSource; |
||||
|
||||
import org.springframework.jdbc.datasource.SimpleDriverDataSource; |
||||
|
||||
/** |
||||
* Encapsulates the configuration required to create, connect to, and shutdown a specific type of embedded database such as HSQLdb or H2. |
||||
* Create a subclass for each database type we wish to support. |
||||
* |
||||
* @see EmbeddedDatabaseConfigurerFactory |
||||
*/ |
||||
abstract class EmbeddedDatabaseConfigurer { |
||||
|
||||
/** |
||||
* Configure the properties required to create and connect to the embedded database instance. |
||||
* @param dataSource the data source to configure |
||||
* @param databaseName the name of the test database |
||||
*/ |
||||
public abstract void configureConnectionProperties(SimpleDriverDataSource dataSource, String databaseName); |
||||
|
||||
/** |
||||
* Shutdown the embedded database instance that backs dataSource. |
||||
* @param dataSource the data source |
||||
*/ |
||||
public abstract void shutdown(DataSource dataSource); |
||||
|
||||
} |
@ -0,0 +1,32 @@ |
||||
/* |
||||
* Copyright 2002-2009 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
package org.springframework.jdbc.datasource.embedded; |
||||
|
||||
class EmbeddedDatabaseConfigurerFactory { |
||||
|
||||
public static EmbeddedDatabaseConfigurer getConfigurer(EmbeddedDatabaseType type) throws IllegalStateException { |
||||
try { |
||||
if (type == EmbeddedDatabaseType.HSQL) { |
||||
return HsqlEmbeddedDatabaseConfigurer.getInstance(); |
||||
} else { |
||||
throw new UnsupportedOperationException("Other types not yet supported"); |
||||
} |
||||
} catch (ClassNotFoundException e) { |
||||
throw new IllegalStateException("Drivers for test database type [" + type + "] are not available in the classpath", e); |
||||
} |
||||
} |
||||
|
||||
} |
@ -0,0 +1,191 @@ |
||||
/* |
||||
* Copyright 2002-2009 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
package org.springframework.jdbc.datasource.embedded; |
||||
|
||||
import java.io.PrintWriter; |
||||
import java.sql.Connection; |
||||
import java.sql.SQLException; |
||||
|
||||
import javax.sql.DataSource; |
||||
|
||||
import org.apache.commons.logging.Log; |
||||
import org.apache.commons.logging.LogFactory; |
||||
import org.springframework.jdbc.core.JdbcTemplate; |
||||
import org.springframework.jdbc.datasource.DataSourceTransactionManager; |
||||
import org.springframework.jdbc.datasource.SimpleDriverDataSource; |
||||
import org.springframework.transaction.TransactionStatus; |
||||
import org.springframework.transaction.support.TransactionCallbackWithoutResult; |
||||
import org.springframework.transaction.support.TransactionTemplate; |
||||
import org.springframework.util.Assert; |
||||
|
||||
/** |
||||
* Returns a {@link EmbeddedDatabase} instance pre-populated with test data. |
||||
* When the database is returned, callers are guaranteed that the database schema and test data will have already been loaded. |
||||
* |
||||
* Can be configured. |
||||
* Call {@link #setDatabaseName(String)} to change the name of the test database. |
||||
* Call {@link #setDatabaseType(EmbeddedDatabaseType)} to set the test database type. |
||||
* Call {@link #setDatabasePopulator(DatabasePopulator)} to change the algorithm used to populate the database. |
||||
* Call {@link #getDatabase()} to the {@link EmbeddedDatabase} instance. |
||||
*/ |
||||
public class EmbeddedDatabaseFactory { |
||||
|
||||
private static Log logger = LogFactory.getLog(EmbeddedDatabaseFactory.class); |
||||
|
||||
private String databaseName; |
||||
|
||||
private EmbeddedDatabaseConfigurer dataSourceConfigurer; |
||||
|
||||
private DatabasePopulator databasePopulator; |
||||
|
||||
private EmbeddedDatabase database; |
||||
|
||||
/** |
||||
* Creates a new EmbeddedDatabaseFactory that uses the default {@link ResourceDatabasePopulator}. |
||||
*/ |
||||
public EmbeddedDatabaseFactory() { |
||||
setDatabaseName("testdb"); |
||||
setDatabaseType(EmbeddedDatabaseType.HSQL); |
||||
setDatabasePopulator(new ResourceDatabasePopulator()); |
||||
} |
||||
|
||||
/** |
||||
* Sets the name of the test database. |
||||
* Defaults to 'testdb'. |
||||
* @param name of the test database |
||||
*/ |
||||
public void setDatabaseName(String name) { |
||||
Assert.notNull(name, "The testDatabaseName is required"); |
||||
databaseName = name; |
||||
} |
||||
|
||||
/** |
||||
* Sets the type of test database to use. |
||||
* Defaults to HSQL. |
||||
* @param type the test database type |
||||
*/ |
||||
public void setDatabaseType(EmbeddedDatabaseType type) { |
||||
Assert.notNull(type, "The TestDatabaseType is required"); |
||||
dataSourceConfigurer = EmbeddedDatabaseConfigurerFactory.getConfigurer(type); |
||||
} |
||||
|
||||
/** |
||||
* Sets the helper that will be invoked to populate the test database with data. |
||||
* Defaults a {@link ResourceDatabasePopulator}. |
||||
* @param populator |
||||
*/ |
||||
public void setDatabasePopulator(DatabasePopulator populator) { |
||||
Assert.notNull(populator, "The TestDatabasePopulator is required"); |
||||
databasePopulator = populator; |
||||
} |
||||
|
||||
// other public methods
|
||||
|
||||
/** |
||||
* Factory method that returns the embedded database instance. |
||||
*/ |
||||
public EmbeddedDatabase getDatabase() { |
||||
if (database == null) { |
||||
initDatabase(); |
||||
} |
||||
return database; |
||||
} |
||||
|
||||
// internal helper methods
|
||||
|
||||
// encapsulates the steps involved in initializing the data source: creating it, and populating it
|
||||
private void initDatabase() { |
||||
// create the in-memory database source first
|
||||
database = new EmbeddedDataSourceProxy(createDataSource()); |
||||
if (logger.isInfoEnabled()) { |
||||
logger.info("Created embedded database '" + databaseName + "'"); |
||||
} |
||||
if (databasePopulator != null) { |
||||
// now populate the database
|
||||
populateDatabase(); |
||||
} |
||||
} |
||||
|
||||
private DataSource createDataSource() { |
||||
SimpleDriverDataSource dataSource = new SimpleDriverDataSource(); |
||||
dataSourceConfigurer.configureConnectionProperties(dataSource, databaseName); |
||||
return dataSource; |
||||
} |
||||
|
||||
private void populateDatabase() { |
||||
TransactionTemplate template = new TransactionTemplate(new DataSourceTransactionManager(database)); |
||||
template.execute(new TransactionCallbackWithoutResult() { |
||||
@Override |
||||
protected void doInTransactionWithoutResult(TransactionStatus status) { |
||||
databasePopulator.populate(new JdbcTemplate(database)); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
class EmbeddedDataSourceProxy implements EmbeddedDatabase { |
||||
private DataSource dataSource; |
||||
|
||||
public EmbeddedDataSourceProxy(DataSource dataSource) { |
||||
this.dataSource = dataSource; |
||||
} |
||||
|
||||
public Connection getConnection() throws SQLException { |
||||
return dataSource.getConnection(); |
||||
} |
||||
|
||||
public Connection getConnection(String username, String password) |
||||
throws SQLException { |
||||
return dataSource.getConnection(username, password); |
||||
} |
||||
|
||||
public int getLoginTimeout() throws SQLException { |
||||
return dataSource.getLoginTimeout(); |
||||
} |
||||
|
||||
public PrintWriter getLogWriter() throws SQLException { |
||||
return dataSource.getLogWriter(); |
||||
} |
||||
|
||||
public void setLoginTimeout(int seconds) throws SQLException { |
||||
dataSource.setLoginTimeout(seconds); |
||||
} |
||||
|
||||
public void setLogWriter(PrintWriter out) throws SQLException { |
||||
dataSource.setLogWriter(out); |
||||
} |
||||
|
||||
public boolean isWrapperFor(Class<?> iface) throws SQLException { |
||||
return dataSource.isWrapperFor(iface); |
||||
} |
||||
|
||||
public <T> T unwrap(Class<T> iface) throws SQLException { |
||||
return dataSource.unwrap(iface); |
||||
} |
||||
|
||||
public void shutdown() { |
||||
shutdownDatabase(); |
||||
} |
||||
|
||||
} |
||||
|
||||
private void shutdownDatabase() { |
||||
if (database != null) { |
||||
dataSourceConfigurer.shutdown(database); |
||||
database = null; |
||||
} |
||||
} |
||||
|
||||
} |
@ -0,0 +1,24 @@ |
||||
/* |
||||
* Copyright 2002-2009 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
package org.springframework.jdbc.datasource.embedded; |
||||
|
||||
/** |
||||
* A supported embedded database type. |
||||
* @author Keith Donald |
||||
*/ |
||||
public enum EmbeddedDatabaseType { |
||||
HSQL; |
||||
} |
@ -0,0 +1,47 @@ |
||||
/* |
||||
* Copyright 2002-2009 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
package org.springframework.jdbc.datasource.embedded; |
||||
|
||||
import javax.sql.DataSource; |
||||
|
||||
import org.springframework.jdbc.core.JdbcTemplate; |
||||
import org.springframework.jdbc.datasource.SimpleDriverDataSource; |
||||
import org.springframework.util.ClassUtils; |
||||
|
||||
class HsqlEmbeddedDatabaseConfigurer extends EmbeddedDatabaseConfigurer { |
||||
|
||||
private static HsqlEmbeddedDatabaseConfigurer INSTANCE; |
||||
|
||||
public static synchronized HsqlEmbeddedDatabaseConfigurer getInstance() throws ClassNotFoundException { |
||||
if (INSTANCE == null) { |
||||
ClassUtils.forName("org.hsqldb.jdbcDriver", HsqlEmbeddedDatabaseConfigurer.class.getClassLoader()); |
||||
INSTANCE = new HsqlEmbeddedDatabaseConfigurer(); |
||||
} |
||||
return INSTANCE; |
||||
} |
||||
|
||||
public void configureConnectionProperties(SimpleDriverDataSource dataSource, String databaseName) { |
||||
dataSource.setDriverClass(org.hsqldb.jdbcDriver.class); |
||||
dataSource.setUrl("jdbc:hsqldb:mem:" + databaseName); |
||||
dataSource.setUsername("sa"); |
||||
dataSource.setPassword(""); |
||||
} |
||||
|
||||
public void shutdown(DataSource dataSource) { |
||||
new JdbcTemplate(dataSource).execute("SHUTDOWN"); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,220 @@ |
||||
/* |
||||
* Copyright 2002-2009 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
package org.springframework.jdbc.datasource.embedded; |
||||
|
||||
import java.io.IOException; |
||||
import java.io.LineNumberReader; |
||||
import java.util.LinkedList; |
||||
import java.util.List; |
||||
|
||||
import org.apache.commons.logging.Log; |
||||
import org.apache.commons.logging.LogFactory; |
||||
import org.springframework.core.io.ClassPathResource; |
||||
import org.springframework.core.io.Resource; |
||||
import org.springframework.core.io.support.EncodedResource; |
||||
import org.springframework.dao.DataAccessException; |
||||
import org.springframework.dao.DataAccessResourceFailureException; |
||||
import org.springframework.jdbc.core.JdbcTemplate; |
||||
import org.springframework.util.StringUtils; |
||||
|
||||
/** |
||||
* Populates a database from schema and test-data SQL defined in external resources. |
||||
* By default, looks for a schema.sql file and test-data.sql resource in the root of the classpath. |
||||
* |
||||
* May be configured. |
||||
* Call {@link #setSchemaLocation(Resource)} to configure the location of the database schema file. |
||||
* Call {@link #setTestDataLocation(Resource)} to configure the location of the test data file. |
||||
* Call {@link #setSqlScriptEncoding(String)} to set the encoding for the schema and test data SQL. |
||||
*/ |
||||
public class ResourceDatabasePopulator implements DatabasePopulator { |
||||
|
||||
private static final Log logger = LogFactory.getLog(ResourceDatabasePopulator.class); |
||||
|
||||
private Resource schemaLocation = new ClassPathResource("schema.sql"); |
||||
|
||||
private Resource testDataLocation = new ClassPathResource("test-data.sql"); |
||||
|
||||
private String sqlScriptEncoding; |
||||
|
||||
/** |
||||
* Sets the location of .sql file containing the database schema to create. |
||||
* @param schemaLocation the path to the db schema definition |
||||
*/ |
||||
public void setSchemaLocation(Resource schemaLocation) { |
||||
this.schemaLocation = schemaLocation; |
||||
} |
||||
|
||||
/** |
||||
* Sets the location of the .sql file containing the test data to populate. |
||||
* @param testDataLocation the path to the db test data file |
||||
*/ |
||||
public void setTestDataLocation(Resource testDataLocation) { |
||||
this.testDataLocation = testDataLocation; |
||||
} |
||||
|
||||
/** |
||||
* Specify the encoding for SQL scripts, if different from the platform encoding. |
||||
*/ |
||||
public void setSqlScriptEncoding(String sqlScriptEncoding) { |
||||
this.sqlScriptEncoding = sqlScriptEncoding; |
||||
} |
||||
|
||||
public void populate(JdbcTemplate template) { |
||||
createDatabaseSchema(template); |
||||
insertTestData(template); |
||||
} |
||||
|
||||
// create the application's database schema (tables, indexes, etc.)
|
||||
private void createDatabaseSchema(JdbcTemplate template) { |
||||
executeSqlScript(template, new EncodedResource(schemaLocation, sqlScriptEncoding), false); |
||||
} |
||||
|
||||
// populate the tables with test data
|
||||
private void insertTestData(JdbcTemplate template) { |
||||
executeSqlScript(template, new EncodedResource(testDataLocation, sqlScriptEncoding), false); |
||||
} |
||||
|
||||
// From SimpleJdbcTestUtils - TODO address duplication
|
||||
|
||||
/** |
||||
* Execute the given SQL script. |
||||
* <p>The script will normally be loaded by classpath. There should be one statement |
||||
* per line. Any semicolons will be removed. <b>Do not use this method to execute |
||||
* DDL if you expect rollback.</b> |
||||
* @param simpleJdbcTemplate the SimpleJdbcTemplate with which to perform JDBC operations |
||||
* @param resource the resource (potentially associated with a specific encoding) |
||||
* to load the SQL script from. |
||||
* @param continueOnError whether or not to continue without throwing an |
||||
* exception in the event of an error. |
||||
* @throws DataAccessException if there is an error executing a statement |
||||
* and continueOnError was <code>false</code> |
||||
*/ |
||||
static void executeSqlScript(JdbcTemplate jdbcTemplate, |
||||
EncodedResource resource, boolean continueOnError) throws DataAccessException { |
||||
if (logger.isInfoEnabled()) { |
||||
logger.info("Executing SQL script from " + resource); |
||||
} |
||||
long startTime = System.currentTimeMillis(); |
||||
List<String> statements = new LinkedList<String>(); |
||||
try { |
||||
LineNumberReader lnr = new LineNumberReader(resource.getReader()); |
||||
String script = readScript(lnr); |
||||
char delimiter = ';'; |
||||
if (!containsSqlScriptDelimiters(script, delimiter)) { |
||||
delimiter = '\n'; |
||||
} |
||||
splitSqlScript(script, delimiter, statements); |
||||
for (String statement : statements) { |
||||
try { |
||||
int rowsAffected = jdbcTemplate.update(statement); |
||||
if (logger.isDebugEnabled()) { |
||||
logger.debug(rowsAffected + " rows affected by SQL: " + statement); |
||||
} |
||||
} |
||||
catch (DataAccessException ex) { |
||||
if (continueOnError) { |
||||
if (logger.isWarnEnabled()) { |
||||
logger.warn("SQL: " + statement + " failed", ex); |
||||
} |
||||
} |
||||
else { |
||||
throw ex; |
||||
} |
||||
} |
||||
} |
||||
long elapsedTime = System.currentTimeMillis() - startTime; |
||||
if (logger.isInfoEnabled()) { |
||||
logger.info("Done executing SQL script from " + resource + " in " + elapsedTime + " ms."); |
||||
} |
||||
} |
||||
catch (IOException ex) { |
||||
throw new DataAccessResourceFailureException("Failed to open SQL script from " + resource, ex); |
||||
} |
||||
} |
||||
|
||||
// From JdbcTestUtils - TODO address duplication
|
||||
|
||||
/** |
||||
* Read a script from the LineNumberReader and build a String containing the lines. |
||||
* @param lineNumberReader the <code>LineNumberReader</> containing the script to be processed |
||||
* @return <code>String</code> containing the script lines |
||||
* @throws IOException |
||||
*/ |
||||
static String readScript(LineNumberReader lineNumberReader) throws IOException { |
||||
String currentStatement = lineNumberReader.readLine(); |
||||
StringBuilder scriptBuilder = new StringBuilder(); |
||||
while (currentStatement != null) { |
||||
if (StringUtils.hasText(currentStatement)) { |
||||
if (scriptBuilder.length() > 0) { |
||||
scriptBuilder.append('\n'); |
||||
} |
||||
scriptBuilder.append(currentStatement); |
||||
} |
||||
currentStatement = lineNumberReader.readLine(); |
||||
} |
||||
return scriptBuilder.toString(); |
||||
} |
||||
|
||||
/** |
||||
* Does the provided SQL script contain the specified delimiter? |
||||
* @param script the SQL script |
||||
* @param delim charecter delimiting each statement - typically a ';' character |
||||
*/ |
||||
static boolean containsSqlScriptDelimiters(String script, char delim) { |
||||
boolean inLiteral = false; |
||||
char[] content = script.toCharArray(); |
||||
for (int i = 0; i < script.length(); i++) { |
||||
if (content[i] == '\'') { |
||||
inLiteral = !inLiteral; |
||||
} |
||||
if (content[i] == delim && !inLiteral) { |
||||
return true; |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
/** |
||||
* Split an SQL script into separate statements delimited with the provided delimiter character. Each |
||||
* individual statement will be added to the provided <code>List</code>. |
||||
* @param script the SQL script |
||||
* @param delim charecter delimiting each statement - typically a ';' character |
||||
* @param statements the List that will contain the individual statements |
||||
*/ |
||||
static void splitSqlScript(String script, char delim, List<String> statements) { |
||||
StringBuilder sb = new StringBuilder(); |
||||
boolean inLiteral = false; |
||||
char[] content = script.toCharArray(); |
||||
for (int i = 0; i < script.length(); i++) { |
||||
if (content[i] == '\'') { |
||||
inLiteral = !inLiteral; |
||||
} |
||||
if (content[i] == delim && !inLiteral) { |
||||
if (sb.length() > 0) { |
||||
statements.add(sb.toString()); |
||||
sb = new StringBuilder(); |
||||
} |
||||
} |
||||
else { |
||||
sb.append(content[i]); |
||||
} |
||||
} |
||||
if (sb.length() > 0) { |
||||
statements.add(sb.toString()); |
||||
} |
||||
} |
||||
|
||||
} |
@ -0,0 +1,52 @@ |
||||
package org.springframework.jdbc.datasource.embedded; |
||||
|
||||
import static org.junit.Assert.assertEquals; |
||||
import static org.junit.Assert.fail; |
||||
|
||||
import org.junit.Test; |
||||
import org.springframework.dao.DataAccessException; |
||||
import org.springframework.jdbc.core.JdbcTemplate; |
||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase; |
||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; |
||||
|
||||
public class EmbeddedDatabaseBuilderTests { |
||||
|
||||
@Test |
||||
public void testBuildDefaults() { |
||||
EmbeddedDatabase db = EmbeddedDatabaseBuilder.buildDefault(); |
||||
JdbcTemplate template = new JdbcTemplate(db); |
||||
assertEquals("Keith", template.queryForObject("select NAME from T_TEST", String.class)); |
||||
db.shutdown(); |
||||
} |
||||
|
||||
@Test |
||||
public void testBuild() { |
||||
EmbeddedDatabaseBuilder builder = EmbeddedDatabaseBuilder.relativeTo(getClass()); |
||||
EmbeddedDatabase db = builder.schema("db-schema.sql").testData("db-test-data.sql").build(); |
||||
JdbcTemplate template = new JdbcTemplate(db); |
||||
assertEquals("Keith", template.queryForObject("select NAME from T_TEST", String.class)); |
||||
db.shutdown(); |
||||
} |
||||
|
||||
@Test |
||||
public void testBuildNoSuchSchema() { |
||||
try { |
||||
new EmbeddedDatabaseBuilder().schema("bogus.sql").build(); |
||||
fail("Should have failed"); |
||||
} catch (DataAccessException e) { |
||||
|
||||
} |
||||
} |
||||
|
||||
@Test |
||||
public void testBuildNoSuchTestdata() { |
||||
try { |
||||
new EmbeddedDatabaseBuilder().testData("bogus.sql").build(); |
||||
fail("Should have failed"); |
||||
} catch (DataAccessException e) { |
||||
|
||||
} |
||||
} |
||||
|
||||
|
||||
} |
@ -0,0 +1,34 @@ |
||||
package org.springframework.jdbc.datasource.embedded; |
||||
|
||||
import static org.junit.Assert.assertTrue; |
||||
|
||||
import org.junit.Test; |
||||
import org.springframework.dao.DataAccessException; |
||||
import org.springframework.jdbc.core.JdbcTemplate; |
||||
import org.springframework.jdbc.datasource.embedded.DatabasePopulator; |
||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase; |
||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseFactory; |
||||
|
||||
public class EmbeddedDatabaseFactoryTests { |
||||
|
||||
private EmbeddedDatabaseFactory factory = new EmbeddedDatabaseFactory(); |
||||
|
||||
@Test |
||||
public void testGetDataSource() { |
||||
StubDatabasePopulator populator = new StubDatabasePopulator(); |
||||
factory.setDatabasePopulator(populator); |
||||
EmbeddedDatabase db = factory.getDatabase(); |
||||
assertTrue(populator.populateCalled); |
||||
db.shutdown(); |
||||
} |
||||
|
||||
private static class StubDatabasePopulator implements DatabasePopulator { |
||||
|
||||
private boolean populateCalled; |
||||
|
||||
public void populate(JdbcTemplate template) throws DataAccessException { |
||||
this.populateCalled = true; |
||||
} |
||||
|
||||
} |
||||
} |
@ -0,0 +1,3 @@ |
||||
drop table T_TEST if exists; |
||||
|
||||
create table T_TEST (NAME varchar(50) not null); |
@ -0,0 +1 @@ |
||||
insert into T_TEST (NAME) values ('Keith'); |
@ -0,0 +1,3 @@ |
||||
drop table T_TEST if exists; |
||||
|
||||
create table T_TEST (NAME varchar(50) not null); |
@ -0,0 +1 @@ |
||||
insert into T_TEST (NAME) values ('Keith'); |
Loading…
Reference in new issue