2 * Copyright 2005-2010 the original author or authors.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package org.wamblee.support.persistence;
18 import java.sql.Connection;
19 import java.sql.PreparedStatement;
20 import java.sql.ResultSet;
21 import java.sql.SQLException;
22 import java.util.ArrayList;
23 import java.util.List;
24 import java.util.logging.Level;
25 import java.util.logging.Logger;
27 import javax.sql.DataSource;
29 import junit.framework.TestCase;
31 import org.dbunit.DataSourceDatabaseTester;
32 import org.dbunit.IDatabaseTester;
33 import org.dbunit.database.DatabaseConnection;
34 import org.dbunit.database.DatabaseSequenceFilter;
35 import org.dbunit.database.IDatabaseConnection;
36 import org.dbunit.dataset.FilteredDataSet;
37 import org.dbunit.dataset.IDataSet;
38 import org.dbunit.dataset.filter.ITableFilter;
39 import org.dbunit.dataset.filter.ITableFilterSimple;
40 import org.dbunit.operation.DatabaseOperation;
43 * Database utilities is a simple support class for common tasks in working with
46 public class DatabaseUtils {
48 public static interface TableSet {
49 boolean contains(String aTableName);
52 public static interface JdbcUnitOfWork<T> {
53 T execute(Connection aConnection) throws Exception;
56 public static interface TableSetOperation {
57 void execute(String aTable) throws Exception;
60 private static final Logger LOG = Logger.getLogger(DatabaseUtils.class
66 private static final String SCHEMA_PATTERN = "%";
67 private DataSource dataSource;
68 private ITableFilterSimple tables;
70 private IDatabaseTester dbtester;
72 * List of connections that were created for dbtesters.
73 * This list will be closed in the {@link #stop()} method.
75 private List<IDatabaseConnection> connections;
78 * Constructs the database utils.
79 * Before use, {@link #start()} must be called.
80 * @param aDataSource Datasource.
81 * @param aTables Tables to work with.
83 public DatabaseUtils(DataSource aDataSource, ITableFilterSimple aTables) {
84 dataSource = aDataSource;
86 dbtester = new DataSourceDatabaseTester(dataSource);
87 connections = new ArrayList<IDatabaseConnection>();
91 * Starts the database utils.
94 // Empty. No operation currently.
98 * Stops the database utils, closing any JDBC connections that were created
99 * by this utility. Note that connections obtained from the datasource directly
100 * must still be closed by the user. The involved connections are only those that
101 * are created by this utility.
104 for (IDatabaseConnection connection: connections) {
107 } catch (SQLException e) {
108 LOG.log(Level.WARNING, "Could not close connection", e);
114 public IDatabaseTester createDbTester() throws Exception {
115 return createDbTester(getTableNames(tables));
118 public IDatabaseTester createDbTester(String[] aTables) throws Exception {
119 IDatabaseConnection connection = dbtester.getConnection();
120 connections.add(connection);
121 dbtester.setDataSet(connection.createDataSet(aTables));
125 public void cleanDatabase() throws Exception {
126 cleanDatabase(tables);
129 public void executeOnTables(ITableFilterSimple aTables,
130 final TableSetOperation aOperation) throws Exception {
131 final String[] tableNames = getTableNames(aTables);
132 executeInTransaction(new JdbcUnitOfWork<Void>() {
133 public Void execute(Connection aConnection) throws Exception {
134 for (int i = tableNames.length - 1; i >= 0; i--) {
135 aOperation.execute(tableNames[i]);
142 public void cleanDatabase(ITableFilterSimple aSelection) throws Exception {
144 final String[] tableNames = getTableNames(aSelection);
145 executeInTransaction(new JdbcUnitOfWork<Void>() {
147 public Void execute(Connection aConnection) throws Exception {
148 IDatabaseConnection connection = new DatabaseConnection(
150 ITableFilter filter = new DatabaseSequenceFilter(connection,
152 IDataSet dataset = new FilteredDataSet(filter, connection
153 .createDataSet(tableNames));
154 DatabaseOperation.DELETE_ALL.execute(connection, dataset);
161 public <T> T executeInTransaction(JdbcUnitOfWork<T> aCallback)
163 Connection connection = dataSource.getConnection();
164 connection.setAutoCommit(false);
166 T value = aCallback.execute(connection);
174 public String[] getTableNames() throws Exception {
175 return getTableNames(tables);
179 * @throws SQLException
181 public String[] getTableNames(ITableFilterSimple aSelection)
184 List<String> result = new ArrayList<String>();
185 LOG.fine("Getting database table names to clean (schema: '" +
186 SCHEMA_PATTERN + "'");
188 Connection connection = dataSource.getConnection();
190 ResultSet tableNames = connection.getMetaData().getTables(null,
191 SCHEMA_PATTERN, "%", new String[] { "TABLE" });
192 while (tableNames.next()) {
193 String table = tableNames.getString("TABLE_NAME");
194 if (aSelection.accept(table)) {
198 return (String[]) result.toArray(new String[0]);
204 public void emptyTables() throws Exception {
205 executeOnTables(tables, new TableSetOperation() {
206 public void execute(String aTable) throws Exception {
214 * @throws SQLException
216 public void emptyTables(final ITableFilterSimple aSelection)
218 executeOnTables(aSelection, new TableSetOperation() {
219 public void execute(String aTable) throws Exception {
227 * @throws SQLException
229 public void emptyTable(String aTable) throws Exception {
230 executeSql("delete from " + aTable);
233 public void dropTables() throws Exception {
237 public void dropTables(ITableFilterSimple aTables) throws Exception {
238 final String[] tableNames = getTableNames(aTables);
239 String[] sortedTables = executeInTransaction(new JdbcUnitOfWork<String[]>() {
241 public String[] execute(Connection aConnection) throws Exception {
242 IDatabaseConnection connection = new DatabaseConnection(
244 ITableFilter filter = new DatabaseSequenceFilter(connection,
246 IDataSet dataset = new FilteredDataSet(filter, connection
247 .createDataSet(tableNames));
248 return dataset.getTableNames();
251 for (int i = sortedTables.length - 1; i >= 0; i--) {
252 dropTable(sortedTables[i]);
258 * @throws SQLException
260 public void dropTable(final String aTable) throws Exception {
261 executeInTransaction(new JdbcUnitOfWork<Void>() {
262 public Void execute(Connection aConnection) throws Exception {
263 executeUpdate(aConnection, "drop table " + aTable);
271 * Executes an SQL statement within a transaction.
275 * @return Return code of the corresponding JDBC call.
277 public int executeSql(final String aSql) throws Exception {
278 return executeSql(aSql, new Object[0]);
282 * Executes an SQL statement within a transaction. See
283 * {@link #setPreparedParam(int, PreparedStatement, Object)}for details on
284 * supported argument types.
289 * Argument of the sql statement.
290 * @return Return code of the corresponding JDBC call.
292 public int executeSql(final String aSql, final Object aArg)
294 return executeSql(aSql, new Object[] { aArg });
298 * Executes an sql statement. See
299 * {@link #setPreparedParam(int, PreparedStatement, Object)}for details on
300 * supported argument types.
303 * SQL query to execute.
306 * @return Number of rows updated.
308 public int executeSql(final String aSql, final Object[] aArgs)
310 return executeInTransaction(new JdbcUnitOfWork<Integer>() {
311 public Integer execute(Connection aConnection) throws Exception {
312 PreparedStatement stmt = aConnection.prepareStatement(aSql);
313 setPreparedParams(aArgs, stmt);
314 return stmt.executeUpdate();
320 * Executes an SQL query.
324 * @return Result set.
326 public ResultSet executeQuery(Connection aConnection, String aSql) {
327 return executeQuery(aConnection, aSql, new Object[0]);
331 * Executes a query with a single argument. See
332 * {@link #setPreparedParam(int, PreparedStatement, Object)}for details on
333 * supported argument types.
339 * @return Result set.
341 public ResultSet executeQuery(Connection aConnection, String aSql,
343 return executeQuery(aConnection, aSql, new Object[] { aArg });
347 * Executes a query. See
348 * {@link #setPreparedParam(int, PreparedStatement, Object)}for details on
349 * supported argument types.
354 * Arguments to the query.
355 * @return Result set.
357 public ResultSet executeQuery(Connection aConnection, final String aSql,
358 final Object[] aArgs) {
360 PreparedStatement statement = aConnection.prepareStatement(aSql);
361 setPreparedParams(aArgs, statement);
363 return statement.executeQuery();
364 } catch (SQLException e) {
365 throw new RuntimeException(e);
369 public int executeUpdate(Connection aConnection, final String aSql,
370 final Object... aArgs) {
372 PreparedStatement statement = aConnection.prepareStatement(aSql);
373 setPreparedParams(aArgs, statement);
375 return statement.executeUpdate();
376 } catch (SQLException e) {
377 throw new RuntimeException(e);
382 * Sets the values of a prepared statement. See
383 * {@link #setPreparedParam(int, PreparedStatement, Object)}for details on
384 * supported argument types.
387 * Arguments to the prepared statement.
390 * @throws SQLException
392 private void setPreparedParams(final Object[] aArgs,
393 PreparedStatement aStatement) throws SQLException {
394 for (int i = 1; i <= aArgs.length; i++) {
395 setPreparedParam(i, aStatement, aArgs[i - 1]);
400 * Sets a prepared statement parameter.
403 * Index of the parameter.
405 * Prepared statement.
407 * Value Must be of type Integer, Long, or String.
408 * @throws SQLException
410 private void setPreparedParam(int aIndex, PreparedStatement aStatement,
411 Object aObject) throws SQLException {
412 if (aObject instanceof Integer) {
413 aStatement.setInt(aIndex, ((Integer) aObject).intValue());
414 } else if (aObject instanceof Long) {
415 aStatement.setLong(aIndex, ((Long) aObject).longValue());
416 } else if (aObject instanceof String) {
417 aStatement.setString(aIndex, (String) aObject);
419 TestCase.fail("Unsupported object type for prepared statement: " +
420 aObject.getClass() + " value: " + aObject + " statement: " +
427 * @throws SQLException
429 public int getTableSize(final String aTable) throws Exception {
430 return executeInTransaction(new JdbcUnitOfWork<Integer>() {
431 public Integer execute(Connection aConnection) throws Exception {
432 ResultSet resultSet = executeQuery(aConnection,
433 "select count(*) from " + aTable);
435 return resultSet.getInt(1);
441 public int countResultSet(ResultSet aResultSet) throws SQLException {
444 while (aResultSet.next()) {