From 92eb99a5abaaf43f4fddeaf00020dad2f3dd73eb Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Mon, 17 Mar 2014 16:05:09 +0100 Subject: [PATCH] Favor ScriptException over SQLException In ScriptUtils and related classes, SQLExceptions are now caught and wrapped in ScriptExceptions wherever feasible. Affected "throws" declarations have also been revised as appropriate. Issue: SPR-11564 --- .../init/CompositeDatabasePopulator.java | 4 +- .../datasource/init/DatabasePopulator.java | 7 +- .../init/ResourceDatabasePopulator.java | 3 +- .../jdbc/datasource/init/ScriptUtils.java | 124 ++++++++++-------- 4 files changed, 81 insertions(+), 57 deletions(-) diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/CompositeDatabasePopulator.java b/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/CompositeDatabasePopulator.java index 8b570c2b101..16c7b8e0a8e 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/CompositeDatabasePopulator.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/CompositeDatabasePopulator.java @@ -13,10 +13,12 @@ * 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 java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -44,7 +46,7 @@ public class CompositeDatabasePopulator implements DatabasePopulator { } /** - * Add a populator to the list of delegates. + * Add one or more populators to the list of delegates. */ public void addPopulators(DatabasePopulator... populators) { this.populators.addAll(Arrays.asList(populators)); diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/DatabasePopulator.java b/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/DatabasePopulator.java index 1e30251a429..d7e6d550f66 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/DatabasePopulator.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/DatabasePopulator.java @@ -31,7 +31,12 @@ import java.sql.SQLException; public interface DatabasePopulator { /** - * Populate the database using the JDBC connection provided. + * Populate the database using the provided JDBC connection. + *

Concrete implementations may throw an {@link SQLException} if + * an error is encountered but are strongly encouraged to throw a + * specific {@link ScriptException} instead. For example, Spring's + * {@link ResourceDatabasePopulator} and {@link DatabasePopulatorUtils} wrap + * all {@code SQLExceptions} in {@code ScriptExceptions}. * @param connection the JDBC connection to use to populate the db; already * configured and ready to use * @throws SQLException if an unrecoverable data access exception occurs diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/ResourceDatabasePopulator.java b/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/ResourceDatabasePopulator.java index 8f4189a421b..00f46992abd 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/ResourceDatabasePopulator.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/ResourceDatabasePopulator.java @@ -17,7 +17,6 @@ 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; @@ -183,7 +182,7 @@ public class ResourceDatabasePopulator implements DatabasePopulator { * {@inheritDoc} */ @Override - public void populate(Connection connection) throws SQLException, ScriptException { + public void populate(Connection connection) throws ScriptException { for (Resource script : this.scripts) { ScriptUtils.executeSqlScript(connection, encodeScript(script), this.continueOnError, this.ignoreFailedDrops, this.commentPrefix, this.separator, this.blockCommentStartDelimiter, diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/ScriptUtils.java b/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/ScriptUtils.java index 68918f51508..364bfe71363 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/ScriptUtils.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/ScriptUtils.java @@ -99,6 +99,7 @@ public abstract class ScriptUtils { * @param script the SQL script * @param separator character separating each statement — typically a ';' * @param statements the list that will contain the individual statements + * @throws ScriptException if an error occurred while splitting the SQL script * @see #splitSqlScript(String, String, List) * @see #splitSqlScript(EncodedResource, String, String, String, String, String, List) */ @@ -121,6 +122,7 @@ public abstract class ScriptUtils { * @param script the SQL script * @param separator text separating each statement — typically a ';' or newline character * @param statements the list that will contain the individual statements + * @throws ScriptException if an error occurred while splitting the SQL script * @see #splitSqlScript(String, char, List) * @see #splitSqlScript(EncodedResource, String, String, String, String, String, List) */ @@ -151,6 +153,7 @@ public abstract class ScriptUtils { * @param blockCommentEndDelimiter the end block comment delimiter; * never {@code null} or empty * @param statements the list that will contain the individual statements + * @throws ScriptException if an error occurred while splitting the SQL script */ public static void splitSqlScript(EncodedResource resource, String script, String separator, String commentPrefix, String blockCommentStartDelimiter, String blockCommentEndDelimiter, List statements) @@ -257,6 +260,7 @@ public abstract class ScriptUtils { * typically "--" * @param separator the statement separator in the SQL script — typically ";" * @return a {@code String} containing the script lines + * @throws IOException in case of I/O errors */ private static String readScript(EncodedResource resource, String commentPrefix, String separator) throws IOException { @@ -282,6 +286,7 @@ public abstract class ScriptUtils { * typically "--" * @param separator the statement separator in the SQL script — typically ";" * @return a {@code String} containing the script lines + * @throws IOException in case of I/O errors */ public static String readScript(LineNumberReader lineNumberReader, String commentPrefix, String separator) throws IOException { @@ -344,13 +349,14 @@ public abstract class ScriptUtils { * configured and ready to use * @param resource the resource to load the SQL script from; encoded with the * current platform's default encoding + * @throws ScriptException if an error occurred while executing the SQL script * @see #executeSqlScript(Connection, EncodedResource, boolean, boolean, String, String, String, String) * @see #DEFAULT_COMMENT_PREFIX * @see #DEFAULT_STATEMENT_SEPARATOR * @see #DEFAULT_BLOCK_COMMENT_START_DELIMITER * @see #DEFAULT_BLOCK_COMMENT_END_DELIMITER */ - public static void executeSqlScript(Connection connection, Resource resource) throws SQLException, ScriptException { + public static void executeSqlScript(Connection connection, Resource resource) throws ScriptException { executeSqlScript(connection, new EncodedResource(resource)); } @@ -364,14 +370,14 @@ public abstract class ScriptUtils { * configured and ready to use * @param resource the resource (potentially associated with a specific encoding) * to load the SQL script from + * @throws ScriptException if an error occurred while executing the SQL script * @see #executeSqlScript(Connection, EncodedResource, boolean, boolean, String, String, String, String) * @see #DEFAULT_COMMENT_PREFIX * @see #DEFAULT_STATEMENT_SEPARATOR * @see #DEFAULT_BLOCK_COMMENT_START_DELIMITER * @see #DEFAULT_BLOCK_COMMENT_END_DELIMITER */ - public static void executeSqlScript(Connection connection, EncodedResource resource) throws SQLException, - ScriptException { + public static void executeSqlScript(Connection connection, EncodedResource resource) throws ScriptException { executeSqlScript(connection, resource, false, false, DEFAULT_COMMENT_PREFIX, DEFAULT_STATEMENT_SEPARATOR, DEFAULT_BLOCK_COMMENT_START_DELIMITER, DEFAULT_BLOCK_COMMENT_END_DELIMITER); } @@ -398,71 +404,83 @@ public abstract class ScriptUtils { * {@code null} or empty * @param blockCommentEndDelimiter the end block comment delimiter; never * {@code null} or empty + * @throws ScriptException if an error occurred while executing the SQL script */ public static void executeSqlScript(Connection connection, EncodedResource resource, boolean continueOnError, boolean ignoreFailedDrops, String commentPrefix, String separator, String blockCommentStartDelimiter, - String blockCommentEndDelimiter) throws SQLException, ScriptException { + String blockCommentEndDelimiter) throws ScriptException { - if (logger.isInfoEnabled()) { - logger.info("Executing SQL script from " + resource); - } - long startTime = System.currentTimeMillis(); - List statements = new LinkedList(); - String script; try { - script = readScript(resource, commentPrefix, separator); - } - catch (IOException ex) { - throw new CannotReadScriptException(resource, ex); - } + if (logger.isInfoEnabled()) { + logger.info("Executing SQL script from " + resource); + } - if (separator == null) { - separator = DEFAULT_STATEMENT_SEPARATOR; - } - if (!containsSqlScriptDelimiters(script, separator)) { - separator = FALLBACK_STATEMENT_SEPARATOR; - } + long startTime = System.currentTimeMillis(); + List statements = new LinkedList(); + String script; + try { + script = readScript(resource, commentPrefix, separator); + } + catch (IOException ex) { + throw new CannotReadScriptException(resource, ex); + } - splitSqlScript(resource, script, separator, commentPrefix, blockCommentStartDelimiter, - blockCommentEndDelimiter, statements); - int lineNumber = 0; - Statement stmt = connection.createStatement(); - try { - for (String statement : statements) { - lineNumber++; - try { - stmt.execute(statement); - int rowsAffected = stmt.getUpdateCount(); - if (logger.isDebugEnabled()) { - logger.debug(rowsAffected + " returned as updateCount for SQL: " + statement); - } - } - catch (SQLException ex) { - boolean dropStatement = StringUtils.startsWithIgnoreCase(statement.trim(), "drop"); - if (continueOnError || (dropStatement && ignoreFailedDrops)) { + if (separator == null) { + separator = DEFAULT_STATEMENT_SEPARATOR; + } + if (!containsSqlScriptDelimiters(script, separator)) { + separator = FALLBACK_STATEMENT_SEPARATOR; + } + + splitSqlScript(resource, script, separator, commentPrefix, blockCommentStartDelimiter, + blockCommentEndDelimiter, statements); + int lineNumber = 0; + Statement stmt = connection.createStatement(); + try { + for (String statement : statements) { + lineNumber++; + try { + stmt.execute(statement); + int rowsAffected = stmt.getUpdateCount(); if (logger.isDebugEnabled()) { - logger.debug("Failed to execute SQL script statement at line " + lineNumber - + " of resource " + resource + ": " + statement, ex); + logger.debug(rowsAffected + " returned as updateCount for SQL: " + statement); } } - else { - throw new ScriptStatementFailedException(statement, lineNumber, resource, ex); + catch (SQLException 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(); + finally { + try { + stmt.close(); + } + catch (Throwable ex) { + logger.debug("Could not close JDBC Statement", ex); + } } - catch (Throwable ex) { - logger.debug("Could not close JDBC Statement", ex); - } - } - long elapsedTime = System.currentTimeMillis() - startTime; - if (logger.isInfoEnabled()) { - logger.info("Executed SQL script from " + resource + " in " + elapsedTime + " ms."); + long elapsedTime = System.currentTimeMillis() - startTime; + if (logger.isInfoEnabled()) { + logger.info("Executed SQL script from " + resource + " in " + elapsedTime + " ms."); + } + } + catch (Exception ex) { + if (ex instanceof ScriptException) { + throw (ScriptException) ex; + } + + throw new UncategorizedScriptException( + "Failed to execute database script from resource [" + resource + "]", ex); } }