SPR-6717: Added support for database destroy scripts

This commit is contained in:
David Syer 2011-06-06 07:28:25 +00:00
parent 97e047ed66
commit 6933a1af28
5 changed files with 111 additions and 22 deletions

View File

@ -59,11 +59,12 @@ public class InitializeDatabaseBeanDefinitionParser extends AbstractBeanDefiniti
private void setDatabasePopulator(Element element, ParserContext context, BeanDefinitionBuilder builder) {
List<Element> scripts = DomUtils.getChildElementsByTagName(element, "script");
if (scripts.size() > 0) {
builder.addPropertyValue("databasePopulator", createDatabasePopulator(element, scripts, context));
builder.addPropertyValue("databasePopulator", createDatabasePopulator(element, scripts, context, "INIT"));
builder.addPropertyValue("databaseCleaner", createDatabasePopulator(element, scripts, context, "DESTROY"));
}
}
private BeanDefinition createDatabasePopulator(Element element, List<Element> scripts, ParserContext context) {
private BeanDefinition createDatabasePopulator(Element element, List<Element> scripts, ParserContext context, String execution) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(CompositeDatabasePopulator.class);
boolean ignoreFailedDrops = element.getAttribute("ignore-failures").equals("DROPS");
@ -72,6 +73,14 @@ public class InitializeDatabaseBeanDefinitionParser extends AbstractBeanDefiniti
ManagedList<BeanMetadataElement> delegates = new ManagedList<BeanMetadataElement>();
for (Element scriptElement : scripts) {
String executionAttr = scriptElement.getAttribute("execution");
if (!StringUtils.hasText(executionAttr)) {
executionAttr = "INIT";
}
if (!execution.equals(executionAttr)) {
continue;
}
BeanDefinitionBuilder delegate = BeanDefinitionBuilder.genericBeanDefinition(ResourceDatabasePopulator.class);
delegate.addPropertyValue("ignoreFailedDrops", ignoreFailedDrops);

View File

@ -20,6 +20,7 @@ import java.sql.Connection;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.util.Assert;
@ -31,14 +32,15 @@ import org.springframework.util.Assert;
* @since 3.0
* @see DatabasePopulator
*/
public class DataSourceInitializer implements InitializingBean {
public class DataSourceInitializer implements InitializingBean, DisposableBean {
private DataSource dataSource;
private DatabasePopulator databasePopulator;
private boolean enabled = true;
private DatabasePopulator databaseCleaner;
private boolean enabled = true;
/**
* The {@link DataSource} to populate when this component is initialized.
@ -58,6 +60,16 @@ public class DataSourceInitializer implements InitializingBean {
this.databasePopulator = databasePopulator;
}
/**
* Set a script execution to be run in the bean destruction callback, cleaning up the database and leaving it in
* a known state for others.
*
* @param databaseCleaner the database script executor to run on destroy
*/
public void setDatabaseCleaner(DatabasePopulator databaseCleaner) {
this.databaseCleaner = databaseCleaner;
}
/**
* Flag to explicitly enable or disable the database populator.
* @param enabled true if the database populator will be called on startup
@ -66,30 +78,41 @@ public class DataSourceInitializer implements InitializingBean {
this.enabled = enabled;
}
/**
* Use the populator to set up data in the data source.
*/
public void afterPropertiesSet() throws Exception {
if (this.databasePopulator != null) {
execute(this.databasePopulator);
}
}
/**
* Use the populator to clean up data in the data source.
*/
public void destroy() throws Exception {
if (this.databaseCleaner != null) {
execute(this.databaseCleaner);
}
}
private void execute(DatabasePopulator populator) throws Exception {
if (this.enabled) {
Assert.state(this.dataSource != null, "DataSource must be provided");
Assert.state(this.databasePopulator != null, "DatabasePopulator must be provided");
Assert.state(populator != null, "DatabasePopulator must be provided");
try {
Connection connection = this.dataSource.getConnection();
try {
this.databasePopulator.populate(connection);
}
finally {
populator.populate(connection);
} finally {
try {
connection.close();
}
catch (SQLException ex) {
} catch (SQLException ex) {
// ignore
}
}
}
catch (Exception ex) {
throw new DataAccessResourceFailureException("Failed to populate database", ex);
} catch (Exception ex) {
throw new DataAccessResourceFailureException("Failed to execute database script", ex);
}
}
}

View File

@ -5,8 +5,10 @@
targetNamespace="http://www.springframework.org/schema/jdbc"
elementFormDefault="qualified" attributeFormDefault="unqualified">
<xsd:import namespace="http://www.springframework.org/schema/beans" schemaLocation="http://www.springframework.org/schema/beans/spring-beans-3.1.xsd" />
<xsd:import namespace="http://www.springframework.org/schema/tool" schemaLocation="http://www.springframework.org/schema/tool/spring-tool-3.1.xsd" />
<xsd:import namespace="http://www.springframework.org/schema/beans"
schemaLocation="http://www.springframework.org/schema/beans/spring-beans-3.1.xsd" />
<xsd:import namespace="http://www.springframework.org/schema/tool"
schemaLocation="http://www.springframework.org/schema/tool/spring-tool-3.1.xsd" />
<xsd:element name="embedded-database">
<xsd:annotation>
@ -80,8 +82,10 @@
default="true">
<xsd:annotation>
<xsd:documentation>
Is this bean "enabled", meaning the scripts will be executed?
Defaults to true, but can be used to switch on and off the initialization depending on the environment.
Is this bean "enabled", meaning the scripts will
be executed?
Defaults to true, but can be used to switch on and off
the initialization depending on the environment.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
@ -89,7 +93,8 @@
default="NONE">
<xsd:annotation>
<xsd:documentation>
Should failed SQL statements be ignored during initialization?
Should failed SQL statements be ignored during
initialization?
</xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
@ -136,6 +141,19 @@
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="execution">
<xsd:annotation>
<xsd:documentation><![CDATA[
Indicate the timing of the execution of this script. Use INIT to execute on startup (as a bean initialization) and DESTROY to execute on shutdown (as a bean destruction callback).
]]></xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="INIT" />
<xsd:enumeration value="DESTROY" />
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
</xsd:complexType>
<xsd:simpleType name="databaseType">

View File

@ -7,7 +7,9 @@ import static org.junit.Assert.assertThat;
import javax.sql.DataSource;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
@ -15,10 +17,15 @@ import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.ClassPathResource;
import org.springframework.jdbc.BadSqlGrammarException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.init.DataSourceInitializer;
public class JdbcNamespaceIntegrationTest {
@Rule
public ExpectedException expected = ExpectedException.none();
@Test
public void testCreateEmbeddedDatabase() throws Exception {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
@ -52,6 +59,22 @@ public class JdbcNamespaceIntegrationTest {
context.close();
}
@Test
public void testCreateAndDestroy() throws Exception {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
"org/springframework/jdbc/config/jdbc-destroy-config.xml");
try {
DataSource dataSource = context.getBean(DataSource.class);
JdbcTemplate template = new JdbcTemplate(dataSource);
assertEquals(1, template.queryForInt("select count(*) from T_TEST"));
context.getBean(DataSourceInitializer.class).destroy();
expected.expect(BadSqlGrammarException.class); // Table has been dropped
assertEquals(1, template.queryForInt("select count(*) from T_TEST"));
} finally {
context.close();
}
}
@Test
public void testMultipleDataSourcesHaveDifferentDatabaseNames() throws Exception {
@ -73,14 +96,14 @@ public class JdbcNamespaceIntegrationTest {
private void assertCorrectSetup(ConfigurableApplicationContext context, String... dataSources) {
assertCorrectSetup(context, 1, dataSources);
}
private void assertCorrectSetup(ConfigurableApplicationContext context, int count, String... dataSources) {
try {
for (String dataSourceName : dataSources) {
DataSource dataSource = context.getBean(dataSourceName, DataSource.class);
JdbcTemplate t = new JdbcTemplate(dataSource);
assertEquals(count, t.queryForInt("select count(*) from T_TEST"));
JdbcTemplate template = new JdbcTemplate(dataSource);
assertEquals(count, template.queryForInt("select count(*) from T_TEST"));
}
} finally {
context.close();

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.1.xsd">
<jdbc:embedded-database id="dataSource" type="HSQL"/>
<jdbc:initialize-database data-source="dataSource">
<jdbc:script location="classpath:org/springframework/jdbc/config/db-schema.sql" execution="INIT"/>
<jdbc:script location="classpath:org/springframework/jdbc/config/db-test-data.sql" execution="INIT"/>
<jdbc:script location="classpath:org/springframework/jdbc/config/db-drops.sql" execution="DESTROY"/>
</jdbc:initialize-database>
</beans>