From b51f16d0815c856d50a84bbdb8bb8b8d1a4c8a6a Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 28 Jul 2011 14:19:53 +0000 Subject: [PATCH] jdbc:script's "separator" and "execution" attributes work nested with embedded-database as well; added "encoding" attribute to jdbc:script element; general revision of DatabasePopulator configuration and execution code git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@4814 50f2f4bb-b051-0410-bef5-90022cba6387 --- .../config/DatabasePopulatorConfigUtils.java | 84 ++++++++++++++++++ .../EmbeddedDatabaseBeanDefinitionParser.java | 58 +++---------- ...nitializeDatabaseBeanDefinitionParser.java | 86 +++---------------- .../embedded/EmbeddedDatabaseFactory.java | 68 +++++---------- .../embedded/EmbeddedDatabaseFactoryBean.java | 31 +++++-- .../datasource/embedded/package-info.java | 1 - .../init/CompositeDatabasePopulator.java | 28 +++--- .../init/DataSourceInitializer.java | 46 +++------- .../init/DatabasePopulatorUtils.java | 61 +++++++++++++ .../init/ResourceDatabasePopulator.java | 18 ++-- .../jdbc/datasource/init/package-info.java | 8 ++ .../jdbc/config/spring-jdbc-3.0.xsd | 35 +++----- .../jdbc/config/spring-jdbc-3.1.xsd | 58 ++++++------- ...> InitializeDatabaseIntegrationTests.java} | 27 +++++- ...ava => JdbcNamespaceIntegrationTests.java} | 67 ++++++++++++--- .../config/jdbc-destroy-nested-config.xml | 14 +++ .../config/jdbc-initialize-cache-config.xml | 2 +- .../config/jdbc-initialize-endings-config.xml | 4 +- .../jdbc-initialize-endings-nested-config.xml | 13 +++ 19 files changed, 409 insertions(+), 300 deletions(-) create mode 100644 org.springframework.jdbc/src/main/java/org/springframework/jdbc/config/DatabasePopulatorConfigUtils.java create mode 100644 org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/init/DatabasePopulatorUtils.java create mode 100644 org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/init/package-info.java rename org.springframework.jdbc/src/test/java/org/springframework/jdbc/config/{InitializeDatabaseIntegrationTest.java => InitializeDatabaseIntegrationTests.java} (82%) rename org.springframework.jdbc/src/test/java/org/springframework/jdbc/config/{JdbcNamespaceIntegrationTest.java => JdbcNamespaceIntegrationTests.java} (68%) create mode 100644 org.springframework.jdbc/src/test/resources/org/springframework/jdbc/config/jdbc-destroy-nested-config.xml create mode 100644 org.springframework.jdbc/src/test/resources/org/springframework/jdbc/config/jdbc-initialize-endings-nested-config.xml diff --git a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/config/DatabasePopulatorConfigUtils.java b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/config/DatabasePopulatorConfigUtils.java new file mode 100644 index 00000000000..68017a07fc2 --- /dev/null +++ b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/config/DatabasePopulatorConfigUtils.java @@ -0,0 +1,84 @@ +/* + * Copyright 2002-2011 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.config; + +import java.util.Arrays; +import java.util.List; + +import org.w3c.dom.Element; + +import org.springframework.beans.BeanMetadataElement; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.ManagedList; +import org.springframework.jdbc.datasource.init.CompositeDatabasePopulator; +import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator; +import org.springframework.util.StringUtils; +import org.springframework.util.xml.DomUtils; + +/** + * @author Juergen Hoeller + * @since 3.1 + */ +class DatabasePopulatorConfigUtils { + + public static void setDatabasePopulator(Element element, BeanDefinitionBuilder builder) { + List scripts = DomUtils.getChildElementsByTagName(element, "script"); + if (scripts.size() > 0) { + builder.addPropertyValue("databasePopulator", createDatabasePopulator(element, scripts, "INIT")); + builder.addPropertyValue("databaseCleaner", createDatabasePopulator(element, scripts, "DESTROY")); + } + } + + static private BeanDefinition createDatabasePopulator(Element element, List scripts, String execution) { + BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(CompositeDatabasePopulator.class); + + boolean ignoreFailedDrops = element.getAttribute("ignore-failures").equals("DROPS"); + boolean continueOnError = element.getAttribute("ignore-failures").equals("ALL"); + + ManagedList delegates = new ManagedList(); + 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); + delegate.addPropertyValue("continueOnError", continueOnError); + + List locations = Arrays.asList(scriptElement.getAttribute("location")); + // Use a factory bean for the resources so they can be given an order if a pattern is used + BeanDefinitionBuilder resourcesFactory = BeanDefinitionBuilder.genericBeanDefinition(SortedResourcesFactoryBean.class); + resourcesFactory.addConstructorArgValue(locations); + delegate.addPropertyValue("scripts", resourcesFactory.getBeanDefinition()); + if (StringUtils.hasLength(scriptElement.getAttribute("encoding"))) { + delegate.addPropertyValue("sqlScriptEncoding", scriptElement.getAttribute("encoding")); + } + if (StringUtils.hasLength(scriptElement.getAttribute("separator"))) { + delegate.addPropertyValue("separator", scriptElement.getAttribute("separator")); + } + delegates.add(delegate.getBeanDefinition()); + } + builder.addPropertyValue("populators", delegates); + + return builder.getBeanDefinition(); + } + +} diff --git a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/config/EmbeddedDatabaseBeanDefinitionParser.java b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/config/EmbeddedDatabaseBeanDefinitionParser.java index 9e6e9d3f5e8..23ad7c54ea4 100644 --- a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/config/EmbeddedDatabaseBeanDefinitionParser.java +++ b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/config/EmbeddedDatabaseBeanDefinitionParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2011 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. @@ -16,8 +16,7 @@ package org.springframework.jdbc.config; -import java.util.ArrayList; -import java.util.List; +import org.w3c.dom.Element; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.AbstractBeanDefinition; @@ -27,35 +26,32 @@ import org.springframework.beans.factory.xml.ParserContext; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseFactoryBean; import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator; import org.springframework.util.StringUtils; -import org.springframework.util.xml.DomUtils; -import org.w3c.dom.Element; /** - * {@link org.springframework.beans.factory.xml.BeanDefinitionParser} that parses {@code embedded-database} element and - * creates a {@link BeanDefinition} for {@link EmbeddedDatabaseFactoryBean}. Picks up nested {@code script} elements and - * configures a {@link ResourceDatabasePopulator} for them. - * + * {@link org.springframework.beans.factory.xml.BeanDefinitionParser} that parses an {@code embedded-database} + * element and creates a {@link BeanDefinition} for {@link EmbeddedDatabaseFactoryBean}. Picks up nested + * {@code script} elements and configures a {@link ResourceDatabasePopulator} for them. + * * @author Oliver Gierke + * @author Juergen Hoeller * @since 3.0 */ class EmbeddedDatabaseBeanDefinitionParser extends AbstractBeanDefinitionParser { - private static final String NAME_PROPERTY = "databaseName"; - @Override - protected AbstractBeanDefinition parseInternal(Element element, ParserContext context) { + protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) { BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(EmbeddedDatabaseFactoryBean.class); setDatabaseType(element, builder); - setDatabasePopulator(element, context, builder); + DatabasePopulatorConfigUtils.setDatabasePopulator(element, builder); useIdAsDatabaseNameIfGiven(element, builder); - return getSourcedBeanDefinition(builder, element, context); + builder.getRawBeanDefinition().setSource(parserContext.extractSource(element)); + return builder.getBeanDefinition(); } private void useIdAsDatabaseNameIfGiven(Element element, BeanDefinitionBuilder builder) { - String id = element.getAttribute(ID_ATTRIBUTE); if (StringUtils.hasText(id)) { - builder.addPropertyValue(NAME_PROPERTY, id); + builder.addPropertyValue("databaseName", id); } } @@ -66,34 +62,4 @@ class EmbeddedDatabaseBeanDefinitionParser extends AbstractBeanDefinitionParser } } - private void setDatabasePopulator(Element element, ParserContext context, BeanDefinitionBuilder builder) { - List scripts = DomUtils.getChildElementsByTagName(element, "script"); - if (scripts.size() > 0) { - builder.addPropertyValue("databasePopulator", createDatabasePopulator(scripts, context)); - } - } - - private BeanDefinition createDatabasePopulator(List scripts, ParserContext context) { - BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(ResourceDatabasePopulator.class); - List locations = new ArrayList(); - for (Element scriptElement : scripts) { - locations.add(scriptElement.getAttribute("location")); - } - // Use a factory bean for the resources so they can be given an order if a pattern is used - BeanDefinitionBuilder resourcesFactory = BeanDefinitionBuilder - .genericBeanDefinition(SortedResourcesFactoryBean.class); - resourcesFactory.addConstructorArgValue(locations); - builder.addPropertyValue("scripts", resourcesFactory.getBeanDefinition()); - - return builder.getBeanDefinition(); - } - - private AbstractBeanDefinition getSourcedBeanDefinition( - BeanDefinitionBuilder builder, Element source, ParserContext context) { - - AbstractBeanDefinition definition = builder.getBeanDefinition(); - definition.setSource(context.extractSource(source)); - return definition; - } - } diff --git a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/config/InitializeDatabaseBeanDefinitionParser.java b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/config/InitializeDatabaseBeanDefinitionParser.java index 2956aa40e50..5ea642fa6c7 100644 --- a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/config/InitializeDatabaseBeanDefinitionParser.java +++ b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/config/InitializeDatabaseBeanDefinitionParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2011 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. @@ -16,39 +16,35 @@ package org.springframework.jdbc.config; -import java.util.Arrays; -import java.util.List; +import org.w3c.dom.Element; -import org.springframework.beans.BeanMetadataElement; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; -import org.springframework.beans.factory.support.ManagedList; import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; -import org.springframework.jdbc.datasource.init.CompositeDatabasePopulator; import org.springframework.jdbc.datasource.init.DataSourceInitializer; import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator; -import org.springframework.util.StringUtils; -import org.springframework.util.xml.DomUtils; -import org.w3c.dom.Element; /** - * {@link org.springframework.beans.factory.xml.BeanDefinitionParser} that parses an {@code initialize-database} element and - * creates a {@link BeanDefinition} of type {@link DataSourceInitializer}. Picks up nested {@code script} elements and - * configures a {@link ResourceDatabasePopulator} for them. + * {@link org.springframework.beans.factory.xml.BeanDefinitionParser} that parses an {@code initialize-database} + * element and creates a {@link BeanDefinition} of type {@link DataSourceInitializer}. Picks up nested + * {@code script} elements and configures a {@link ResourceDatabasePopulator} for them. + * * @author Dave Syer + * @author Juergen Hoeller * @since 3.0 */ -public class InitializeDatabaseBeanDefinitionParser extends AbstractBeanDefinitionParser { +class InitializeDatabaseBeanDefinitionParser extends AbstractBeanDefinitionParser { @Override - protected AbstractBeanDefinition parseInternal(Element element, ParserContext context) { + protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) { BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(DataSourceInitializer.class); builder.addPropertyReference("dataSource", element.getAttribute("data-source")); builder.addPropertyValue("enabled", element.getAttribute("enabled")); - setDatabasePopulator(element, context, builder); - return getSourcedBeanDefinition(builder, element, context); + DatabasePopulatorConfigUtils.setDatabasePopulator(element, builder); + builder.getRawBeanDefinition().setSource(parserContext.extractSource(element)); + return builder.getBeanDefinition(); } @Override @@ -56,62 +52,4 @@ public class InitializeDatabaseBeanDefinitionParser extends AbstractBeanDefiniti return true; } - private void setDatabasePopulator(Element element, ParserContext context, BeanDefinitionBuilder builder) { - List scripts = DomUtils.getChildElementsByTagName(element, "script"); - if (scripts.size() > 0) { - builder.addPropertyValue("databasePopulator", createDatabasePopulator(element, scripts, context, "INIT")); - builder.addPropertyValue("databaseCleaner", createDatabasePopulator(element, scripts, context, "DESTROY")); - } - } - - private BeanDefinition createDatabasePopulator(Element element, List scripts, ParserContext context, String execution) { - BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(CompositeDatabasePopulator.class); - - boolean ignoreFailedDrops = element.getAttribute("ignore-failures").equals("DROPS"); - boolean continueOnError = element.getAttribute("ignore-failures").equals("ALL"); - - ManagedList delegates = new ManagedList(); - - 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); - delegate.addPropertyValue("continueOnError", continueOnError); - - List locations = Arrays.asList(scriptElement.getAttribute("location")); - // Use a factory bean for the resources so they can be given an order if a pattern is used - BeanDefinitionBuilder resourcesFactory = BeanDefinitionBuilder - .genericBeanDefinition(SortedResourcesFactoryBean.class); - resourcesFactory.addConstructorArgValue(locations); - - delegate.addPropertyValue("scripts", resourcesFactory.getBeanDefinition()); - - if (StringUtils.hasLength(scriptElement.getAttribute("separator"))) { - delegate.addPropertyValue("separator", scriptElement.getAttribute("separator")); - } - - delegates.add(delegate.getBeanDefinition()); - - } - - builder.addPropertyValue("populators", delegates); - - return builder.getBeanDefinition(); - } - - private AbstractBeanDefinition getSourcedBeanDefinition(BeanDefinitionBuilder builder, Element source, - ParserContext context) { - AbstractBeanDefinition definition = builder.getBeanDefinition(); - definition.setSource(context.extractSource(source)); - return definition; - } - } diff --git a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/EmbeddedDatabaseFactory.java b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/EmbeddedDatabaseFactory.java index 2dd44185280..f740e88c1b0 100644 --- a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/EmbeddedDatabaseFactory.java +++ b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/EmbeddedDatabaseFactory.java @@ -25,8 +25,8 @@ import javax.sql.DataSource; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.springframework.dao.DataAccessResourceFailureException; import org.springframework.jdbc.datasource.init.DatabasePopulator; +import org.springframework.jdbc.datasource.init.DatabasePopulatorUtils; import org.springframework.util.Assert; /** @@ -69,6 +69,15 @@ public class EmbeddedDatabaseFactory { this.databaseName = databaseName; } + /** + * Set the factory to use to create the DataSource instance that connects to the embedded database. + *

