11/*
2- * Copyright 2002-2009 the original author or authors.
2+ * Copyright 2002-2012 the original author or authors.
33 *
44 * Licensed under the Apache License, Version 2.0 (the "License");
55 * you may not use this file except in compliance with the License.
1818
1919import java .io .IOException ;
2020import java .io .LineNumberReader ;
21+ import java .util .LinkedList ;
2122import java .util .List ;
2223
24+ import org .apache .commons .logging .Log ;
25+ import org .apache .commons .logging .LogFactory ;
26+
27+ import org .springframework .core .io .Resource ;
28+ import org .springframework .core .io .ResourceLoader ;
29+ import org .springframework .core .io .support .EncodedResource ;
30+ import org .springframework .dao .DataAccessException ;
31+ import org .springframework .dao .DataAccessResourceFailureException ;
32+ import org .springframework .jdbc .core .JdbcTemplate ;
2333import org .springframework .util .StringUtils ;
2434
2535/**
26- * JdbcTestUtils is a collection of JDBC related utility methods for use in unit
27- * and integration testing scenarios.
28- *
36+ * {@code JdbcTestUtils} is a collection of JDBC related utility functions
37+ * intended to simplify standard database testing scenarios.
38+ *
39+ * <p>As of Spring 3.1.3, {@code JdbcTestUtils} supersedes {@link SimpleJdbcTestUtils}.
40+ *
2941 * @author Thomas Risberg
42+ * @author Sam Brannen
43+ * @author Juergen Hoeller
3044 * @since 2.5.4
3145 */
3246public class JdbcTestUtils {
3347
48+ private static final Log logger = LogFactory .getLog (JdbcTestUtils .class );
49+
50+
51+ /**
52+ * Count the rows in the given table.
53+ * @param jdbcTemplate the JdbcTemplate with which to perform JDBC operations
54+ * @param tableName name of the table to count rows in
55+ * @return the number of rows in the table
56+ */
57+ public static int countRowsInTable (JdbcTemplate jdbcTemplate , String tableName ) {
58+ return jdbcTemplate .queryForInt ("SELECT COUNT(0) FROM " + tableName );
59+ }
60+
61+ /**
62+ * Count the rows in the given table, using the provided {@code WHERE} clause.
63+ * <p>If the provided {@code WHERE} clause contains text, it will be prefixed
64+ * with {@code " WHERE "} and then appended to the generated {@code SELECT}
65+ * statement. For example, if the provided table name is {@code "person"} and
66+ * the provided where clause is {@code "name = 'Bob' and age > 25"}, the
67+ * resulting SQL statement to execute will be
68+ * {@code "SELECT COUNT(0) FROM person WHERE name = 'Bob' and age > 25"}.
69+ * @param jdbcTemplate the JdbcTemplate with which to perform JDBC operations
70+ * @param tableName the name of the table to count rows in
71+ * @param whereClause the {@code WHERE} clause to append to the query
72+ * @return the number of rows in the table that match the provided
73+ * {@code WHERE} clause
74+ */
75+ public static int countRowsInTableWhere (JdbcTemplate jdbcTemplate , String tableName , String whereClause ) {
76+ String sql = "SELECT COUNT(0) FROM " + tableName ;
77+ if (StringUtils .hasText (whereClause )) {
78+ sql += " WHERE " + whereClause ;
79+ }
80+ return jdbcTemplate .queryForInt (sql );
81+ }
82+
83+ /**
84+ * Delete all rows from the specified tables.
85+ * @param jdbcTemplate the JdbcTemplate with which to perform JDBC operations
86+ * @param tableNames the names of the tables to delete from
87+ * @return the total number of rows deleted from all specified tables
88+ */
89+ public static int deleteFromTables (JdbcTemplate jdbcTemplate , String ... tableNames ) {
90+ int totalRowCount = 0 ;
91+ for (String tableName : tableNames ) {
92+ int rowCount = jdbcTemplate .update ("DELETE FROM " + tableName );
93+ totalRowCount += rowCount ;
94+ if (logger .isInfoEnabled ()) {
95+ logger .info ("Deleted " + rowCount + " rows from table " + tableName );
96+ }
97+ }
98+ return totalRowCount ;
99+ }
100+
101+ /**
102+ * Drop the specified tables.
103+ * @param jdbcTemplate the JdbcTemplate with which to perform JDBC operations
104+ * @param tableNames the names of the tables to drop
105+ */
106+ public static void dropTables (JdbcTemplate jdbcTemplate , String ... tableNames ) {
107+ for (String tableName : tableNames ) {
108+ jdbcTemplate .execute ("DROP TABLE " + tableName );
109+ if (logger .isInfoEnabled ()) {
110+ logger .info ("Dropped table " + tableName );
111+ }
112+ }
113+ }
114+
115+ /**
116+ * Execute the given SQL script.
117+ * <p>The script will typically be loaded from the classpath. There should
118+ * be one statement per line. Any semicolons will be removed.
119+ * <p><b>Do not use this method to execute DDL if you expect rollback.</b>
120+ * @param jdbcTemplate the JdbcTemplate with which to perform JDBC operations
121+ * @param resourceLoader the resource loader with which to load the SQL script
122+ * @param sqlResourcePath the Spring resource path for the SQL script
123+ * @param continueOnError whether or not to continue without throwing an
124+ * exception in the event of an error
125+ * @throws DataAccessException if there is an error executing a statement
126+ * and {@code continueOnError} is {@code false}
127+ */
128+ public static void executeSqlScript (JdbcTemplate jdbcTemplate , ResourceLoader resourceLoader ,
129+ String sqlResourcePath , boolean continueOnError ) throws DataAccessException {
130+
131+ Resource resource = resourceLoader .getResource (sqlResourcePath );
132+ executeSqlScript (jdbcTemplate , resource , continueOnError );
133+ }
134+
135+ /**
136+ * Execute the given SQL script.
137+ * <p>The script will typically be loaded from the classpath. Statements
138+ * should be delimited with a semicolon. If statements are not delimited with
139+ * a semicolon then there should be one statement per line. Statements are
140+ * allowed to span lines only if they are delimited with a semicolon.
141+ * <p><b>Do not use this method to execute DDL if you expect rollback.</b>
142+ * @param jdbcTemplate the JdbcTemplate with which to perform JDBC operations
143+ * @param resource the resource to load the SQL script from
144+ * @param continueOnError whether or not to continue without throwing an
145+ * exception in the event of an error
146+ * @throws DataAccessException if there is an error executing a statement
147+ * and {@code continueOnError} is {@code false}
148+ */
149+ public static void executeSqlScript (JdbcTemplate jdbcTemplate , Resource resource , boolean continueOnError )
150+ throws DataAccessException {
151+
152+ executeSqlScript (jdbcTemplate , new EncodedResource (resource ), continueOnError );
153+ }
154+
155+ /**
156+ * Execute the given SQL script.
157+ * <p>The script will typically be loaded from the classpath. There should
158+ * be one statement per line. Any semicolons will be removed.
159+ * <p><b>Do not use this method to execute DDL if you expect rollback.</b>
160+ * @param jdbcTemplate the JdbcTemplate with which to perform JDBC operations
161+ * @param resource the resource (potentially associated with a specific encoding)
162+ * to load the SQL script from
163+ * @param continueOnError whether or not to continue without throwing an
164+ * exception in the event of an error
165+ * @throws DataAccessException if there is an error executing a statement
166+ * and {@code continueOnError} is {@code false}
167+ */
168+ public static void executeSqlScript (JdbcTemplate jdbcTemplate , EncodedResource resource , boolean continueOnError )
169+ throws DataAccessException {
170+
171+ if (logger .isInfoEnabled ()) {
172+ logger .info ("Executing SQL script from " + resource );
173+ }
174+ long startTime = System .currentTimeMillis ();
175+ List <String > statements = new LinkedList <String >();
176+ LineNumberReader reader = null ;
177+ try {
178+ reader = new LineNumberReader (resource .getReader ());
179+ String script = readScript (reader );
180+ char delimiter = ';' ;
181+ if (!containsSqlScriptDelimiters (script , delimiter )) {
182+ delimiter = '\n' ;
183+ }
184+ splitSqlScript (script , delimiter , statements );
185+ for (String statement : statements ) {
186+ try {
187+ int rowsAffected = jdbcTemplate .update (statement );
188+ if (logger .isDebugEnabled ()) {
189+ logger .debug (rowsAffected + " rows affected by SQL: " + statement );
190+ }
191+ }
192+ catch (DataAccessException ex ) {
193+ if (continueOnError ) {
194+ if (logger .isWarnEnabled ()) {
195+ logger .warn ("SQL statement [" + statement + "] failed" , ex );
196+ }
197+ }
198+ else {
199+ throw ex ;
200+ }
201+ }
202+ }
203+ long elapsedTime = System .currentTimeMillis () - startTime ;
204+ if (logger .isInfoEnabled ()) {
205+ logger .info (String .format ("Executed SQL script from %s in %s ms." , resource , elapsedTime ));
206+ }
207+ }
208+ catch (IOException ex ) {
209+ throw new DataAccessResourceFailureException ("Failed to open SQL script from " + resource , ex );
210+ }
211+ finally {
212+ try {
213+ if (reader != null ) {
214+ reader .close ();
215+ }
216+ } catch (IOException ex ) {
217+ // ignore
218+ }
219+ }
220+ }
221+
34222 /**
35- * Read a script from the LineNumberReader and build a String containing the
36- * lines.
37- *
38- * @param lineNumberReader the <code>LineNumberReader</code> containing the
39- * script to be processed
40- * @return <code>String</code> containing the script lines
41- * @throws IOException
223+ * Read a script from the provided {@code LineNumberReader} and build a
224+ * {@code String} containing the lines.
225+ * @param lineNumberReader the {@code LineNumberReader} containing the script
226+ * to be processed
227+ * @return a {@code String} containing the script lines
42228 */
43229 public static String readScript (LineNumberReader lineNumberReader ) throws IOException {
44230 String currentStatement = lineNumberReader .readLine ();
@@ -56,11 +242,10 @@ public static String readScript(LineNumberReader lineNumberReader) throws IOExce
56242 }
57243
58244 /**
59- * Does the provided SQL script contain the specified delimiter?
60- *
245+ * Determine if the provided SQL script contains the specified delimiter.
61246 * @param script the SQL script
62- * @param delim character delimiting each statement - typically a ';'
63- * character
247+ * @param delim character delimiting each statement — typically a ';' character
248+ * @return {@code true} if the script contains the delimiter; {@code false} otherwise
64249 */
65250 public static boolean containsSqlScriptDelimiters (String script , char delim ) {
66251 boolean inLiteral = false ;
@@ -80,11 +265,9 @@ public static boolean containsSqlScriptDelimiters(String script, char delim) {
80265 * Split an SQL script into separate statements delimited with the provided
81266 * delimiter character. Each individual statement will be added to the
82267 * provided <code>List</code>.
83- *
84268 * @param script the SQL script
85- * @param delim character delimiting each statement - typically a ';'
86- * character
87- * @param statements the List that will contain the individual statements
269+ * @param delim character delimiting each statement — typically a ';' character
270+ * @param statements the list that will contain the individual statements
88271 */
89272 public static void splitSqlScript (String script , char delim , List <String > statements ) {
90273 StringBuilder sb = new StringBuilder ();
0 commit comments