* 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.wamblee.support.persistence;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
+import java.util.logging.Level;
import java.util.logging.Logger;
import javax.sql.DataSource;
*/
public class DatabaseUtils {
+ /**
+ * Represents a set of tables.
+ *
+ * @author Erik Brakkee
+ */
public static interface TableSet {
boolean contains(String aTableName);
}
+ /**
+ * Represents a unit of work (transaction).
+ *
+ * @author Erik Brakkee
+ *
+ * @param <T> Type of return value.
+ */
public static interface JdbcUnitOfWork<T> {
+ /**
+ * Executes statement within a transaction.
+ * @param aConnection Connection.
+ * @return Result of the work.
+ * @throws Exception
+ */
T execute(Connection aConnection) throws Exception;
}
+ /**
+ * Operation to be executed on a set of tables for each table
+ * individually.
+ *
+ * @author Erik Brakkee
+ */
public static interface TableSetOperation {
+ /**
+ * Executes on a table.
+ * @param aTable Table name.
+ * @throws Exception
+ */
void execute(String aTable) throws Exception;
}
*/
private static final String SCHEMA_PATTERN = "%";
private DataSource dataSource;
- private ITableFilterSimple tables;
- public DatabaseUtils(DataSource aDataSource, ITableFilterSimple aTables) {
+ private IDatabaseTester dbtester;
+ /**
+ * List of connections that were created for dbtesters.
+ * This list will be closed in the {@link #stop()} method.
+ */
+ private List<IDatabaseConnection> connections;
+
+ /**
+ * Constructs the database utils.
+ * Before use, {@link #start()} must be called.
+ * @param aDataSource Datasource.
+ */
+ public DatabaseUtils(DataSource aDataSource) {
dataSource = aDataSource;
- tables = aTables;
+ dbtester = new DataSourceDatabaseTester(dataSource);
+ connections = new ArrayList<IDatabaseConnection>();
}
- public IDatabaseTester createDbTester() throws Exception {
- return createDbTester(getTableNames(tables));
+ /**
+ * Starts the database utils.
+ */
+ public void start() {
+ // Empty. No operation currently.
}
- public IDatabaseTester createDbTester(String[] aTables) throws Exception {
- IDatabaseTester dbtester = new DataSourceDatabaseTester(dataSource);
- dbtester.setDataSet(dbtester.getConnection().createDataSet(aTables));
- return dbtester;
+ /**
+ * Stops the database utils, closing any JDBC connections that were created
+ * by this utility. Note that connections obtained from the datasource directly
+ * must still be closed by the user. The involved connections are only those that
+ * are created by this utility.
+ */
+ public void stop() {
+ for (IDatabaseConnection connection: connections) {
+ try {
+ connection.close();
+ } catch (SQLException e) {
+ LOG.log(Level.WARNING, "Could not close connection", e);
+ }
+ }
+ connections.clear();
}
- public void cleanDatabase() throws Exception {
- cleanDatabase(tables);
+ /**
+ * Creates database tester.
+ * @param aTables Tables to create the tester for.
+ * @return Database tester.
+ * @throws Exception
+ */
+ public IDatabaseTester createDbTester(ITableFilterSimple aTables) throws Exception {
+ return createDbTester(getTableNames(aTables));
+ }
+
+ /**
+ * Creates database tester.
+ * @param aTables Tables to create the tester for.
+ * @return Database tester.
+ * @throws Exception
+ */
+ public IDatabaseTester createDbTester(String[] aTables) throws Exception {
+ IDatabaseConnection connection = dbtester.getConnection();
+ connections.add(connection);
+ dbtester.setDataSet(connection.createDataSet(aTables));
+ return dbtester;
}
+ /**
+ * Executes an operation on a set of tables.
+ * @param aTables Tables.
+ * @param aOperation Operation.
+ * @throws Exception
+ */
public void executeOnTables(ITableFilterSimple aTables,
final TableSetOperation aOperation) throws Exception {
- final String[] tables = getTableNames(aTables);
+ final String[] tableNames = getTableNames(aTables);
executeInTransaction(new JdbcUnitOfWork<Void>() {
public Void execute(Connection aConnection) throws Exception {
- for (int i = tables.length - 1; i >= 0; i--) {
- aOperation.execute(tables[i]);
+ for (int i = tableNames.length - 1; i >= 0; i--) {
+ aOperation.execute(tableNames[i]);
}
return null;
}
});
- for (String table : tables) {
-
- }
}
+ /**
+ * Cleans a number of database tables. This means deleting the content not dropping the tables.
+ * This may fail in case of cyclic dependencies between the tables (current limitation).
+ * @param aSelection Tables.
+ * @throws Exception
+ */
public void cleanDatabase(ITableFilterSimple aSelection) throws Exception {
- final String[] tables = getTableNames(aSelection);
+ final String[] tableNames = getTableNames(aSelection);
executeInTransaction(new JdbcUnitOfWork<Void>() {
public Void execute(Connection aConnection) throws Exception {
IDatabaseConnection connection = new DatabaseConnection(
aConnection);
ITableFilter filter = new DatabaseSequenceFilter(connection,
- tables);
+ tableNames);
IDataSet dataset = new FilteredDataSet(filter, connection
- .createDataSet(tables));
+ .createDataSet(tableNames));
DatabaseOperation.DELETE_ALL.execute(connection, dataset);
return null;
}
}
- public <T> T executeInTransaction(JdbcUnitOfWork<T> aCallback)
+ /**
+ * Executes a unit of work within a transaction.
+ * @param <T> Result type of th ework.
+ * @param aWork Unit of work.
+ * @return
+ * @throws Exception
+ */
+ public <T> T executeInTransaction(JdbcUnitOfWork<T> aWork)
throws Exception {
Connection connection = dataSource.getConnection();
+ connection.setAutoCommit(false);
try {
- T value = aCallback.execute(connection);
+ T value = aWork.execute(connection);
connection.commit();
return value;
} finally {
connection.close();
}
}
-
- public String[] getTableNames() throws Exception {
- return getTableNames(tables);
- }
-
+
/**
- * @throws SQLException
+ * Returns table names based on a table filter.
+ * @param aSelection Table filter.
+ * @return Table names.
+ * @throws Exception
*/
public String[] getTableNames(ITableFilterSimple aSelection)
throws Exception {
LOG.fine("Getting database table names to clean (schema: '" +
SCHEMA_PATTERN + "'");
- ResultSet tables = dataSource.getConnection().getMetaData().getTables(
- null, SCHEMA_PATTERN, "%", new String[] { "TABLE" });
- while (tables.next()) {
- String table = tables.getString("TABLE_NAME");
- if (aSelection.accept(table)) {
- result.add(table);
+ Connection connection = dataSource.getConnection();
+ try {
+ ResultSet tableNames = connection.getMetaData().getTables(null,
+ SCHEMA_PATTERN, "%", new String[] { "TABLE" });
+ while (tableNames.next()) {
+ String table = tableNames.getString("TABLE_NAME");
+ if (aSelection.accept(table)) {
+ result.add(table);
+ }
}
+ return (String[]) result.toArray(new String[0]);
+ } finally {
+ connection.close();
}
- return (String[]) result.toArray(new String[0]);
- }
-
- public void emptyTables() throws Exception {
- executeOnTables(tables, new TableSetOperation() {
- public void execute(String aTable) throws Exception {
- emptyTable(aTable);
- }
- });
}
/**
- * @return
- * @throws SQLException
+ * Use {@link #cleanDatabase(ITableFilterSimple)} instead.
*/
+ @Deprecated
public void emptyTables(final ITableFilterSimple aSelection)
throws Exception {
executeOnTables(aSelection, new TableSetOperation() {
}
/**
- * @return
- * @throws SQLException
+ * User {@link #cleanDatabase(ITableFilterSimple)} instead.
*/
+ @Deprecated
public void emptyTable(String aTable) throws Exception {
executeSql("delete from " + aTable);
}
- public void dropTables() throws Exception {
- executeOnTables(tables, new TableSetOperation() {
-
- public void execute(String aTable) throws Exception {
- dropTable(aTable);
- }
- });
- }
-
+ /**
+ * Drops tables. This only works if there are no cyclic dependencies between the tables.
+ * @param aTables Tables to drop.
+ * @throws Exception
+ */
public void dropTables(ITableFilterSimple aTables) throws Exception {
- executeOnTables(aTables, new TableSetOperation() {
+ final String[] tableNames = getTableNames(aTables);
+ String[] sortedTables = executeInTransaction(new JdbcUnitOfWork<String[]>() {
- public void execute(String aTable) throws Exception {
- dropTable(aTable);
+ public String[] execute(Connection aConnection) throws Exception {
+ IDatabaseConnection connection = new DatabaseConnection(
+ aConnection);
+ ITableFilter filter = new DatabaseSequenceFilter(connection,
+ tableNames);
+ IDataSet dataset = new FilteredDataSet(filter, connection
+ .createDataSet(tableNames));
+ return dataset.getTableNames();
}
});
+ for (int i = sortedTables.length - 1; i >= 0; i--) {
+ dropTable(sortedTables[i]);
+ }
}
/**
- * @return
- * @throws SQLException
+ * Drops a table.
+ * @param aTable Table to drop.
+ * @throws Exception
*/
public void dropTable(final String aTable) throws Exception {
executeInTransaction(new JdbcUnitOfWork<Void>() {
}
}
+ /**
+ * Executes an update.
+ * @param aConnection Connection to use.
+ * @param aSql SQL update to use.
+ * @param aArgs Arguments to the update.
+ * @return Number of rows updated.
+ */
public int executeUpdate(Connection aConnection, final String aSql,
final Object... aArgs) {
try {
if (aObject instanceof Integer) {
aStatement.setInt(aIndex, ((Integer) aObject).intValue());
} else if (aObject instanceof Long) {
- aStatement.setLong(aIndex, ((Integer) aObject).longValue());
+ aStatement.setLong(aIndex, ((Long) aObject).longValue());
} else if (aObject instanceof String) {
aStatement.setString(aIndex, (String) aObject);
} else {
}
/**
- * @return
+ * Gets the table size.
+ * @param aTable Table.
+ * @return Table size.
* @throws SQLException
*/
public int getTableSize(final String aTable) throws Exception {
}
+ /**
+ * Counts the results in a result set.
+ * @param aResultSet Resultset.
+ * @return Number of rows in the set.
+ * @throws SQLException
+ */
public int countResultSet(ResultSet aResultSet) throws SQLException {
int count = 0;