Browse Source

Refactor SQL script support

This commit continues the work in the previous commit as follows:

 - Introduced an exception hierarchy for exceptions related to SQL
   scripts, with ScriptException as the base.

 - CannotReadScriptException and ScriptStatementFailedException now
   extend ScriptException.

 - Introduced ScriptParseException, used by ScriptUtils.splitSqlScript().

 - DatabasePopulatorUtils.execute() now explicitly throws a
   DataAccessException.

 - Polished Javadoc in ResourceDatabasePopulator.

 - Overhauled Javadoc in ScriptUtils and documented all constants.

 - Added missing @author tags for original authors in ScriptUtils and
   ScriptUtilsTests.

 - ScriptUtils.splitSqlScript() now asserts preconditions.

 - Deleted superfluous methods in ScriptUtils and changed method
   visibility to private or package private as appropriate.

 - Deleted the ScriptStatementExecutor introduced in the previous
   commit; ScriptUtils.executeSqlScript() now accepts a JDBC Connection;
   JdbcTestUtils, AbstractTransactionalJUnit4SpringContextTests, and
   AbstractTransactionalTestNGSpringContextTests now use
   DatabasePopulatorUtils to execute a ResourceDatabasePopulator instead
   of executing a script directly via ScriptUtils.

 - Introduced JdbcTestUtilsIntegrationTests.

Issue: SPR-9531
pull/448/merge
Sam Brannen 12 years ago
parent
commit
2bfd6ddcf4
  1. 4
      spring-core/src/main/java/org/springframework/core/io/support/EncodedResource.java
  2. 8
      spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/CannotReadScriptException.java
  3. 15
      spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/DatabasePopulatorUtils.java
  4. 79
      spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/ResourceDatabasePopulator.java
  5. 45
      spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/ScriptException.java
  6. 54
      spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/ScriptParseException.java
  7. 12
      spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/ScriptStatementFailedException.java
  8. 298
      spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/ScriptUtils.java
  9. 6
      spring-jdbc/src/test/java/org/springframework/jdbc/datasource/init/DatabasePopulatorTests.java
  10. 104
      spring-jdbc/src/test/java/org/springframework/jdbc/datasource/init/ScriptUtilsTests.java
  11. 23
      spring-test/src/main/java/org/springframework/test/context/junit4/AbstractTransactionalJUnit4SpringContextTests.java
  12. 23
      spring-test/src/main/java/org/springframework/test/context/testng/AbstractTransactionalTestNGSpringContextTests.java
  13. 77
      spring-test/src/main/java/org/springframework/test/jdbc/JdbcTestUtils.java
  14. 63
      spring-test/src/test/java/org/springframework/test/jdbc/JdbcTestUtilsIntegrationTests.java
  15. 13
      spring-test/src/test/java/org/springframework/test/jdbc/JdbcTestUtilsTests.java
  16. 1
      spring-test/src/test/resources/org/springframework/test/jdbc/data.sql
  17. 4
      spring-test/src/test/resources/org/springframework/test/jdbc/schema.sql

