SPR-6717: Added support for database destroy scripts
This commit is contained in:
parent
97e047ed66
commit
6933a1af28
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
Loading…
Reference in New Issue