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