embedded database support initial commit; moved from spring-test; ResourceDatabasePopulator duplicate code with test.jdbc package that needs review
This commit is contained in:
parent
6fbd198420
commit
2e45a19be3
|
|
@ -15,5 +15,6 @@
|
||||||
<classpathentry kind="var" path="IVY_CACHE/com.mchange.c3p0/com.springsource.com.mchange.v2.c3p0/0.9.1.2/com.springsource.com.mchange.v2.c3p0-0.9.1.2.jar" sourcepath="/IVY_CACHE/com.mchange.c3p0/com.springsource.com.mchange.v2.c3p0/0.9.1.2/com.springsource.com.mchange.v2.c3p0-sources-0.9.1.2.jar"/>
|
<classpathentry kind="var" path="IVY_CACHE/com.mchange.c3p0/com.springsource.com.mchange.v2.c3p0/0.9.1.2/com.springsource.com.mchange.v2.c3p0-0.9.1.2.jar" sourcepath="/IVY_CACHE/com.mchange.c3p0/com.springsource.com.mchange.v2.c3p0/0.9.1.2/com.springsource.com.mchange.v2.c3p0-sources-0.9.1.2.jar"/>
|
||||||
<classpathentry kind="var" path="IVY_CACHE/org.junit/com.springsource.org.junit/4.5.0/com.springsource.org.junit-4.5.0.jar" sourcepath="/IVY_CACHE/org.junit/com.springsource.org.junit/4.5.0/com.springsource.org.junit-sources-4.5.0.jar"/>
|
<classpathentry kind="var" path="IVY_CACHE/org.junit/com.springsource.org.junit/4.5.0/com.springsource.org.junit-4.5.0.jar" sourcepath="/IVY_CACHE/org.junit/com.springsource.org.junit/4.5.0/com.springsource.org.junit-sources-4.5.0.jar"/>
|
||||||
<classpathentry kind="var" path="IVY_CACHE/org.easymock/com.springsource.org.easymock/2.3.0/com.springsource.org.easymock-2.3.0.jar" sourcepath="/IVY_CACHE/org.easymock/com.springsource.org.easymock/2.3.0/com.springsource.org.easymock-sources-2.3.0.jar"/>
|
<classpathentry kind="var" path="IVY_CACHE/org.easymock/com.springsource.org.easymock/2.3.0/com.springsource.org.easymock-2.3.0.jar" sourcepath="/IVY_CACHE/org.easymock/com.springsource.org.easymock/2.3.0/com.springsource.org.easymock-sources-2.3.0.jar"/>
|
||||||
|
<classpathentry kind="var" path="IVY_CACHE/org.hsqldb/com.springsource.org.hsqldb/1.8.0.9/com.springsource.org.hsqldb-1.8.0.9.jar" sourcepath="/IVY_CACHE/org.hsqldb/com.springsource.org.hsqldb/1.8.0.9/com.springsource.org.hsqldb-sources-1.8.0.9.jar"/>
|
||||||
<classpathentry kind="output" path="target/classes"/>
|
<classpathentry kind="output" path="target/classes"/>
|
||||||
</classpath>
|
</classpath>
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@
|
||||||
<dependency org="org.springframework" name="org.springframework.context" rev="latest.integration" conf="optional, jndi->compile"/>
|
<dependency org="org.springframework" name="org.springframework.context" rev="latest.integration" conf="optional, jndi->compile"/>
|
||||||
<dependency org="org.springframework" name="org.springframework.core" rev="latest.integration" conf="compile->compile"/>
|
<dependency org="org.springframework" name="org.springframework.core" rev="latest.integration" conf="compile->compile"/>
|
||||||
<dependency org="org.springframework" name="org.springframework.transaction" rev="latest.integration" conf="compile->compile"/>
|
<dependency org="org.springframework" name="org.springframework.transaction" rev="latest.integration" conf="compile->compile"/>
|
||||||
|
<dependency org="org.hsqldb" name="com.springsource.org.hsqldb" rev="1.8.0.9" conf="optional->compile"/>
|
||||||
<!-- test dependencies -->
|
<!-- test dependencies -->
|
||||||
<dependency org="org.junit" name="com.springsource.org.junit" rev="4.5.0" conf="test->runtime"/>
|
<dependency org="org.junit" name="com.springsource.org.junit" rev="4.5.0" conf="test->runtime"/>
|
||||||
<dependency org="org.easymock" name="com.springsource.org.easymock" rev="2.3.0" conf="test->compile"/>
|
<dependency org="org.easymock" name="com.springsource.org.easymock" rev="2.3.0" conf="test->compile"/>
|
||||||
|
|
|
||||||
|
|
@ -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