Defaults to {@link SimpleDriverDataSourceFactory}. + */ + public void setDataSourceFactory(DataSourceFactory dataSourceFactory) { + Assert.notNull(dataSourceFactory, "DataSourceFactory is required"); + this.dataSourceFactory = dataSourceFactory; + } + /** * Set the type of embedded database to use. Call this when you wish to configure * one of the pre-supported types. Defaults to HSQL. @@ -80,33 +89,20 @@ public class EmbeddedDatabaseFactory { /** * Set the strategy that will be used to configure the embedded database instance. - * Call this when you wish to use an embedded database type not already supported. - * @param configurer the embedded database configurer + *

Call this when you wish to use an embedded database type not already supported. */ public void setDatabaseConfigurer(EmbeddedDatabaseConfigurer configurer) { - Assert.notNull(configurer, "EmbeddedDatabaseConfigurer is required"); this.databaseConfigurer = configurer; } /** * Set the strategy that will be used to populate the embedded database. Defaults to null. - * @param populator the database populator + * @see org.springframework.jdbc.datasource.init.DataSourceInitializer#setDatabasePopulator */ public void setDatabasePopulator(DatabasePopulator populator) { - Assert.notNull(populator, "DatabasePopulator is required"); this.databasePopulator = populator; } - /** - * Set the factory to use to create the DataSource instance that connects to the embedded database. - * Defaults to {@link SimpleDriverDataSourceFactory}. - * @param dataSourceFactory the data source factory - */ - public void setDataSourceFactory(DataSourceFactory dataSourceFactory) { - Assert.notNull(dataSourceFactory, "DataSourceFactory is required"); - this.dataSourceFactory = dataSourceFactory; - } - /** * Factory method that returns the embedded database instance. */ @@ -137,7 +133,7 @@ public class EmbeddedDatabaseFactory { // Now populate the database if (this.databasePopulator != null) { try { - populateDatabase(); + DatabasePopulatorUtils.execute(this.databasePopulator, this.dataSource); } catch (RuntimeException ex) { // failed to populate, so leave it as not initialized @@ -147,35 +143,6 @@ public class EmbeddedDatabaseFactory { } } - private void populateDatabase() { - try { - Connection connection = this.dataSource.getConnection(); - try { - this.databasePopulator.populate(connection); - } - finally { - try { - connection.close(); - } - catch (SQLException ex) { - // ignore - } - } - } - catch (Exception ex) { - throw new DataAccessResourceFailureException("Failed to populate database", ex); - } - } - - /** - * Hook that gets the DataSource that provides the connectivity to the embedded database. - *

Returns null if the DataSource has not been initialized or the database has been shut down. - * Subclasses may call to access the datasource instance directly. - */ - protected DataSource getDataSource() { - return this.dataSource; - } - /** * Hook to shutdown the embedded database. Subclasses may call to force shutdown. * After calling, {@link #getDataSource()} returns null. Does nothing if no embedded database has been initialized. @@ -187,6 +154,15 @@ public class EmbeddedDatabaseFactory { } } + /** + * Hook that gets the DataSource that provides the connectivity to the embedded database. + *

Returns null if the DataSource has not been initialized or the database + * has been shut down. Subclasses may call to access the DataSource instance directly. + */ + protected final DataSource getDataSource() { + return this.dataSource; + } + private class EmbeddedDataSourceProxy implements EmbeddedDatabase { diff --git a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/EmbeddedDatabaseFactoryBean.java b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/EmbeddedDatabaseFactoryBean.java index 8fd55af0f98..a58d84a75dd 100644 --- a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/EmbeddedDatabaseFactoryBean.java +++ b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/EmbeddedDatabaseFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2011 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. @@ -21,6 +21,8 @@ import javax.sql.DataSource; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.InitializingBean; +import org.springframework.jdbc.datasource.init.DatabasePopulator; +import org.springframework.jdbc.datasource.init.DatabasePopulatorUtils; /** * A subclass of {@link EmbeddedDatabaseFactory} that implements {@link FactoryBean} for registration as a Spring bean. @@ -32,15 +34,38 @@ import org.springframework.beans.factory.InitializingBean; *

Implements DisposableBean to shutdown the embedded database when the managing Spring container is shutdown. * * @author Keith Donald + * @author Juergen Hoeller * @since 3.0 */ public class EmbeddedDatabaseFactoryBean extends EmbeddedDatabaseFactory implements FactoryBean, InitializingBean, DisposableBean { + private DatabasePopulator databaseCleaner; + + + /** + * 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 + * @see #setDatabasePopulator + * @see org.springframework.jdbc.datasource.init.DataSourceInitializer#setDatabaseCleaner + */ + public void setDatabaseCleaner(DatabasePopulator databaseCleaner) { + this.databaseCleaner = databaseCleaner; + } + public void afterPropertiesSet() { initDatabase(); } + public void destroy() { + if (this.databaseCleaner != null) { + DatabasePopulatorUtils.execute(this.databaseCleaner, getDataSource()); + } + shutdownDatabase(); + } + + public DataSource getObject() { return getDataSource(); } @@ -53,8 +78,4 @@ public class EmbeddedDatabaseFactoryBean extends EmbeddedDatabaseFactory return true; } - public void destroy() { - shutdownDatabase(); - } - } diff --git a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/package-info.java b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/package-info.java index 734176c994b..4349f9d217f 100644 --- a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/package-info.java +++ b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/package-info.java @@ -2,7 +2,6 @@ /** * * Provides extensible support for creating embedded database instances. - * HSQL in-memory support provided natively * */ package org.springframework.jdbc.datasource.embedded; diff --git a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/init/CompositeDatabasePopulator.java b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/init/CompositeDatabasePopulator.java index 574a1ef2682..143317b694a 100644 --- a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/init/CompositeDatabasePopulator.java +++ b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/init/CompositeDatabasePopulator.java @@ -18,38 +18,40 @@ package org.springframework.jdbc.datasource.init; import java.sql.Connection; import java.sql.SQLException; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; /** - * {@link DatabasePopulator} implementation that delegates to a list of other implementations, executing alll scripts. - * - * @author Dave Syer + * {@link DatabasePopulator} implementation that delegates to a list of other + * DatabasePopulator implementations, executing all scripts. * + * @author Dave Syer + * @author Juergen Hoeller + * @since 3.1 */ public class CompositeDatabasePopulator implements DatabasePopulator { private List populators = new ArrayList(); + /** - * @param populators the populators to set + * Specify a list of populators to delegate to. */ - public void setPopulators(List populators) { + public void setPopulators(DatabasePopulator... populators) { this.populators.clear(); - this.populators.addAll(populators); + this.populators.addAll(Arrays.asList(populators)); } /** - * @param populator the populator to add + * Add a populator to the list of delegates. */ - public void addPopulator(DatabasePopulator populator) { - this.populators.add(populator); + public void addPopulators(DatabasePopulator... populators) { + this.populators.addAll(Arrays.asList(populators)); } - /* (non-Javadoc) - * @see org.springframework.jdbc.datasource.init.DatabasePopulator#populate(java.sql.Connection) - */ + public void populate(Connection connection) throws SQLException { - for (DatabasePopulator populator : populators) { + for (DatabasePopulator populator : this.populators) { populator.populate(connection); } } diff --git a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/init/DataSourceInitializer.java b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/init/DataSourceInitializer.java index d7dfd350fd8..96f795ca76d 100644 --- a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/init/DataSourceInitializer.java +++ b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/init/DataSourceInitializer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2011 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. @@ -16,14 +16,10 @@ package org.springframework.jdbc.datasource.init; -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; /** * Used to populate a database during initialization. @@ -42,6 +38,7 @@ public class DataSourceInitializer implements InitializingBean, DisposableBean { private boolean enabled = true; + /** * The {@link DataSource} to populate when this component is initialized. * Mandatory with no default. @@ -61,9 +58,8 @@ public class DataSourceInitializer implements InitializingBean, DisposableBean { } /** - * 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. - * + * 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) { @@ -78,42 +74,22 @@ public class DataSourceInitializer implements InitializingBean, DisposableBean { 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); + public void afterPropertiesSet() { + if (this.databasePopulator != null && this.enabled) { + DatabasePopulatorUtils.execute(this.databasePopulator, this.dataSource); } } /** * 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(populator != null, "DatabasePopulator must be provided"); - try { - Connection connection = this.dataSource.getConnection(); - try { - populator.populate(connection); - } finally { - try { - connection.close(); - } catch (SQLException ex) { - // ignore - } - } - } catch (Exception ex) { - throw new DataAccessResourceFailureException("Failed to execute database script", ex); - } + public void destroy() { + if (this.databaseCleaner != null && this.enabled) { + DatabasePopulatorUtils.execute(this.databaseCleaner, this.dataSource); } } diff --git a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/init/DatabasePopulatorUtils.java b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/init/DatabasePopulatorUtils.java new file mode 100644 index 00000000000..f90d94b4189 --- /dev/null +++ b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/init/DatabasePopulatorUtils.java @@ -0,0 +1,61 @@ +/* + * Copyright 2002-2011 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.init; + +import java.sql.Connection; +import java.sql.SQLException; +import javax.sql.DataSource; + +import org.springframework.dao.DataAccessResourceFailureException; +import org.springframework.util.Assert; + +/** + * Utility methods for executing a DatabasePopulator. + * + * @author Juergen Hoeller + * @since 3.1 + */ +public abstract class DatabasePopulatorUtils { + + /** + * Execute the given DatabasePopulator against the given DataSource. + * @param populator the DatabasePopulator to execute + * @param dataSource the DataSource to execute against + */ + public static void execute(DatabasePopulator populator, DataSource dataSource) { + Assert.notNull(populator, "DatabasePopulator must be provided"); + Assert.notNull(dataSource, "DataSource must be provided"); + try { + Connection connection = dataSource.getConnection(); + try { + populator.populate(connection); + } + finally { + try { + connection.close(); + } + catch (SQLException ex) { + // ignore + } + } + } + catch (Exception ex) { + throw new DataAccessResourceFailureException("Failed to execute database script", ex); + } + } + +} diff --git a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/init/ResourceDatabasePopulator.java b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/init/ResourceDatabasePopulator.java index 596f8b1a33a..e65cb24a5e1 100644 --- a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/init/ResourceDatabasePopulator.java +++ b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/init/ResourceDatabasePopulator.java @@ -55,14 +55,14 @@ public class ResourceDatabasePopulator implements DatabasePopulator { private String sqlScriptEncoding; + private String separator; + private String commentPrefix = DEFAULT_COMMENT_PREFIX; private boolean continueOnError = false; private boolean ignoreFailedDrops = false; - private String separator = null; - /** * Add a script to execute to populate the database. @@ -90,6 +90,13 @@ public class ResourceDatabasePopulator implements DatabasePopulator { this.sqlScriptEncoding = sqlScriptEncoding; } + /** + * Specify the statement separator, if a custom one. + */ + public void setSeparator(String separator) { + this.separator = separator; + } + /** * Set the line prefix that identifies comments in the SQL script. * Default is "--". @@ -116,13 +123,6 @@ public class ResourceDatabasePopulator implements DatabasePopulator { this.ignoreFailedDrops = ignoreFailedDrops; } - /** - * Specify the statement separator, if a custom one. - */ - public void setSeparator(String separator) { - this.separator = separator; - } - public void populate(Connection connection) throws SQLException { for (Resource script : this.scripts) { diff --git a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/init/package-info.java b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/init/package-info.java new file mode 100644 index 00000000000..5b10fffff51 --- /dev/null +++ b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/init/package-info.java @@ -0,0 +1,8 @@ + +/** + * + * Provides extensible support for initializing databases through scripts. + * + */ +package org.springframework.jdbc.datasource.init; + diff --git a/org.springframework.jdbc/src/main/resources/org/springframework/jdbc/config/spring-jdbc-3.0.xsd b/org.springframework.jdbc/src/main/resources/org/springframework/jdbc/config/spring-jdbc-3.0.xsd index c7e57ae8e2e..9c25333a08e 100644 --- a/org.springframework.jdbc/src/main/resources/org/springframework/jdbc/config/spring-jdbc-3.0.xsd +++ b/org.springframework.jdbc/src/main/resources/org/springframework/jdbc/config/spring-jdbc-3.0.xsd @@ -18,7 +18,7 @@ ]]> - + @@ -26,8 +26,7 @@ - + - + - elements. ]]> - + + ]]> - + + A reference to a data source that should be initialized. Defaults to "dataSource". + ]]> - - + + - + Is this bean "enabled", meaning the scripts will be executed? @@ -87,8 +81,7 @@ - + Should failed SQL statements be ignored during initialization? @@ -159,4 +152,4 @@ - \ No newline at end of file + diff --git a/org.springframework.jdbc/src/main/resources/org/springframework/jdbc/config/spring-jdbc-3.1.xsd b/org.springframework.jdbc/src/main/resources/org/springframework/jdbc/config/spring-jdbc-3.1.xsd index 39365eb33c3..8eef9cb6fb2 100644 --- a/org.springframework.jdbc/src/main/resources/org/springframework/jdbc/config/spring-jdbc-3.1.xsd +++ b/org.springframework.jdbc/src/main/resources/org/springframework/jdbc/config/spring-jdbc-3.1.xsd @@ -12,13 +12,12 @@ - - + @@ -26,8 +25,7 @@ - + - + - elements. ]]> - + + ]]> - + + A reference to a data source that should be initialized. Defaults to "dataSource". + ]]> - - + + - + - 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. - + - Should failed SQL statements be ignored during - initialization? + Should failed SQL statements be ignored during initialization? @@ -134,6 +123,13 @@ ]]> + + + + + - - + + diff --git a/org.springframework.jdbc/src/test/java/org/springframework/jdbc/config/InitializeDatabaseIntegrationTest.java b/org.springframework.jdbc/src/test/java/org/springframework/jdbc/config/InitializeDatabaseIntegrationTests.java similarity index 82% rename from org.springframework.jdbc/src/test/java/org/springframework/jdbc/config/InitializeDatabaseIntegrationTest.java rename to org.springframework.jdbc/src/test/java/org/springframework/jdbc/config/InitializeDatabaseIntegrationTests.java index f932ca42d83..eb46775db94 100644 --- a/org.springframework.jdbc/src/test/java/org/springframework/jdbc/config/InitializeDatabaseIntegrationTest.java +++ b/org.springframework.jdbc/src/test/java/org/springframework/jdbc/config/InitializeDatabaseIntegrationTests.java @@ -1,21 +1,40 @@ -package org.springframework.jdbc.config; +/* + * Copyright 2002-2011 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. + */ -import static org.junit.Assert.assertEquals; +package org.springframework.jdbc.config; import java.util.List; import java.util.Map; - import javax.sql.DataSource; import org.junit.After; import org.junit.Before; import org.junit.Test; + import org.springframework.beans.factory.InitializingBean; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.jdbc.BadSqlGrammarException; import org.springframework.jdbc.core.JdbcTemplate; -public class InitializeDatabaseIntegrationTest { +import static org.junit.Assert.*; + +/** + * @author Dave Syer + */ +public class InitializeDatabaseIntegrationTests { private String enabled; private ClassPathXmlApplicationContext context; diff --git a/org.springframework.jdbc/src/test/java/org/springframework/jdbc/config/JdbcNamespaceIntegrationTest.java b/org.springframework.jdbc/src/test/java/org/springframework/jdbc/config/JdbcNamespaceIntegrationTests.java similarity index 68% rename from org.springframework.jdbc/src/test/java/org/springframework/jdbc/config/JdbcNamespaceIntegrationTest.java rename to org.springframework.jdbc/src/test/java/org/springframework/jdbc/config/JdbcNamespaceIntegrationTests.java index 08d6d6bfc24..3419ccd417b 100644 --- a/org.springframework.jdbc/src/test/java/org/springframework/jdbc/config/JdbcNamespaceIntegrationTest.java +++ b/org.springframework.jdbc/src/test/java/org/springframework/jdbc/config/JdbcNamespaceIntegrationTests.java @@ -1,15 +1,27 @@ -package org.springframework.jdbc.config; +/* + * Copyright 2002-2011 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. + */ -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.notNullValue; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; +package org.springframework.jdbc.config; 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; @@ -19,9 +31,17 @@ 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.embedded.EmbeddedDatabaseFactoryBean; import org.springframework.jdbc.datasource.init.DataSourceInitializer; -public class JdbcNamespaceIntegrationTest { +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +/** + * @author Dave Syer + * @author Juergen Hoeller + */ +public class JdbcNamespaceIntegrationTests { @Rule public ExpectedException expected = ExpectedException.none(); @@ -59,6 +79,14 @@ public class JdbcNamespaceIntegrationTest { context.close(); } + @Test + public void testCreateWithEndingsNested() throws Exception { + ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( + "org/springframework/jdbc/config/jdbc-initialize-endings-nested-config.xml"); + assertCorrectSetup(context, 2, "dataSource"); + context.close(); + } + @Test public void testCreateAndDestroy() throws Exception { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( @@ -70,23 +98,38 @@ public class JdbcNamespaceIntegrationTest { context.getBean(DataSourceInitializer.class).destroy(); expected.expect(BadSqlGrammarException.class); // Table has been dropped assertEquals(1, template.queryForInt("select count(*) from T_TEST")); - } finally { + } + finally { context.close(); } } @Test - public void testMultipleDataSourcesHaveDifferentDatabaseNames() throws Exception { + public void testCreateAndDestroyNested() throws Exception { + ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( + "org/springframework/jdbc/config/jdbc-destroy-nested-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(EmbeddedDatabaseFactoryBean.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 { DefaultListableBeanFactory factory = new XmlBeanFactory(new ClassPathResource( "org/springframework/jdbc/config/jdbc-config-multiple-datasources.xml")); - assertBeanPropertyValueOf("databaseName", "firstDataSource", factory); assertBeanPropertyValueOf("databaseName", "secondDataSource", factory); } private void assertBeanPropertyValueOf(String propertyName, String expected, DefaultListableBeanFactory factory) { - BeanDefinition bean = factory.getBeanDefinition(expected); PropertyValue value = bean.getPropertyValues().getPropertyValue(propertyName); assertThat(value, is(notNullValue())); @@ -98,14 +141,14 @@ public class JdbcNamespaceIntegrationTest { } private void assertCorrectSetup(ConfigurableApplicationContext context, int count, String... dataSources) { - try { for (String dataSourceName : dataSources) { DataSource dataSource = context.getBean(dataSourceName, DataSource.class); JdbcTemplate template = new JdbcTemplate(dataSource); assertEquals(count, template.queryForInt("select count(*) from T_TEST")); } - } finally { + } + finally { context.close(); } diff --git a/org.springframework.jdbc/src/test/resources/org/springframework/jdbc/config/jdbc-destroy-nested-config.xml b/org.springframework.jdbc/src/test/resources/org/springframework/jdbc/config/jdbc-destroy-nested-config.xml new file mode 100644 index 00000000000..628e6412c84 --- /dev/null +++ b/org.springframework.jdbc/src/test/resources/org/springframework/jdbc/config/jdbc-destroy-nested-config.xml @@ -0,0 +1,14 @@ + + + + + + + + + + diff --git a/org.springframework.jdbc/src/test/resources/org/springframework/jdbc/config/jdbc-initialize-cache-config.xml b/org.springframework.jdbc/src/test/resources/org/springframework/jdbc/config/jdbc-initialize-cache-config.xml index 01897f217b5..24135d9fd5e 100644 --- a/org.springframework.jdbc/src/test/resources/org/springframework/jdbc/config/jdbc-initialize-cache-config.xml +++ b/org.springframework.jdbc/src/test/resources/org/springframework/jdbc/config/jdbc-initialize-cache-config.xml @@ -14,7 +14,7 @@ - + diff --git a/org.springframework.jdbc/src/test/resources/org/springframework/jdbc/config/jdbc-initialize-endings-config.xml b/org.springframework.jdbc/src/test/resources/org/springframework/jdbc/config/jdbc-initialize-endings-config.xml index da9700b7f04..18de5ce819c 100644 --- a/org.springframework.jdbc/src/test/resources/org/springframework/jdbc/config/jdbc-initialize-endings-config.xml +++ b/org.springframework.jdbc/src/test/resources/org/springframework/jdbc/config/jdbc-initialize-endings-config.xml @@ -6,9 +6,9 @@ http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.1.xsd"> - + - + diff --git a/org.springframework.jdbc/src/test/resources/org/springframework/jdbc/config/jdbc-initialize-endings-nested-config.xml b/org.springframework.jdbc/src/test/resources/org/springframework/jdbc/config/jdbc-initialize-endings-nested-config.xml new file mode 100644 index 00000000000..07d18a31975 --- /dev/null +++ b/org.springframework.jdbc/src/test/resources/org/springframework/jdbc/config/jdbc-initialize-endings-nested-config.xml @@ -0,0 +1,13 @@ + + + + + + + + +