2 * Copyright 2005 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.
17 package org.wamblee.system.spring.component;
19 import java.sql.Connection;
20 import java.sql.PreparedStatement;
21 import java.sql.ResultSet;
22 import java.sql.SQLException;
23 import java.util.ArrayList;
24 import java.util.Iterator;
25 import java.util.List;
27 import java.util.TreeMap;
29 import javax.sql.DataSource;
31 import junit.framework.TestCase;
33 import org.apache.commons.logging.Log;
34 import org.apache.commons.logging.LogFactory;
35 import org.dbunit.DatabaseUnitException;
36 import org.dbunit.database.DatabaseConnection;
37 import org.dbunit.database.DatabaseSequenceFilter;
38 import org.dbunit.database.IDatabaseConnection;
39 import org.dbunit.dataset.FilteredDataSet;
40 import org.dbunit.dataset.IDataSet;
41 import org.dbunit.dataset.filter.ITableFilter;
42 import org.dbunit.operation.DatabaseOperation;
43 import org.hibernate.SessionFactory;
44 import org.springframework.beans.factory.NoSuchBeanDefinitionException;
45 import org.springframework.context.ApplicationContext;
46 import org.springframework.jdbc.core.JdbcTemplate;
47 import org.springframework.jdbc.datasource.DataSourceUtils;
48 import org.springframework.orm.hibernate3.HibernateTemplate;
49 import org.springframework.transaction.PlatformTransactionManager;
50 import org.springframework.transaction.TransactionDefinition;
51 import org.springframework.transaction.TransactionStatus;
52 import org.springframework.transaction.support.DefaultTransactionDefinition;
53 import org.springframework.transaction.support.TransactionCallback;
54 import org.springframework.transaction.support.TransactionCallbackWithoutResult;
55 import org.springframework.transaction.support.TransactionTemplate;
56 import org.wamblee.test.spring.TestTransactionCallback;
57 import org.wamblee.test.spring.TestTransactionCallbackWithoutResult;
60 * Test support class for database testing. Currently, this still requires the
61 * spring platform transaction manager and hibernate template.
63 public class DatabaseTesterComponent {
65 private static final Log LOG = LogFactory
66 .getLog(DatabaseTesterComponent.class);
71 private static final String SCHEMA_PATTERN = "%";
74 * Cached spring application context.
76 private ApplicationContext _context;
78 private HibernateTemplate _hibernateTemplate;
80 private PlatformTransactionManager _transactionManager;
82 private DataSource _dataSource;
84 public DatabaseTesterComponent(HibernateTemplate aHibernateTemplate,
85 PlatformTransactionManager aTransactionManager,
86 DataSource aDataSource) {
87 _hibernateTemplate = aHibernateTemplate;
88 _transactionManager = aTransactionManager;
89 _dataSource = aDataSource;
93 * @return Hibernate session factory.
95 protected SessionFactory getSessionFactory() {
96 return _hibernateTemplate.getSessionFactory();
100 * Performs common initialization for test cases:
102 * <li>Cleaning the database. </li>
107 public void setUp() throws Exception {
108 LOG.info("Performing setUp()");
114 * Performs common tear down after execution of a test case. Currenlty this
115 * method does nothing.
119 protected void tearDown() throws Exception {
124 * @return Transaction manager
126 protected PlatformTransactionManager getTransactionManager() {
127 return _transactionManager;
131 * @return Starts a new transaction.
133 protected TransactionStatus getTransaction() {
134 DefaultTransactionDefinition def = new DefaultTransactionDefinition();
135 def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
137 return getTransactionManager().getTransaction(def);
141 * Returns the hibernate template for executing hibernate-specific
144 * @return Hibernate template.
146 public HibernateTemplate getTemplate() {
147 return _hibernateTemplate;
151 * Flushes the session. Should be called after some Hibernate work and
152 * before JDBC is used to check results.
155 public void flush() {
156 getTemplate().flush();
160 * Flushes the session first and then removes all objects from the Session
161 * cache. Should be called after some Hibernate work and before JDBC is used
165 public void clear() {
167 getTemplate().clear();
171 * Evicts the object from the session. This is essential for the
172 * implementation of unit tests where first an object is saved and is
173 * retrieved later. By removing the object from the session, Hibernate must
174 * retrieve the object again from the database.
178 protected void evict(Object aObject) {
179 getTemplate().evict(aObject);
183 * Gets the connection.
185 * @return Connection.
187 public Connection getConnection() {
188 return DataSourceUtils.getConnection(getDataSource());
191 public void cleanDatabase() throws SQLException {
193 if (!isDatabaseConfigured()) {
197 String[] tables = getTableNames();
200 IDatabaseConnection connection = new DatabaseConnection(
202 ITableFilter filter = new DatabaseSequenceFilter(connection, tables);
203 IDataSet dataset = new FilteredDataSet(filter, connection
204 .createDataSet(tables));
206 DatabaseOperation.DELETE_ALL.execute(connection, dataset);
207 } catch (DatabaseUnitException e) {
208 SQLException exc = new SQLException(e.getMessage());
215 * @throws SQLException
217 public String[] getTableNames() throws SQLException {
219 List<String> result = new ArrayList<String>();
220 LOG.debug("Getting database table names to clean (schema: '"
221 + SCHEMA_PATTERN + "'");
223 ResultSet tables = getConnection().getMetaData().getTables(null,
224 SCHEMA_PATTERN, "%", new String[] { "TABLE" });
225 while (tables.next()) {
226 String table = tables.getString("TABLE_NAME");
227 // Make sure we do not touch hibernate's specific
228 // infrastructure tables.
229 if (!table.toLowerCase().startsWith("hibernate")) {
231 LOG.debug("Adding " + table
232 + " to list of tables to be cleaned.");
235 return (String[]) result.toArray(new String[0]);
240 * @throws SQLException
242 public void emptyTables(List aTableList) throws SQLException {
243 Iterator liTable = aTableList.iterator();
244 while (liTable.hasNext()) {
245 emptyTable((String) liTable.next());
251 * @throws SQLException
253 public void emptyTable(String aTable) throws SQLException {
254 executeSql("delete from " + aTable);
259 * @throws SQLException
261 public void dropTable(String aTable) throws SQLException {
262 executeQuery("drop table " + aTable);
266 * Executes an SQL statement within a transaction.
270 * @return Return code of the corresponding JDBC call.
272 public int executeSql(final String aSql) {
273 return executeSql(aSql, new Object[0]);
277 * Executes an SQL statement within a transaction. See
278 * {@link #setPreparedParam(int, PreparedStatement, Object)}for details on
279 * supported argument types.
284 * Argument of the sql statement.
285 * @return Return code of the corresponding JDBC call.
287 public int executeSql(final String aSql, final Object aArg) {
288 return executeSql(aSql, new Object[] { aArg });
292 * Executes an sql statement. See
293 * {@link #setPreparedParam(int, PreparedStatement, Object)}for details on
294 * supported argument types.
297 * SQL query to execute.
300 * @return Number of rows updated.
302 public int executeSql(final String aSql, final Object[] aArgs) {
303 Map results = executeTransaction(new TestTransactionCallback() {
304 public Map execute() throws Exception {
305 JdbcTemplate template = new JdbcTemplate(getDataSource());
306 int result = template.update(aSql, aArgs);
308 Map<String, Integer> map = new TreeMap<String, Integer>();
309 map.put("result", new Integer(result));
315 return ((Integer) results.get("result")).intValue();
319 * Executes a transaction with a result.
322 * Callback to do your transactional work.
325 public Object executeTransaction(TransactionCallback aCallback) {
326 TransactionTemplate lTemplate = new TransactionTemplate(
327 getTransactionManager());
328 return lTemplate.execute(aCallback);
332 * Executes a transaction without a result.
335 * Callback to do your transactional work. .
337 public void executeTransaction(TransactionCallbackWithoutResult aCallback) {
338 TransactionTemplate template = new TransactionTemplate(
339 getTransactionManager());
340 template.execute(aCallback);
344 * Executes a transaction with a result, causing the testcase to fail if any
345 * type of exception is thrown.
348 * Code to be executed within the transaction.
351 public Map executeTransaction(final TestTransactionCallback aCallback) {
352 return (Map) executeTransaction(new TransactionCallback() {
353 public Object doInTransaction(TransactionStatus aArg) {
355 return aCallback.execute();
356 } catch (Exception e) {
357 // test case must fail.
359 throw new RuntimeException(e);
366 * Executes a transaction with a result, causing the testcase to fail if any
367 * type of exception is thrown.
370 * Code to be executed within the transaction.
372 public void executeTransaction(
373 final TestTransactionCallbackWithoutResult aCallback) {
374 executeTransaction(new TransactionCallbackWithoutResult() {
375 public void doInTransactionWithoutResult(TransactionStatus aArg) {
378 } catch (Exception e) {
379 // test case must fail.
380 throw new RuntimeException(e.getMessage(), e);
387 * Executes an SQL query.
391 * @return Result set.
393 public ResultSet executeQuery(String aSql) {
394 return executeQuery(aSql, new Object[0]);
398 * Executes a query with a single argument. See
399 * {@link #setPreparedParam(int, PreparedStatement, Object)}for details on
400 * supported argument types.
406 * @return Result set.
408 public ResultSet executeQuery(String aSql, Object aArg) {
409 return executeQuery(aSql, new Object[] { aArg });
413 * Executes a query. See
414 * {@link #setPreparedParam(int, PreparedStatement, Object)}for details on
415 * supported argument types.
420 * Arguments to the query.
421 * @return Result set.
423 public ResultSet executeQuery(final String aSql, final Object[] aArgs) {
425 Connection connection = getConnection();
427 PreparedStatement statement = connection.prepareStatement(aSql);
428 setPreparedParams(aArgs, statement);
430 return statement.executeQuery();
431 } catch (SQLException e) {
432 throw new RuntimeException(e);
437 * Sets the values of a prepared statement. See
438 * {@link #setPreparedParam(int, PreparedStatement, Object)}for details on
439 * supported argument types.
442 * Arguments to the prepared statement.
445 * @throws SQLException
447 private void setPreparedParams(final Object[] aArgs,
448 PreparedStatement aStatement) throws SQLException {
449 for (int i = 1; i <= aArgs.length; i++) {
450 setPreparedParam(i, aStatement, aArgs[i - 1]);
455 * Sets a prepared statement parameter.
458 * Index of the parameter.
460 * Prepared statement.
462 * Value Must be of type Integer, Long, or String.
463 * @throws SQLException
465 private void setPreparedParam(int aIndex, PreparedStatement aStatement,
466 Object aObject) throws SQLException {
467 if (aObject instanceof Integer) {
468 aStatement.setInt(aIndex, ((Integer) aObject).intValue());
469 } else if (aObject instanceof Long) {
470 aStatement.setLong(aIndex, ((Integer) aObject).longValue());
471 } else if (aObject instanceof String) {
472 aStatement.setString(aIndex, (String) aObject);
474 TestCase.fail("Unsupported object type for prepared statement: "
475 + aObject.getClass() + " value: " + aObject
476 + " statement: " + aStatement);
480 private boolean isDatabaseConfigured() {
483 } catch (NoSuchBeanDefinitionException e) {
490 * @return Returns the dataSource.
492 public DataSource getDataSource() {
498 * @throws SQLException
500 public int getTableSize(final String aTable) throws SQLException {
502 ResultSet resultSet = executeQuery("select * from " + aTable);
505 while (resultSet.next()) {
511 public int countResultSet(ResultSet aResultSet) throws SQLException {
514 while (aResultSet.next()) {