4
spring-core/src/main/java/org/springframework/core/io/support/EncodedResource.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2013 the original author or authors. * Copyright 2002-2014 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -31,7 +31,7 @@ import org.springframework.util.ObjectUtils;
* with a specific encoding to be used for reading from the resource. * with a specific encoding to be used for reading from the resource.
* *
* <p>Used as argument for operations that support to read content with * <p>Used as argument for operations that support to read content with
* a specific encoding (usually through a {@code java.io.Reader}. * a specific encoding (usually through a {@code java.io.Reader}).
* *
* @author Juergen Hoeller * @author Juergen Hoeller
* @since 1.2.6 * @since 1.2.6

8
spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/CannotReadScriptException.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2012 the original author or authors. * Copyright 2002-2014 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -19,14 +19,14 @@ package org.springframework.jdbc.datasource.init;
import org.springframework.core.io.support.EncodedResource; import org.springframework.core.io.support.EncodedResource;
/** /**
* Thrown by {@link ResourceDatabasePopulator} if one of its SQL scripts cannot * Thrown by {@link ScriptUtils} if an SQL script cannot be read.
* be read during population.
* *
* @author Keith Donald * @author Keith Donald
* @author Sam Brannen
* @since 3.0 * @since 3.0
*/ */
@SuppressWarnings("serial") @SuppressWarnings("serial")
public class CannotReadScriptException extends RuntimeException { public class CannotReadScriptException extends ScriptException {
/** /**
* Construct a new {@code CannotReadScriptException}. * Construct a new {@code CannotReadScriptException}.

15
spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/DatabasePopulatorUtils.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2012 the original author or authors. * Copyright 2002-2014 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -20,25 +20,28 @@ import java.sql.Connection;
import javax.sql.DataSource; import javax.sql.DataSource;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataAccessResourceFailureException; import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.jdbc.datasource.DataSourceUtils; import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.util.Assert; import org.springframework.util.Assert;
/** /**
* Utility methods for executing a DatabasePopulator. * Utility methods for executing a {@link DatabasePopulator}.
* *
* @author Juergen Hoeller * @author Juergen Hoeller
* @author Oliver Gierke * @author Oliver Gierke
* @author Sam Brannen
* @since 3.1 * @since 3.1
*/ */
public abstract class DatabasePopulatorUtils { public abstract class DatabasePopulatorUtils {
/** /**
* Execute the given DatabasePopulator against the given DataSource. * Execute the given {@link DatabasePopulator} against the given {@link DataSource}.
* @param populator the DatabasePopulator to execute * @param populator the {@code DatabasePopulator} to execute
* @param dataSource the DataSource to execute against * @param dataSource the {@code DataSource} to execute against
* @throws DataAccessException if an error occurs
*/ */
public static void execute(DatabasePopulator populator, DataSource dataSource) { public static void execute(DatabasePopulator populator, DataSource dataSource) throws DataAccessException {
Assert.notNull(populator, "DatabasePopulator must be provided"); Assert.notNull(populator, "DatabasePopulator must be provided");
Assert.notNull(dataSource, "DataSource must be provided"); Assert.notNull(dataSource, "DataSource must be provided");
try { try {

79
spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/ResourceDatabasePopulator.java

@ -18,16 +18,12 @@ package org.springframework.jdbc.datasource.init;
import java.sql.Connection; import java.sql.Connection;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.core.io.support.EncodedResource; import org.springframework.core.io.support.EncodedResource;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.UncategorizedSQLException;
import org.springframework.jdbc.datasource.init.ScriptUtils.ScriptStatementExecutor;
/** /**
* Populates a database from SQL scripts defined in external resources. * Populates a database from SQL scripts defined in external resources.
@ -55,9 +51,9 @@ public class ResourceDatabasePopulator implements DatabasePopulator {
private String commentPrefix = ScriptUtils.DEFAULT_COMMENT_PREFIX; private String commentPrefix = ScriptUtils.DEFAULT_COMMENT_PREFIX;
private String blockCommentStartDelimiter = ScriptUtils.DEFAULT_BLOCK_COMMENT_START_DELIMITER; private String blockCommentStartDelimiter = ScriptUtils.DEFAULT_BLOCK_COMMENT_START_DELIMITER;
private String blockCommentEndDelimiter = ScriptUtils.DEFAULT_BLOCK_COMMENT_END_DELIMITER; private String blockCommentEndDelimiter = ScriptUtils.DEFAULT_BLOCK_COMMENT_END_DELIMITER;
private boolean continueOnError = false; private boolean continueOnError = false;
private boolean ignoreFailedDrops = false; private boolean ignoreFailedDrops = false;
@ -65,7 +61,7 @@ public class ResourceDatabasePopulator implements DatabasePopulator {
/** /**
* Add a script to execute to populate the database. * Add a script to execute to populate the database.
* @param script the path to a SQL script * @param script the path to an SQL script
*/ */
public void addScript(Resource script) { public void addScript(Resource script) {
this.scripts.add(script); this.scripts.add(script);
@ -81,8 +77,8 @@ public class ResourceDatabasePopulator implements DatabasePopulator {
/** /**
* Specify the encoding for SQL scripts, if different from the platform encoding. * Specify the encoding for SQL scripts, if different from the platform encoding.
* Note setting this property has no effect on added scripts that are already * <p>Note that setting this property has no effect on added scripts that are
* {@link EncodedResource encoded resources}. * already {@linkplain EncodedResource encoded resources}.
* @see #addScript(Resource) * @see #addScript(Resource)
*/ */
public void setSqlScriptEncoding(String sqlScriptEncoding) { public void setSqlScriptEncoding(String sqlScriptEncoding) {
@ -90,41 +86,46 @@ public class ResourceDatabasePopulator implements DatabasePopulator {
} }
/** /**
* Specify the statement separator, if a custom one. Default is ";". * Specify the statement separator, if a custom one.
* <p>Default is ";".
*/ */
public void setSeparator(String separator) { public void setSeparator(String separator) {
this.separator = separator; this.separator = separator;
} }
/** /**
* Set the line prefix that identifies comments in the SQL script. * Set the prefix that identifies line comments within the SQL scripts.
* Default is "--". * <p>Default is "--".
*/ */
public void setCommentPrefix(String commentPrefix) { public void setCommentPrefix(String commentPrefix) {
this.commentPrefix = commentPrefix; this.commentPrefix = commentPrefix;
} }
/** /**
* Set the block comment start delimiter in the SQL script. * Set the start delimiter that identifies block comments within the SQL
* Default is "/*" * scripts.
* <p>Default is "/*".
* @since 4.0.3 * @since 4.0.3
* @see #setBlockCommentEndDelimiter
*/ */
public void setBlockCommentStartDelimiter(String blockCommentStartDelimiter) { public void setBlockCommentStartDelimiter(String blockCommentStartDelimiter) {
this.blockCommentStartDelimiter = blockCommentStartDelimiter; this.blockCommentStartDelimiter = blockCommentStartDelimiter;
} }
/** /**
* Set the block comment end delimiter in the SQL script. * Set the end delimiter that identifies block comments within the SQL
* Default is "*\/" * scripts.
* <p>Default is "*&#47;".
* @since 4.0.3 * @since 4.0.3
* @see #setBlockCommentStartDelimiter
*/ */
public void setBlockCommentEndDelimiter(String blockCommentEndDelimiter) { public void setBlockCommentEndDelimiter(String blockCommentEndDelimiter) {
this.blockCommentEndDelimiter = blockCommentEndDelimiter; this.blockCommentEndDelimiter = blockCommentEndDelimiter;
} }
/** /**
* Flag to indicate that all failures in SQL should be logged but not cause a failure. * Flag to indicate that all failures in SQL should be logged but not cause a failure.
* Defaults to false. * <p>Defaults to {@code false}.
*/ */
public void setContinueOnError(boolean continueOnError) { public void setContinueOnError(boolean continueOnError) {
this.continueOnError = continueOnError; this.continueOnError = continueOnError;
@ -133,44 +134,20 @@ public class ResourceDatabasePopulator implements DatabasePopulator {
/** /**
* Flag to indicate that a failed SQL {@code DROP} statement can be ignored. * Flag to indicate that a failed SQL {@code DROP} statement can be ignored.
* <p>This is useful for non-embedded databases whose SQL dialect does not support an * <p>This is useful for non-embedded databases whose SQL dialect does not support an
* {@code IF EXISTS} clause in a {@code DROP}. The default is false so that if the * {@code IF EXISTS} clause in a {@code DROP} statement.
* populator runs accidentally, it will fail fast when the script starts with a {@code DROP}. * <p>The default is {@code false} so that if the populator runs accidentally, it will
* fail fast if the script starts with a {@code DROP} statement.
*/ */
public void setIgnoreFailedDrops(boolean ignoreFailedDrops) { public void setIgnoreFailedDrops(boolean ignoreFailedDrops) {
this.ignoreFailedDrops = ignoreFailedDrops; this.ignoreFailedDrops = ignoreFailedDrops;
} }
@Override @Override
public void populate(Connection connection) throws SQLException { public void populate(Connection connection) throws SQLException {
Statement statement = null; for (Resource script : this.scripts) {
try { ScriptUtils.executeSqlScript(connection, applyEncodingIfNecessary(script), this.continueOnError,
statement = connection.createStatement(); this.ignoreFailedDrops, this.commentPrefix, this.separator, this.blockCommentStartDelimiter,
final Statement stmt = statement; this.blockCommentEndDelimiter);
for (Resource script : this.scripts) {
ScriptUtils.executeSqlScript(
new ScriptStatementExecutor() {
@Override
public int executeScriptStatement(String statement) throws DataAccessException {
try {
stmt.execute(statement);
return stmt.getUpdateCount();
}
catch (SQLException e) {
throw new UncategorizedSQLException(getClass().getName(), statement, e);
}
}
},
applyEncodingIfNecessary(script), this.continueOnError, this.ignoreFailedDrops,
this.commentPrefix, this.separator, this.blockCommentStartDelimiter,
this.blockCommentEndDelimiter);
}
}
finally {
if (statement != null) {
statement.close();
}
} }
} }

45
spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/ScriptException.java

@ -0,0 +1,45 @@
/*
* Copyright 2002-2014 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;
/**
* Root of the hierarchy of SQL script exceptions.
*
* @author Sam Brannen
* @since 4.0.3
*/
@SuppressWarnings("serial")
public abstract class ScriptException extends RuntimeException {
/**
* Constructor for {@code ScriptException}.
* @param message the detail message
*/
public ScriptException(String message) {
super(message);
}
/**
* Constructor for {@code ScriptException}.
* @param message the detail message
* @param cause the root cause
*/
public ScriptException(String message, Throwable cause) {
super(message, cause);
}
}

54
spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/ScriptParseException.java

@ -0,0 +1,54 @@
/*
* Copyright 2002-2014 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 org.springframework.core.io.support.EncodedResource;
/**
* Thrown by {@link ScriptUtils} if an SQL script cannot be properly parsed.
*
* @author Sam Brannen
* @since 4.0.3
*/
@SuppressWarnings("serial")
public class ScriptParseException extends ScriptException {
/**
* Construct a new {@code ScriptParseException}.
* @param message detailed message
* @param resource the resource from which the SQL script was read
*/
public ScriptParseException(String message, EncodedResource resource) {
super(buildMessage(message, resource));
}
/**
* Construct a new {@code ScriptParseException}.
* @param message detailed message
* @param resource the resource from which the SQL script was read
* @param cause the underlying cause of the failure
*/
public ScriptParseException(String message, EncodedResource resource, Throwable cause) {
super(buildMessage(message, resource), cause);
}
private static String buildMessage(String message, EncodedResource resource) {
return String.format("Failed to parse SQL script from resource [%s]: %s", (resource == null ? "<unknown>"
: resource), message);
}
}

12
spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/ScriptStatementFailedException.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2012 the original author or authors. * Copyright 2002-2014 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -19,15 +19,15 @@ package org.springframework.jdbc.datasource.init;
import org.springframework.core.io.support.EncodedResource; import org.springframework.core.io.support.EncodedResource;
/** /**
* Thrown by {@link ResourceDatabasePopulator} if a statement in one of its SQL scripts * Thrown by {@link ScriptUtils} if a statement in an SQL script failed when
* failed when executing it against the target database. * executing it against the target database.
* *
* @author Juergen Hoeller * @author Juergen Hoeller
* @author Sam Brannen * @author Sam Brannen
* @since 3.0.5 * @since 3.0.5
*/ */
@SuppressWarnings("serial") @SuppressWarnings("serial")
public class ScriptStatementFailedException extends RuntimeException { public class ScriptStatementFailedException extends ScriptException {
/** /**
* Construct a new {@code ScriptStatementFailedException}. * Construct a new {@code ScriptStatementFailedException}.
@ -37,8 +37,8 @@ public class ScriptStatementFailedException extends RuntimeException {
* @param cause the underlying cause of the failure * @param cause the underlying cause of the failure
*/ */
public ScriptStatementFailedException(String statement, int lineNumber, EncodedResource resource, Throwable cause) { public ScriptStatementFailedException(String statement, int lineNumber, EncodedResource resource, Throwable cause) {
super("Failed to execute SQL script statement at line " + lineNumber + super("Failed to execute SQL script statement at line " + lineNumber + " of resource " + resource + ": "
" of resource " + resource + ": " + statement, cause); + statement, cause);
} }
} }

298
spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/ScriptUtils.java

@ -18,73 +18,119 @@ package org.springframework.jdbc.datasource.init;
import java.io.IOException; import java.io.IOException;
import java.io.LineNumberReader; import java.io.LineNumberReader;
import java.sql.Connection;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.springframework.core.io.support.EncodedResource; import org.springframework.core.io.support.EncodedResource;
import org.springframework.dao.DataAccessException; import org.springframework.util.Assert;
import org.springframework.jdbc.BadSqlGrammarException;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
/** /**
* Generic utility methods for working with SQL scripts. Mainly for * Generic utility methods for working with SQL scripts. Mainly for internal use
* internal use within the framework. * within the framework.
* *
* @author Thomas Risberg
* @author Sam Brannen
* @author Juergen Hoeller
* @author Keith Donald
* @author Dave Syer
* @author Chris Beams
* @author Oliver Gierke
* @author Chris Baldwin * @author Chris Baldwin
* @since 4.0.3 * @since 4.0.3
*/ */
public abstract class ScriptUtils { public abstract class ScriptUtils {
private static final Log logger = LogFactory.getLog(ScriptUtils.class);
/**
* Default statement separator within SQL scripts.
*/
public static final String DEFAULT_STATEMENT_SEPARATOR = ";";
/**
* Default prefix for line comments within SQL scripts.
*/
public static final String DEFAULT_COMMENT_PREFIX = "--"; public static final String DEFAULT_COMMENT_PREFIX = "--";
public static final char DEFAULT_STATEMENT_SEPARATOR_CHAR = ';'; /**
* Default start delimiter for block comments within SQL scripts.
public static final String DEFAULT_STATEMENT_SEPARATOR = String.valueOf(DEFAULT_STATEMENT_SEPARATOR_CHAR); */
public static final String DEFAULT_BLOCK_COMMENT_START_DELIMITER = "/*"; public static final String DEFAULT_BLOCK_COMMENT_START_DELIMITER = "/*";
/**
* Default end delimiter for block comments within SQL scripts.
*/
public static final String DEFAULT_BLOCK_COMMENT_END_DELIMITER = "*/"; public static final String DEFAULT_BLOCK_COMMENT_END_DELIMITER = "*/";
private static final Log logger = LogFactory.getLog(ScriptUtils.class);
/**
* Prevent instantiation of this utility class.
*/
private ScriptUtils() {
/* no-op */
}
/** /**
* Split an SQL script into separate statements delimited by the provided * Split an SQL script into separate statements delimited by the provided
* delimiter character. Each individual statement will be added to the * delimiter character. Each individual statement will be added to the
* provided {@code List}. * provided {@code List}.
* <p>Within a statement, "{@code --}" will be used as the comment prefix; * <p>Within the script, {@value #DEFAULT_COMMENT_PREFIX} will be used as the
* any text beginning with the comment prefix and extending to the end of * comment prefix; any text beginning with the comment prefix and extending to
* the line will be omitted from the statement. In addition, multiple adjacent * the end of the line will be omitted from the output. Similarly,
* whitespace characters will be collapsed into a single space. * {@value #DEFAULT_BLOCK_COMMENT_START_DELIMITER} and
* {@value #DEFAULT_BLOCK_COMMENT_END_DELIMITER} will be used as the
* <em>start</em> and <em>end</em> block comment delimiters: any text enclosed
* in a block comment will be omitted from the output. In addition, multiple
* adjacent whitespace characters will be collapsed into a single space.
* @param script the SQL script * @param script the SQL script
* @param delim character delimiting each statement &mdash; typically a ';' character * @param delimiter character delimiting each statement &mdash; typically a ';' character
* @param statements the list that will contain the individual statements * @param statements the list that will contain the individual statements
* @since 4.0.3 * @see #splitSqlScript(EncodedResource, String, String, String, String, String, List)
*/ */
public static void splitSqlScript(String script, char delim, List<String> statements) { public static void splitSqlScript(String script, char delimiter, List<String> statements) throws ScriptException {
splitSqlScript(script, String.valueOf(delim), DEFAULT_COMMENT_PREFIX, DEFAULT_BLOCK_COMMENT_START_DELIMITER, splitSqlScript(null, script, String.valueOf(delimiter), DEFAULT_COMMENT_PREFIX,
DEFAULT_BLOCK_COMMENT_END_DELIMITER, statements); DEFAULT_BLOCK_COMMENT_START_DELIMITER, DEFAULT_BLOCK_COMMENT_END_DELIMITER, statements);
} }
/** /**
* Split an SQL script into separate statements delimited by the provided * Split an SQL script into separate statements delimited by the provided
* delimiter string. Each individual statement will be added to the provided * delimiter string. Each individual statement will be added to the provided
* {@code List}. * {@code List}.
* <p>Within a statement, the provided {@code commentPrefix} will be honored; * <p>Within the script, the provided {@code commentPrefix} will be honored:
* any text beginning with the comment prefix and extending to the end of the * any text beginning with the comment prefix and extending to the end of the
* line will be omitted from the statement. In addition, multiple adjacent * line will be omitted from the output. Similarly, the provided
* whitespace characters will be collapsed into a single space. * {@code blockCommentStartDelimiter} and {@code blockCommentEndDelimiter}
* @param script the SQL script * delimiters will be honored: any text enclosed in a block comment will be
* @param delim character delimiting each statement &mdash; typically a ';' character * omitted from the output. In addition, multiple adjacent whitespace characters
* @param commentPrefix the prefix that identifies line comments in the SQL script &mdash; typically "--" * will be collapsed into a single space.
* @param statements the List that will contain the individual statements * @param resource the resource from which the script was read
* @since 4.0.3 * @param script the SQL script; never {@code null} or empty
* @param delimiter text delimiting each statement &mdash; typically a ';'
* character; never {@code null}
* @param commentPrefix the prefix that identifies SQL line comments &mdash;
* typically "--"; never {@code null} or empty
* @param blockCommentStartDelimiter the <em>start</em> block comment delimiter;
* never {@code null} or empty
* @param blockCommentEndDelimiter the <em>end</em> block comment delimiter;
* never {@code null} or empty
* @param statements the list that will contain the individual statements
*/ */
public static void splitSqlScript(String script, String delim, String commentPrefix, String blockCommentOpen, public static void splitSqlScript(EncodedResource resource, String script, String delimiter, String commentPrefix,
String blockCommentClose, List<String> statements) { String blockCommentStartDelimiter, String blockCommentEndDelimiter, List<String> statements)
throws ScriptException {
Assert.hasText(script, "script must not be null or empty");
Assert.notNull(delimiter, "delimiter must not be null");
Assert.hasText(commentPrefix, "commentPrefix must not be null or empty");
Assert.hasText(blockCommentStartDelimiter, "blockCommentStartDelimiter must not be null or empty");
Assert.hasText(blockCommentEndDelimiter, "blockCommentEndDelimiter must not be null or empty");
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
boolean inLiteral = false; boolean inLiteral = false;
boolean inEscape = false; boolean inEscape = false;
@ -106,13 +152,13 @@ public abstract class ScriptUtils {
inLiteral = !inLiteral; inLiteral = !inLiteral;
} }
if (!inLiteral) { if (!inLiteral) {
if (script.startsWith(delim, i)) { if (script.startsWith(delimiter, i)) {
// we've reached the end of the current statement // we've reached the end of the current statement
if (sb.length() > 0) { if (sb.length() > 0) {
statements.add(sb.toString()); statements.add(sb.toString());
sb = new StringBuilder(); sb = new StringBuilder();
} }
i += delim.length() - 1; i += delimiter.length() - 1;
continue; continue;
} }
else if (script.startsWith(commentPrefix, i)) { else if (script.startsWith(commentPrefix, i)) {
@ -128,16 +174,16 @@ public abstract class ScriptUtils {
break; break;
} }
} }
else if (script.startsWith(blockCommentOpen, i)) { else if (script.startsWith(blockCommentStartDelimiter, i)) {
// skip over any block comments // skip over any block comments
int indexOfCommentClose = script.indexOf(blockCommentClose, i); int indexOfCommentEnd = script.indexOf(blockCommentEndDelimiter, i);
if (indexOfCommentClose > i) { if (indexOfCommentEnd > i) {
i = indexOfCommentClose + blockCommentClose.length() - 1; i = indexOfCommentEnd + blockCommentEndDelimiter.length() - 1;
continue; continue;
} }
else { else {
throw new BadSqlGrammarException("", script.substring(i), throw new ScriptParseException(String.format("Missing block comment end delimiter [%s].",
new SQLException("Missing block comment end delimiter")); blockCommentEndDelimiter), resource);
} }
} }
else if (c == ' ' || c == '\n' || c == '\t') { else if (c == ' ' || c == '\n' || c == '\t') {
@ -158,52 +204,32 @@ public abstract class ScriptUtils {
} }
/** /**
* Read a script from the provided {@code LineNumberReader}, using * Read a script from the given resource, using "{@code --}" as the comment prefix
* "{@code --}" as the comment prefix, and build a {@code String} containing * and "{@code ;}" as the statement separator, and build a String containing the lines.
* the lines.
* @param lineNumberReader the {@code LineNumberReader} containing the script
* to be processed
* @return a {@code String} containing the script lines
* @see #readScript(LineNumberReader, String, String)
* @since 4.0.3
*/
public static String readScript(LineNumberReader lineNumberReader) throws IOException {
return readScript(lineNumberReader, DEFAULT_COMMENT_PREFIX, DEFAULT_STATEMENT_SEPARATOR);
}
/**
* Read a script from the given resource, using "{@code --}" as the comment prefix
* and "{@code ;} as the statement separator, and build a String containing the lines.
* @param resource the {@code EncodedResource} to be read * @param resource the {@code EncodedResource} to be read
* @return {@code String} containing the script lines * @return {@code String} containing the script lines
* @throws IOException in case of I/O errors * @throws IOException in case of I/O errors
* @since 4.0.3
*/ */
public static String readScript(EncodedResource resource) throws IOException { static String readScript(EncodedResource resource) throws IOException {
LineNumberReader lnr = new LineNumberReader(resource.getReader()); return readScript(resource, DEFAULT_COMMENT_PREFIX, DEFAULT_STATEMENT_SEPARATOR);
try {
return readScript(lnr, DEFAULT_COMMENT_PREFIX, DEFAULT_STATEMENT_SEPARATOR);
}
finally {
lnr.close();
}
} }
/** /**
* Read a script from the provided resource, using the supplied * Read a script from the provided resource, using the supplied
* comment prefix and statement separator, and build a {@code String} containing the lines. * comment prefix and statement separator, and build a {@code String} containing
* the lines.
* <p>Lines <em>beginning</em> with the comment prefix are excluded from the * <p>Lines <em>beginning</em> with the comment prefix are excluded from the
* results; however, line comments anywhere else &mdash; for example, within * results; however, line comments anywhere else &mdash; for example, within
* a statement &mdash; will be included in the results. * a statement &mdash; will be included in the results.
* @param resource the {@code EncodedResource} containing the script * @param resource the {@code EncodedResource} containing the script
* to be processed * to be processed
* @param commentPrefix the prefix that identifies comments in the SQL script &mdash; typically "--" * @param commentPrefix the prefix that identifies comments in the SQL script &mdash;
* typically "--"
* @param separator the statement separator in the SQL script &mdash; typically ";" * @param separator the statement separator in the SQL script &mdash; typically ";"
* @return a {@code String} containing the script lines * @return a {@code String} containing the script lines
* @since 4.0.3
*/ */
public static String readScript(EncodedResource resource, String commentPrefix, private static String readScript(EncodedResource resource, String commentPrefix, String separator)
String separator) throws IOException { throws IOException {
LineNumberReader lnr = new LineNumberReader(resource.getReader()); LineNumberReader lnr = new LineNumberReader(resource.getReader());
try { try {
return readScript(lnr, commentPrefix, separator); return readScript(lnr, commentPrefix, separator);
@ -212,27 +238,28 @@ public abstract class ScriptUtils {
lnr.close(); lnr.close();
} }
} }
/** /**
* Read a script from the provided {@code LineNumberReader}, using the supplied * Read a script from the provided {@code LineNumberReader}, using the supplied
* comment prefix and statement separator, and build a {@code String} containing the lines. * comment prefix and statement separator, and build a {@code String} containing
* the lines.
* <p>Lines <em>beginning</em> with the comment prefix are excluded from the * <p>Lines <em>beginning</em> with the comment prefix are excluded from the
* results; however, line comments anywhere else &mdash; for example, within * results; however, line comments anywhere else &mdash; for example, within
* a statement &mdash; will be included in the results. * a statement &mdash; will be included in the results.
* @param lineNumberReader the {@code LineNumberReader} containing the script * @param lineNumberReader the {@code LineNumberReader} containing the script
* to be processed * to be processed
* @param commentPrefix the prefix that identifies comments in the SQL script &mdash; typically "--" * @param commentPrefix the prefix that identifies comments in the SQL script &mdash;
* typically "--"
* @param separator the statement separator in the SQL script &mdash; typically ";" * @param separator the statement separator in the SQL script &mdash; typically ";"
* @return a {@code String} containing the script lines * @return a {@code String} containing the script lines
* @since 4.0.3
*/ */
public static String readScript(LineNumberReader lineNumberReader, String commentPrefix, public static String readScript(LineNumberReader lineNumberReader, String commentPrefix, String separator)
String separator) throws IOException { throws IOException {
String currentStatement = lineNumberReader.readLine(); String currentStatement = lineNumberReader.readLine();
StringBuilder scriptBuilder = new StringBuilder(); StringBuilder scriptBuilder = new StringBuilder();
while (currentStatement != null) { while (currentStatement != null) {
if (StringUtils.hasText(currentStatement) && if (StringUtils.hasText(currentStatement)
(commentPrefix != null && !currentStatement.startsWith(commentPrefix))) { && (commentPrefix != null && !currentStatement.startsWith(commentPrefix))) {
if (scriptBuilder.length() > 0) { if (scriptBuilder.length() > 0) {
scriptBuilder.append('\n'); scriptBuilder.append('\n');
} }
@ -259,21 +286,10 @@ public abstract class ScriptUtils {
} }
} }
/**
* Does the provided SQL script contain the specified delimiter?
* @param script the SQL script
* @param delim character delimiting each statement - typically a ';' character
* @since 4.0.3
*/
public static boolean containsSqlScriptDelimiters(String script, char delim) {
return containsSqlScriptDelimiters(script, String.valueOf(delim));
}
/** /**
* Does the provided SQL script contain the specified delimiter? * Does the provided SQL script contain the specified delimiter?
* @param script the SQL script * @param script the SQL script
* @param delim String delimiting each statement - typically a ';' character * @param delim String delimiting each statement - typically a ';' character
* @since 4.0.3
*/ */
public static boolean containsSqlScriptDelimiters(String script, String delim) { public static boolean containsSqlScriptDelimiters(String script, String delim) {
boolean inLiteral = false; boolean inLiteral = false;
@ -291,24 +307,29 @@ public abstract class ScriptUtils {
/** /**
* Execute the given SQL script. * Execute the given SQL script.
* <p>The script will normally be loaded by classpath. There should be one statement * <p>Statement separators and comments will be removed before executing
* per line. Any statement separators will be removed. * individual statements within the supplied script.
* <p><b>Do not use this method to execute DDL if you expect rollback.</b> * <p><b>Do not use this method to execute DDL if you expect rollback.</b>
* @param executor the {@code ScriptStatementExecutor} with which to perform JDBC operations * @param connection the JDBC connection to use to execute the script; already
* @param resource the resource (potentially associated with a specific encoding) to load the SQL script from * configured and ready to use
* @param continueOnError whether or not to continue without throwing an exception in the event of an error * @param resource the resource (potentially associated with a specific encoding)
* @param ignoreFailedDrops whether of not to continue in the event of specifically an error on a {@code DROP} * to load the SQL script from
* @param commentPrefix the script line comment prefix * @param continueOnError whether or not to continue without throwing an exception
* if not specified * in the event of an error
* @param separator the script statement separator, defaults to {@code DEFAUT_STATEMENT_SEPARATOR} * @param ignoreFailedDrops whether or not to continue in the event of specifically
* if not specified * an error on a {@code DROP} statement
* @param blockCommentStartDelim the script block comment starting delimiter * @param commentPrefix the prefix that identifies comments in the SQL script &mdash;
* @param blockCommentEndDelim the script block comment ending delimiter * typically "--"
* @since 4.0.3 * @param separator the script statement separator; defaults to
* {@value #DEFAULT_STATEMENT_SEPARATOR} if not specified
* @param blockCommentStartDelimiter the <em>start</em> block comment delimiter; never
* {@code null} or empty
* @param blockCommentEndDelimiter the <em>end</em> block comment delimiter; never
* {@code null} or empty
*/ */
public static void executeSqlScript(ScriptStatementExecutor executor, EncodedResource resource, public static void executeSqlScript(Connection connection, EncodedResource resource, boolean continueOnError,
boolean continueOnError, boolean ignoreFailedDrops, String commentPrefix, String separator, boolean ignoreFailedDrops, String commentPrefix, String separator, String blockCommentStartDelimiter,
String blockCommentStartDelim, String blockCommentEndDelim) throws DataAccessException { String blockCommentEndDelimiter) throws SQLException, ScriptException {
if (logger.isInfoEnabled()) { if (logger.isInfoEnabled()) {
logger.info("Executing SQL script from " + resource); logger.info("Executing SQL script from " + resource);
@ -322,54 +343,55 @@ public abstract class ScriptUtils {
catch (IOException ex) { catch (IOException ex) {
throw new CannotReadScriptException(resource, ex); throw new CannotReadScriptException(resource, ex);
} }
if (separator == null) { if (separator == null) {
separator = DEFAULT_STATEMENT_SEPARATOR; separator = DEFAULT_STATEMENT_SEPARATOR;
if (!containsSqlScriptDelimiters(script, separator)) { if (!containsSqlScriptDelimiters(script, separator)) {
separator = "\n"; separator = "\n";
} }
} }
splitSqlScript(script, separator, commentPrefix, blockCommentStartDelim, blockCommentEndDelim, statements);
splitSqlScript(resource, script, separator, commentPrefix, blockCommentStartDelimiter,
blockCommentEndDelimiter, statements);
int lineNumber = 0; int lineNumber = 0;
for (String statement : statements) { Statement stmt = connection.createStatement();
lineNumber++; try {
try { for (String statement : statements) {
int rowsAffected = executor.executeScriptStatement(statement); lineNumber++;
if (logger.isDebugEnabled()) { try {
logger.debug(rowsAffected + " returned as updateCount for SQL: " + statement); stmt.execute(statement);
} int rowsAffected = stmt.getUpdateCount();
}
catch (DataAccessException ex) {
boolean dropStatement = StringUtils.startsWithIgnoreCase(statement.trim(), "drop");
if (continueOnError || (dropStatement && ignoreFailedDrops)) {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("Failed to execute SQL script statement at line " + lineNumber + logger.debug(rowsAffected + " returned as updateCount for SQL: " + statement);
" of resource " + resource + ": " + statement, ex);
} }
} }
else { catch (SQLException ex) {
throw new ScriptStatementFailedException(statement, lineNumber, resource, ex); boolean dropStatement = StringUtils.startsWithIgnoreCase(statement.trim(), "drop");
if (continueOnError || (dropStatement && ignoreFailedDrops)) {
if (logger.isDebugEnabled()) {
logger.debug("Failed to execute SQL script statement at line " + lineNumber
+ " of resource " + resource + ": " + statement, ex);
}
}
else {
throw new ScriptStatementFailedException(statement, lineNumber, resource, ex);
}
} }
} }
} }
finally {
try {
stmt.close();
}
catch (Throwable ex) {
logger.debug("Could not close JDBC Statement", ex);
}
}
long elapsedTime = System.currentTimeMillis() - startTime; long elapsedTime = System.currentTimeMillis() - startTime;
if (logger.isInfoEnabled()) { if (logger.isInfoEnabled()) {
logger.info("Done executing SQL script from " + resource + " in " + elapsedTime + " ms."); logger.info("Executed SQL script from " + resource + " in " + elapsedTime + " ms.");
} }
} }
/**
* Interface to be implemented by an object so that {@code executeScript()} is able to use
* it to execute script statements.
* @since 4.0.3
*/
public interface ScriptStatementExecutor
{
/**
* Execute the given SQL statement and return a count of the number of affected rows.
* @return the number of rows affected by the statement
* @throws DataAccessException if there is a problem during statement execution
*/
public int executeScriptStatement(String statement) throws DataAccessException;
}
} }

6
spring-jdbc/src/test/java/org/springframework/jdbc/datasource/init/DatabasePopulatorTests.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2013 the original author or authors. * Copyright 2002-2014 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -40,9 +40,7 @@ import static org.mockito.BDDMockito.*;
*/ */
public class DatabasePopulatorTests { public class DatabasePopulatorTests {
private final EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder(); private final EmbeddedDatabase db = new EmbeddedDatabaseBuilder().build();
private final EmbeddedDatabase db = builder.build();
private final ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator(); private final ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator();

104
spring-jdbc/src/test/java/org/springframework/jdbc/datasource/init/ScriptUtilsTests.java

@ -16,10 +16,8 @@
package org.springframework.jdbc.datasource.init; package org.springframework.jdbc.datasource.init;
import java.io.LineNumberReader;
import java.sql.Connection; import java.sql.Connection;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -27,8 +25,6 @@ import org.junit.After;
import org.junit.Test; import org.junit.Test;
import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.support.EncodedResource; import org.springframework.core.io.support.EncodedResource;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.UncategorizedSQLException;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
@ -37,20 +33,19 @@ import org.springframework.transaction.support.TransactionSynchronizationManager
import static org.hamcrest.Matchers.*; import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*; import static org.junit.Assert.*;
/** /**
* JUnit test cases for ScriptUtils. * Unit and integration tests for {@link ScriptUtils}.
* *
* @author Thomas Risberg
* @author Sam Brannen
* @author Phillip Webb
* @author Chris Baldwin * @author Chris Baldwin
*/ */
public class ScriptUtilsTests { public class ScriptUtilsTests {
private final EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder(); private final EmbeddedDatabase db = new EmbeddedDatabaseBuilder().build();
private final EmbeddedDatabase db = builder.build();
private final JdbcTemplate jdbcTemplate = new JdbcTemplate(db);
@After @After
public void shutDown() { public void shutDown() {
if (TransactionSynchronizationManager.isSynchronizationActive()) { if (TransactionSynchronizationManager.isSynchronizationActive()) {
@ -95,11 +90,9 @@ public class ScriptUtilsTests {
@Test @Test
public void readAndSplitScriptContainingComments() throws Exception { public void readAndSplitScriptContainingComments() throws Exception {
EncodedResource resource = new EncodedResource(new ClassPathResource("test-data-with-comments.sql", getClass())); EncodedResource resource = new EncodedResource(new ClassPathResource("test-data-with-comments.sql", getClass()));
LineNumberReader lineNumberReader = new LineNumberReader(resource.getReader());
String script = ScriptUtils.readScript(lineNumberReader); String script = ScriptUtils.readScript(resource);
char delim = ';'; char delim = ';';
List<String> statements = new ArrayList<String>(); List<String> statements = new ArrayList<String>();
@ -123,12 +116,10 @@ public class ScriptUtilsTests {
*/ */
@Test @Test
public void readAndSplitScriptContainingCommentsWithLeadingTabs() throws Exception { public void readAndSplitScriptContainingCommentsWithLeadingTabs() throws Exception {
EncodedResource resource = new EncodedResource(new ClassPathResource( EncodedResource resource = new EncodedResource(new ClassPathResource(
"test-data-with-comments-and-leading-tabs.sql", getClass())); "test-data-with-comments-and-leading-tabs.sql", getClass()));
LineNumberReader lineNumberReader = new LineNumberReader(resource.getReader());
String script = ScriptUtils.readScript(lineNumberReader); String script = ScriptUtils.readScript(resource);
char delim = ';'; char delim = ';';
List<String> statements = new ArrayList<String>(); List<String> statements = new ArrayList<String>();
@ -143,24 +134,24 @@ public class ScriptUtilsTests {
assertEquals("statement 2 not split correctly", statement2, statements.get(1)); assertEquals("statement 2 not split correctly", statement2, statements.get(1));
assertEquals("statement 3 not split correctly", statement3, statements.get(2)); assertEquals("statement 3 not split correctly", statement3, statements.get(2));
} }
/** /**
* See <a href="https://jira.springsource.org/browse/SPR-9531">SPR-9531</a> * See <a href="https://jira.springsource.org/browse/SPR-9531">SPR-9531</a>
*/ */
@Test @Test
public void readAndSplitScriptContainingMuliLineComments() throws Exception { public void readAndSplitScriptContainingMuliLineComments() throws Exception {
EncodedResource resource = new EncodedResource(new ClassPathResource( EncodedResource resource = new EncodedResource(new ClassPathResource("test-data-with-multi-line-comments.sql",
"test-data-with-multi-line-comments.sql", getClass())); getClass()));
String script = ScriptUtils.readScript(resource); String script = ScriptUtils.readScript(resource);
char delim = ';'; char delim = ';';
List<String> statements = new ArrayList<String>(); List<String> statements = new ArrayList<String>();
ScriptUtils.splitSqlScript(script, delim, statements); ScriptUtils.splitSqlScript(script, delim, statements);
String statement1 = "INSERT INTO users(first_name, last_name) VALUES('Juergen', 'Hoeller')"; String statement1 = "INSERT INTO users(first_name, last_name) VALUES('Juergen', 'Hoeller')";
String statement2 = "INSERT INTO users(first_name, last_name) VALUES( 'Sam' , 'Brannen' )"; String statement2 = "INSERT INTO users(first_name, last_name) VALUES( 'Sam' , 'Brannen' )";
assertEquals("wrong number of statements", 2, statements.size()); assertEquals("wrong number of statements", 2, statements.size());
assertEquals("statement 1 not split correctly", statement1, statements.get(0)); assertEquals("statement 1 not split correctly", statement1, statements.get(0));
assertEquals("statement 2 not split correctly", statement2, statements.get(1)); assertEquals("statement 2 not split correctly", statement2, statements.get(1));
@ -168,71 +159,38 @@ public class ScriptUtilsTests {
@Test @Test
public void containsDelimiters() { public void containsDelimiters() {
assertTrue("test with ';' is wrong", !ScriptUtils.containsSqlScriptDelimiters("select 1\n select ';'", ';')); assertTrue("test with ';' is wrong", !ScriptUtils.containsSqlScriptDelimiters("select 1\n select ';'", ";"));
assertTrue("test with delimiter ; is wrong", assertTrue("test with delimiter ; is wrong", ScriptUtils.containsSqlScriptDelimiters("select 1; select 2", ";"));
ScriptUtils.containsSqlScriptDelimiters("select 1; select 2", ';'));
assertTrue("test with '\\n' is wrong", assertTrue("test with '\\n' is wrong",
!ScriptUtils.containsSqlScriptDelimiters("select 1; select '\\n\n';", '\n')); !ScriptUtils.containsSqlScriptDelimiters("select 1; select '\\n\n';", "\n"));
assertTrue("test with delimiter \\n is wrong", assertTrue("test with delimiter \\n is wrong",
ScriptUtils.containsSqlScriptDelimiters("select 1\n select 2", '\n')); ScriptUtils.containsSqlScriptDelimiters("select 1\n select 2", "\n"));
} }
@Test @Test
public void executeSqlScript() throws SQLException { public void executeSqlScript() throws SQLException {
EncodedResource schemaResource = new EncodedResource(new ClassPathResource( EncodedResource schemaResource = new EncodedResource(new ClassPathResource("users-schema.sql", getClass()));
"users-schema.sql", getClass()));
EncodedResource commentResource = new EncodedResource(new ClassPathResource( EncodedResource commentResource = new EncodedResource(new ClassPathResource(
"test-data-with-multi-line-comments.sql", getClass())); "test-data-with-multi-line-comments.sql", getClass()));
Connection connection = db.getConnection(); Connection connection = db.getConnection();
Statement stmt = connection.createStatement();
TestScriptStatementExecutor executor = new TestScriptStatementExecutor(stmt); ScriptUtils.executeSqlScript(connection, schemaResource, false, false, ScriptUtils.DEFAULT_COMMENT_PREFIX,
ScriptUtils.DEFAULT_STATEMENT_SEPARATOR, ScriptUtils.DEFAULT_BLOCK_COMMENT_START_DELIMITER,
try { ScriptUtils.DEFAULT_BLOCK_COMMENT_END_DELIMITER);
ScriptUtils.executeSqlScript(executor, schemaResource, false, false, ScriptUtils.DEFAULT_COMMENT_PREFIX, ScriptUtils.executeSqlScript(connection, commentResource, false, false, ScriptUtils.DEFAULT_COMMENT_PREFIX,
ScriptUtils.DEFAULT_STATEMENT_SEPARATOR, ScriptUtils.DEFAULT_BLOCK_COMMENT_START_DELIMITER, ScriptUtils.DEFAULT_STATEMENT_SEPARATOR, ScriptUtils.DEFAULT_BLOCK_COMMENT_START_DELIMITER,
ScriptUtils.DEFAULT_BLOCK_COMMENT_END_DELIMITER); ScriptUtils.DEFAULT_BLOCK_COMMENT_END_DELIMITER);
ScriptUtils.executeSqlScript(executor, commentResource, false, false, ScriptUtils.DEFAULT_COMMENT_PREFIX,
ScriptUtils.DEFAULT_STATEMENT_SEPARATOR, ScriptUtils.DEFAULT_BLOCK_COMMENT_START_DELIMITER,
ScriptUtils.DEFAULT_BLOCK_COMMENT_END_DELIMITER);
}
finally {
stmt.close();
connection.close();
}
assertUsersDatabaseCreated("Hoeller", "Brannen"); assertUsersDatabaseCreated("Hoeller", "Brannen");
} }
private void assertUsersDatabaseCreated(String... lastNames) { private void assertUsersDatabaseCreated(String... lastNames) {
final JdbcTemplate jdbcTemplate = new JdbcTemplate(db);
for (String lastName : lastNames) { for (String lastName : lastNames) {
assertThat("Did not find user with last name [" + lastName + "].", assertThat("Did not find user with last name [" + lastName + "].",
jdbcTemplate.queryForObject("select count(0) from users where last_name = ?", Integer.class, lastName), jdbcTemplate.queryForObject("select count(0) from users where last_name = ?", Integer.class, lastName),
equalTo(1)); equalTo(1));
} }
} }
private class TestScriptStatementExecutor implements ScriptUtils.ScriptStatementExecutor
{
Statement stmt;
public TestScriptStatementExecutor(Statement stmt) {
super();
this.stmt = stmt;
}
/* (non-Javadoc)
* @see org.springframework.jdbc.datasource.init.ScriptUtils.ScriptStatementExecutor#executeScriptStatement(java.lang.String)
*/
@Override
public int executeScriptStatement(String statement) throws DataAccessException {
try {
stmt.execute(statement);
return stmt.getUpdateCount();
}
catch (SQLException e) {
throw new UncategorizedSQLException(getClass().getName(), statement, e);
}
}
}
} }

23
spring-test/src/main/java/org/springframework/test/context/junit4/AbstractTransactionalJUnit4SpringContextTests.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2013 the original author or authors. * Copyright 2002-2014 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -21,9 +21,10 @@ import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.core.io.support.EncodedResource;
import org.springframework.dao.DataAccessException; import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.init.DatabasePopulatorUtils;
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners; import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.transaction.TransactionalTestExecutionListener; import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
@ -171,22 +172,24 @@ public abstract class AbstractTransactionalJUnit4SpringContextTests extends Abst
/** /**
* Execute the given SQL script. * Execute the given SQL script.
* <p>Use with caution outside of a transaction! * <p>Use with caution outside of a transaction!
* <p>The script will normally be loaded by classpath. There should be one * <p>The script will normally be loaded by classpath.
* statement per line. Any semicolons will be removed. <b>Do not use this * <p><b>Do not use this method to execute DDL if you expect rollback.</b>
* method to execute DDL if you expect rollback.</b>
* @param sqlResourcePath the Spring resource path for the SQL script * @param sqlResourcePath the Spring resource path for the SQL script
* @param continueOnError whether or not to continue without throwing an * @param continueOnError whether or not to continue without throwing an
* exception in the event of an error * exception in the event of an error
* @throws DataAccessException if there is an error executing a statement * @throws DataAccessException if there is an error executing a statement
* and continueOnError was {@code false} * @see ResourceDatabasePopulator
* @see JdbcTestUtils#executeSqlScript(JdbcTemplate, EncodedResource, boolean) * @see DatabasePopulatorUtils
* @see #setSqlScriptEncoding * @see #setSqlScriptEncoding
*/ */
@SuppressWarnings("deprecation")
protected void executeSqlScript(String sqlResourcePath, boolean continueOnError) throws DataAccessException { protected void executeSqlScript(String sqlResourcePath, boolean continueOnError) throws DataAccessException {
Resource resource = this.applicationContext.getResource(sqlResourcePath); Resource resource = this.applicationContext.getResource(sqlResourcePath);
JdbcTestUtils.executeSqlScript(this.jdbcTemplate, new EncodedResource(resource, this.sqlScriptEncoding), ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator();
continueOnError); databasePopulator.setContinueOnError(continueOnError);
databasePopulator.addScript(resource);
databasePopulator.setSqlScriptEncoding(this.sqlScriptEncoding);
DatabasePopulatorUtils.execute(databasePopulator, jdbcTemplate.getDataSource());
} }
} }

23
spring-test/src/main/java/org/springframework/test/context/testng/AbstractTransactionalTestNGSpringContextTests.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2013 the original author or authors. * Copyright 2002-2014 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -21,9 +21,10 @@ import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.core.io.support.EncodedResource;
import org.springframework.dao.DataAccessException; import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.init.DatabasePopulatorUtils;
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
import org.springframework.test.context.TestExecutionListeners; import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.transaction.TransactionalTestExecutionListener; import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
import org.springframework.test.jdbc.JdbcTestUtils; import org.springframework.test.jdbc.JdbcTestUtils;
@ -162,22 +163,24 @@ public abstract class AbstractTransactionalTestNGSpringContextTests extends Abst
/** /**
* Execute the given SQL script. * Execute the given SQL script.
* <p>Use with caution outside of a transaction! * <p>Use with caution outside of a transaction!
* <p>The script will normally be loaded by classpath. There should be one * <p>The script will normally be loaded by classpath.
* statement per line. Any semicolons will be removed. <b>Do not use this * <p><b>Do not use this method to execute DDL if you expect rollback.</b>
* method to execute DDL if you expect rollback.</b>
* @param sqlResourcePath the Spring resource path for the SQL script * @param sqlResourcePath the Spring resource path for the SQL script
* @param continueOnError whether or not to continue without throwing an * @param continueOnError whether or not to continue without throwing an
* exception in the event of an error * exception in the event of an error
* @throws DataAccessException if there is an error executing a statement * @throws DataAccessException if there is an error executing a statement
* and continueOnError was {@code false} * @see ResourceDatabasePopulator
* @see JdbcTestUtils#executeSqlScript(JdbcTemplate, EncodedResource, boolean) * @see DatabasePopulatorUtils
* @see #setSqlScriptEncoding * @see #setSqlScriptEncoding
*/ */
@SuppressWarnings("deprecation")
protected void executeSqlScript(String sqlResourcePath, boolean continueOnError) throws DataAccessException { protected void executeSqlScript(String sqlResourcePath, boolean continueOnError) throws DataAccessException {
Resource resource = this.applicationContext.getResource(sqlResourcePath); Resource resource = this.applicationContext.getResource(sqlResourcePath);
JdbcTestUtils.executeSqlScript(this.jdbcTemplate, new EncodedResource(resource, this.sqlScriptEncoding), ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator();
continueOnError); databasePopulator.setContinueOnError(continueOnError);
databasePopulator.addScript(resource);
databasePopulator.setSqlScriptEncoding(this.sqlScriptEncoding);
DatabasePopulatorUtils.execute(databasePopulator, jdbcTemplate.getDataSource());
} }
} }

77
spring-test/src/main/java/org/springframework/test/jdbc/JdbcTestUtils.java

@ -22,15 +22,16 @@ import java.util.List;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader; import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.EncodedResource; import org.springframework.core.io.support.EncodedResource;
import org.springframework.dao.DataAccessException; import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.SqlParameterValue; import org.springframework.jdbc.core.SqlParameterValue;
import org.springframework.jdbc.datasource.init.DatabasePopulatorUtils;
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator; import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
import org.springframework.jdbc.datasource.init.ScriptUtils; import org.springframework.jdbc.datasource.init.ScriptUtils;
import org.springframework.jdbc.datasource.init.ScriptUtils.ScriptStatementExecutor;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
/** /**
@ -48,6 +49,7 @@ public class JdbcTestUtils {
private static final Log logger = LogFactory.getLog(JdbcTestUtils.class); private static final Log logger = LogFactory.getLog(JdbcTestUtils.class);
/** /**
* Count the rows in the given table. * Count the rows in the given table.
* @param jdbcTemplate the JdbcTemplate with which to perform JDBC operations * @param jdbcTemplate the JdbcTemplate with which to perform JDBC operations
@ -117,14 +119,13 @@ public class JdbcTestUtils {
* optionally the scale. * optionally the scale.
* @return the number of rows deleted from the table * @return the number of rows deleted from the table
*/ */
public static int deleteFromTableWhere(JdbcTemplate jdbcTemplate, String tableName, public static int deleteFromTableWhere(JdbcTemplate jdbcTemplate, String tableName, String whereClause,
String whereClause, Object... args) { Object... args) {
String sql = "DELETE FROM " + tableName; String sql = "DELETE FROM " + tableName;
if (StringUtils.hasText(whereClause)) { if (StringUtils.hasText(whereClause)) {
sql += " WHERE " + whereClause; sql += " WHERE " + whereClause;
} }
int rowCount = (args != null && args.length > 0 ? jdbcTemplate.update(sql, args) int rowCount = (args != null && args.length > 0 ? jdbcTemplate.update(sql, args) : jdbcTemplate.update(sql));
: jdbcTemplate.update(sql));
if (logger.isInfoEnabled()) { if (logger.isInfoEnabled()) {
logger.info("Deleted " + rowCount + " rows from table " + tableName); logger.info("Deleted " + rowCount + " rows from table " + tableName);
} }
@ -158,9 +159,11 @@ public class JdbcTestUtils {
* @throws DataAccessException if there is an error executing a statement * @throws DataAccessException if there is an error executing a statement
* and {@code continueOnError} is {@code false} * and {@code continueOnError} is {@code false}
* @see ResourceDatabasePopulator * @see ResourceDatabasePopulator
* @see DatabasePopulatorUtils
* @see #executeSqlScript(JdbcTemplate, Resource, boolean) * @see #executeSqlScript(JdbcTemplate, Resource, boolean)
* @deprecated as of Spring 4.0.3, in favor of using * @deprecated as of Spring 4.0.3, in favor of using
* {@link org.springframework.jdbc.datasource.init.ScriptUtils#executeSqlScript(ScriptStatementExecutor, EncodedResource, boolean, boolean, String, String, String, String)} * {@link org.springframework.jdbc.datasource.init.ScriptUtils#executeSqlScript}
* or {@link org.springframework.jdbc.datasource.init.ResourceDatabasePopulator}.
*/ */
@Deprecated @Deprecated
public static void executeSqlScript(JdbcTemplate jdbcTemplate, ResourceLoader resourceLoader, public static void executeSqlScript(JdbcTemplate jdbcTemplate, ResourceLoader resourceLoader,
@ -184,9 +187,11 @@ public class JdbcTestUtils {
* @throws DataAccessException if there is an error executing a statement * @throws DataAccessException if there is an error executing a statement
* and {@code continueOnError} is {@code false} * and {@code continueOnError} is {@code false}
* @see ResourceDatabasePopulator * @see ResourceDatabasePopulator
* @see DatabasePopulatorUtils
* @see #executeSqlScript(JdbcTemplate, EncodedResource, boolean) * @see #executeSqlScript(JdbcTemplate, EncodedResource, boolean)
* @deprecated as of Spring 4.0.3, in favor of using * @deprecated as of Spring 4.0.3, in favor of using
* {@link org.springframework.jdbc.datasource.init.ScriptUtils#executeSqlScript(ScriptStatementExecutor, EncodedResource, boolean, boolean, String, String, String, String)} * {@link org.springframework.jdbc.datasource.init.ScriptUtils#executeSqlScript}
* or {@link org.springframework.jdbc.datasource.init.ResourceDatabasePopulator}.
*/ */
@Deprecated @Deprecated
public static void executeSqlScript(JdbcTemplate jdbcTemplate, Resource resource, boolean continueOnError) public static void executeSqlScript(JdbcTemplate jdbcTemplate, Resource resource, boolean continueOnError)
@ -207,36 +212,22 @@ public class JdbcTestUtils {
* @throws DataAccessException if there is an error executing a statement * @throws DataAccessException if there is an error executing a statement
* and {@code continueOnError} is {@code false} * and {@code continueOnError} is {@code false}
* @see ResourceDatabasePopulator * @see ResourceDatabasePopulator
* @deprecated as of Spring 4.0.3, in favor of using * @see DatabasePopulatorUtils
* {@link org.springframework.jdbc.datasource.init.ScriptUtils#executeSqlScript(ScriptStatementExecutor, EncodedResource, boolean, boolean, String, String, String, String)} * @deprecated as of Spring 4.0.3, in favor of using
* {@link org.springframework.jdbc.datasource.init.ScriptUtils#executeSqlScript}
* or {@link org.springframework.jdbc.datasource.init.ResourceDatabasePopulator}.
*/ */
@Deprecated @Deprecated
public static void executeSqlScript(JdbcTemplate jdbcTemplate, EncodedResource resource, boolean continueOnError) public static void executeSqlScript(JdbcTemplate jdbcTemplate, EncodedResource resource, boolean continueOnError)
throws DataAccessException { throws DataAccessException {
ScriptUtils.executeSqlScript(new JdbcTemplateScriptStatementExecutor(jdbcTemplate), resource, ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator();
continueOnError, continueOnError, ScriptUtils.DEFAULT_COMMENT_PREFIX, databasePopulator.setContinueOnError(continueOnError);
ScriptUtils.DEFAULT_STATEMENT_SEPARATOR, ScriptUtils.DEFAULT_BLOCK_COMMENT_START_DELIMITER, databasePopulator.addScript(resource.getResource());
ScriptUtils.DEFAULT_BLOCK_COMMENT_END_DELIMITER); databasePopulator.setSqlScriptEncoding(resource.getEncoding());
}
private static class JdbcTemplateScriptStatementExecutor implements ScriptStatementExecutor {
private JdbcTemplate jdbcTemplate;
public JdbcTemplateScriptStatementExecutor(JdbcTemplate jdbcTemplate) {
super();
this.jdbcTemplate = jdbcTemplate;
}
/* (non-Javadoc) DatabasePopulatorUtils.execute(databasePopulator, jdbcTemplate.getDataSource());
* @see org.springframework.jdbc.support.JdbcUtils.ScriptStatementExecutor#executeScriptStatement(java.lang.String)
*/
@Override
public int executeScriptStatement(String statement) throws DataAccessException {
return jdbcTemplate.update(statement);
}
} }
/** /**
* Read a script from the provided {@code LineNumberReader}, using * Read a script from the provided {@code LineNumberReader}, using
* "{@code --}" as the comment prefix, and build a {@code String} containing * "{@code --}" as the comment prefix, and build a {@code String} containing
@ -245,14 +236,14 @@ public class JdbcTestUtils {
* to be processed * to be processed
* @return a {@code String} containing the script lines * @return a {@code String} containing the script lines
* @see #readScript(LineNumberReader, String) * @see #readScript(LineNumberReader, String)
* @deprecated as of Spring 4.0.3, in favor of using * @deprecated as of Spring 4.0.3, in favor of using
* {@link org.springframework.jdbc.datasource.init.ScriptUtils#readScript(LineNumberReader)} * {@link org.springframework.jdbc.datasource.init.ScriptUtils#readScript(LineNumberReader, String, String)}
*/ */
@Deprecated @Deprecated
public static String readScript(LineNumberReader lineNumberReader) throws IOException { public static String readScript(LineNumberReader lineNumberReader) throws IOException {
return ScriptUtils.readScript(lineNumberReader); return readScript(lineNumberReader, ScriptUtils.DEFAULT_COMMENT_PREFIX);
} }
/** /**
* Read a script from the provided {@code LineNumberReader}, using the supplied * Read a script from the provided {@code LineNumberReader}, using the supplied
* comment prefix, and build a {@code String} containing the lines. * comment prefix, and build a {@code String} containing the lines.
@ -263,7 +254,7 @@ public class JdbcTestUtils {
* to be processed * to be processed
* @param commentPrefix the prefix that identifies comments in the SQL script &mdash; typically "--" * @param commentPrefix the prefix that identifies comments in the SQL script &mdash; typically "--"
* @return a {@code String} containing the script lines * @return a {@code String} containing the script lines
* @deprecated as of Spring 4.0.3, in favor of using * @deprecated as of Spring 4.0.3, in favor of using
* {@link org.springframework.jdbc.datasource.init.ScriptUtils#readScript(LineNumberReader, String, String)} * {@link org.springframework.jdbc.datasource.init.ScriptUtils#readScript(LineNumberReader, String, String)}
*/ */
@Deprecated @Deprecated
@ -276,14 +267,14 @@ public class JdbcTestUtils {
* @param script the SQL script * @param script the SQL script
* @param delim character delimiting each statement &mdash; typically a ';' character * @param delim character delimiting each statement &mdash; typically a ';' character
* @return {@code true} if the script contains the delimiter; {@code false} otherwise * @return {@code true} if the script contains the delimiter; {@code false} otherwise
* @deprecated as of Spring 4.0.3, in favor of using * @deprecated as of Spring 4.0.3, in favor of using
* {@link org.springframework.jdbc.datasource.init.ScriptUtils#containsSqlScriptDelimiters(String, char)} * {@link org.springframework.jdbc.datasource.init.ScriptUtils#containsSqlScriptDelimiters}
*/ */
@Deprecated @Deprecated
public static boolean containsSqlScriptDelimiters(String script, char delim) { public static boolean containsSqlScriptDelimiters(String script, char delim) {
return ScriptUtils.containsSqlScriptDelimiters(script, delim); return ScriptUtils.containsSqlScriptDelimiters(script, String.valueOf(delim));
} }
/** /**
* Split an SQL script into separate statements delimited by the provided * Split an SQL script into separate statements delimited by the provided
* delimiter character. Each individual statement will be added to the * delimiter character. Each individual statement will be added to the
@ -295,7 +286,7 @@ public class JdbcTestUtils {
* @param script the SQL script * @param script the SQL script
* @param delim character delimiting each statement &mdash; typically a ';' character * @param delim character delimiting each statement &mdash; typically a ';' character
* @param statements the list that will contain the individual statements * @param statements the list that will contain the individual statements
* @deprecated as of Spring 4.0.3, in favor of using * @deprecated as of Spring 4.0.3, in favor of using
* {@link org.springframework.jdbc.datasource.init.ScriptUtils#splitSqlScript(String, char, List)} * {@link org.springframework.jdbc.datasource.init.ScriptUtils#splitSqlScript(String, char, List)}
*/ */
@Deprecated @Deprecated

63
spring-test/src/test/java/org/springframework/test/jdbc/JdbcTestUtilsIntegrationTests.java

@ -0,0 +1,63 @@
/*
* Copyright 2002-2014 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.test.jdbc;
import java.util.Arrays;
import org.junit.After;
import org.junit.Test;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.EncodedResource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import static org.junit.Assert.*;
/**
* Integration tests for {@link JdbcTestUtils}.
*
* @author Sam Brannen
* @since 4.0.3
* @see JdbcTestUtilsTests
*/
public class JdbcTestUtilsIntegrationTests {
private final EmbeddedDatabase db = new EmbeddedDatabaseBuilder().build();
private JdbcTemplate jdbcTemplate = new JdbcTemplate(db);
@After
public void shutdown() {
db.shutdown();
}
@Test
@SuppressWarnings("deprecation")
public void executeSqlScriptsAndcountRowsInTableWhere() throws Exception {
for (String script : Arrays.asList("schema.sql", "data.sql")) {
Resource resource = new ClassPathResource(script, getClass());
JdbcTestUtils.executeSqlScript(this.jdbcTemplate, new EncodedResource(resource), false);
}
assertEquals(1, JdbcTestUtils.countRowsInTableWhere(jdbcTemplate, "person", "name = 'bob'"));
}
}

13
spring-test/src/test/java/org/springframework/test/jdbc/JdbcTestUtilsTests.java

@ -16,24 +16,22 @@
package org.springframework.test.jdbc; package org.springframework.test.jdbc;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat;
import static org.mockito.BDDMockito.given;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner; import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.JdbcTemplate;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.mockito.BDDMockito.*;
/** /**
* Unit tests for {@link JdbcTestUtils}. * Unit tests for {@link JdbcTestUtils}.
* *
* @author Thomas Risberg
* @author Sam Brannen
* @author Phillip Webb * @author Phillip Webb
* @author Chris Baldwin
* @since 2.5.4 * @since 2.5.4
* @see JdbcTestUtilsIntegrationTests
*/ */
@RunWith(MockitoJUnitRunner.class) @RunWith(MockitoJUnitRunner.class)
public class JdbcTestUtilsTests { public class JdbcTestUtilsTests {
@ -41,6 +39,7 @@ public class JdbcTestUtilsTests {
@Mock @Mock
private JdbcTemplate jdbcTemplate; private JdbcTemplate jdbcTemplate;
@Test @Test
public void deleteWithoutWhereClause() throws Exception { public void deleteWithoutWhereClause() throws Exception {
given(jdbcTemplate.update("DELETE FROM person")).willReturn(10); given(jdbcTemplate.update("DELETE FROM person")).willReturn(10);

1
spring-test/src/test/resources/org/springframework/test/jdbc/data.sql

@ -0,0 +1 @@
INSERT INTO person VALUES('bob');

4
spring-test/src/test/resources/org/springframework/test/jdbc/schema.sql

@ -0,0 +1,4 @@
CREATE TABLE person (
name VARCHAR(20) NOT NULL,
PRIMARY KEY(name)
);
Loading…
Cancel
Save