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.system.spring.component;
18 import junit.framework.TestCase;
20 import org.dbunit.DatabaseUnitException;
22 import org.dbunit.database.DatabaseConnection;
23 import org.dbunit.database.DatabaseSequenceFilter;
24 import org.dbunit.database.IDatabaseConnection;
26 import org.dbunit.dataset.FilteredDataSet;
27 import org.dbunit.dataset.IDataSet;
28 import org.dbunit.dataset.filter.ITableFilter;
30 import org.dbunit.operation.DatabaseOperation;
32 import org.hibernate.SessionFactory;
34 import org.springframework.beans.factory.NoSuchBeanDefinitionException;
36 import org.springframework.context.ApplicationContext;
38 import org.springframework.jdbc.core.JdbcTemplate;
39 import org.springframework.jdbc.datasource.DataSourceUtils;
41 import org.springframework.orm.hibernate3.HibernateTemplate;
43 import org.springframework.transaction.PlatformTransactionManager;
44 import org.springframework.transaction.TransactionDefinition;
45 import org.springframework.transaction.TransactionStatus;
46 import org.springframework.transaction.support.DefaultTransactionDefinition;
47 import org.springframework.transaction.support.TransactionCallback;
48 import org.springframework.transaction.support.TransactionCallbackWithoutResult;
49 import org.springframework.transaction.support.TransactionTemplate;
51 import org.wamblee.test.spring.TestTransactionCallback;
52 import org.wamblee.test.spring.TestTransactionCallbackWithoutResult;
54 import java.sql.Connection;
55 import java.sql.PreparedStatement;
56 import java.sql.ResultSet;
57 import java.sql.SQLException;
59 import java.util.ArrayList;
60 import java.util.Iterator;
61 import java.util.List;
63 import java.util.TreeMap;
64 import java.util.logging.Logger;
66 import javax.sql.DataSource;
69 * Test support class for database testing. Currently, this still requires the
70 * spring platform transaction manager and hibernate template.
72 public class DatabaseTesterComponent {
73 private static final Logger LOG = Logger
74 .getLogger(DatabaseTesterComponent.class.getName());
79 private static final String SCHEMA_PATTERN = "%";
82 * Cached spring application context.
84 private ApplicationContext context;
86 private HibernateTemplate hibernateTemplate;
88 private PlatformTransactionManager transactionManager;
90 private DataSource dataSource;
93 * Creates a new DatabaseTesterComponent object.
96 public DatabaseTesterComponent(HibernateTemplate aHibernateTemplate,
97 PlatformTransactionManager aTransactionManager, DataSource aDataSource) {
98 hibernateTemplate = aHibernateTemplate;
99 transactionManager = aTransactionManager;
100 dataSource = aDataSource;
105 * @return Hibernate session factory.
107 protected SessionFactory getSessionFactory() {
108 return hibernateTemplate.getSessionFactory();
112 * Performs common initialization for test cases:
114 * <li>Cleaning the database.</li>
119 public void setUp() throws Exception {
120 LOG.info("Performing setUp()");
126 * Performs common tear down after execution of a test case. Currenlty this
127 * method does nothing.
131 protected void tearDown() throws Exception {
137 * @return Transaction manager
139 protected PlatformTransactionManager getTransactionManager() {
140 return transactionManager;
145 * @return Starts a new transaction.
147 protected TransactionStatus getTransaction() {
148 DefaultTransactionDefinition def = new DefaultTransactionDefinition();
149 def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
151 return getTransactionManager().getTransaction(def);
155 * Returns the hibernate template for executing hibernate-specific
158 * @return Hibernate template.
160 public HibernateTemplate getTemplate() {
161 return hibernateTemplate;
165 * Flushes the session. Should be called after some Hibernate work and
166 * before JDBC is used to check results.
168 public void flush() {
169 getTemplate().flush();
173 * Flushes the session first and then removes all objects from the Session
174 * cache. Should be called after some Hibernate work and before JDBC is used
177 public void clear() {
179 getTemplate().clear();
183 * Evicts the object from the session. This is essential for the
184 * implementation of unit tests where first an object is saved and is
185 * retrieved later. By removing the object from the session, Hibernate must
186 * retrieve the object again from the database.
190 protected void evict(Object aObject) {
191 getTemplate().evict(aObject);
195 * Gets the connection.
197 * @return Connection.
199 public Connection getConnection() {
200 return DataSourceUtils.getConnection(getDataSource());
203 public void cleanDatabase() throws SQLException {
204 if (!isDatabaseConfigured()) {
208 String[] tables = getTableNames();
211 IDatabaseConnection connection = new DatabaseConnection(
213 ITableFilter filter = new DatabaseSequenceFilter(connection, tables);
214 IDataSet dataset = new FilteredDataSet(filter, connection
215 .createDataSet(tables));
217 DatabaseOperation.DELETE_ALL.execute(connection, dataset);
218 } catch (DatabaseUnitException e) {
219 SQLException exc = new SQLException(e.getMessage());
228 * @throws SQLException
230 public String[] getTableNames() throws SQLException {
231 List<String> result = new ArrayList<String>();
232 LOG.fine("Getting database table names to clean (schema: '" +
233 SCHEMA_PATTERN + "'");
235 ResultSet tables = getConnection().getMetaData().getTables(null,
236 SCHEMA_PATTERN, "%", new String[] { "TABLE" });
238 while (tables.next()) {
239 String table = tables.getString("TABLE_NAME");
241 // Make sure we do not touch hibernate's specific
242 // infrastructure tables.
243 if (!table.toLowerCase().startsWith("hibernate")) {
245 LOG.fine("Adding " + table +
246 " to list of tables to be cleaned.");
250 return (String[]) result.toArray(new String[0]);
256 * @throws SQLException
258 public void emptyTables(List aTableList) throws SQLException {
259 Iterator liTable = aTableList.iterator();
261 while (liTable.hasNext()) {
262 emptyTable((String) liTable.next());
269 * @throws SQLException
271 public void emptyTable(String aTable) throws SQLException {
272 executeSql("delete from " + aTable);
278 * @throws SQLException
280 public void dropTable(String aTable) throws SQLException {
281 executeQuery("drop table " + aTable);
285 * Executes an SQL statement within a transaction.
290 * @return Return code of the corresponding JDBC call.
292 public int executeSql(final String aSql) {
293 return executeSql(aSql, new Object[0]);
297 * Executes an SQL statement within a transaction. See
298 * {@link #setPreparedParam(int, PreparedStatement, Object)}for details on
299 * supported argument types.
304 * Argument of the sql statement.
306 * @return Return code of the corresponding JDBC call.
308 public int executeSql(final String aSql, final Object aArg) {
309 return executeSql(aSql, new Object[] { aArg });
313 * Executes an sql statement. See
314 * {@link #setPreparedParam(int, PreparedStatement, Object)}for details on
315 * supported argument types.
318 * SQL query to execute.
322 * @return Number of rows updated.
324 public int executeSql(final String aSql, final Object[] aArgs) {
325 Map results = executeTransaction(new TestTransactionCallback() {
326 public Map execute() throws Exception {
327 JdbcTemplate template = new JdbcTemplate(getDataSource());
328 int result = template.update(aSql, aArgs);
330 Map<String, Integer> map = new TreeMap<String, Integer>();
331 map.put("result", new Integer(result));
337 return ((Integer) results.get("result")).intValue();
341 * Executes a transaction with a result.
344 * Callback to do your transactional work.
348 public Object executeTransaction(TransactionCallback aCallback) {
349 TransactionTemplate lTemplate = new TransactionTemplate(
350 getTransactionManager());
352 return lTemplate.execute(aCallback);
356 * Executes a transaction without a result.
359 * Callback to do your transactional work. .
361 public void executeTransaction(TransactionCallbackWithoutResult aCallback) {
362 TransactionTemplate template = new TransactionTemplate(
363 getTransactionManager());
364 template.execute(aCallback);
368 * Executes a transaction with a result, causing the testcase to fail if any
369 * type of exception is thrown.
372 * Code to be executed within the transaction.
377 public Map executeTransaction(final TestTransactionCallback aCallback) {
378 return (Map) executeTransaction(new TransactionCallback() {
379 public Object doInTransaction(TransactionStatus aArg) {
381 return aCallback.execute();
382 } catch (Exception e) {
383 // test case must fail.
385 throw new RuntimeException(e);
392 * Executes a transaction with a result, causing the testcase to fail if any
393 * type of exception is thrown.
396 * Code to be executed within the transaction.
399 public void executeTransaction(
400 final TestTransactionCallbackWithoutResult aCallback) {
401 executeTransaction(new TransactionCallbackWithoutResult() {
402 public void doInTransactionWithoutResult(TransactionStatus aArg) {
405 } catch (Exception e) {
406 // test case must fail.
407 throw new RuntimeException(e.getMessage(), e);
414 * Executes an SQL query.
419 * @return Result set.
421 public ResultSet executeQuery(String aSql) {
422 return executeQuery(aSql, new Object[0]);
426 * Executes a query with a single argument. See
427 * {@link #setPreparedParam(int, PreparedStatement, Object)}for details on
428 * supported argument types.
435 * @return Result set.
437 public ResultSet executeQuery(String aSql, Object aArg) {
438 return executeQuery(aSql, new Object[] { aArg });
442 * Executes a query. See
443 * {@link #setPreparedParam(int, PreparedStatement, Object)}for details on
444 * supported argument types.
449 * Arguments to the query.
451 * @return Result set.
454 public ResultSet executeQuery(final String aSql, final Object[] aArgs) {
456 Connection connection = getConnection();
458 PreparedStatement statement = connection.prepareStatement(aSql);
459 setPreparedParams(aArgs, statement);
461 return statement.executeQuery();
462 } catch (SQLException e) {
463 throw new RuntimeException(e);
468 * Sets the values of a prepared statement. See
469 * {@link #setPreparedParam(int, PreparedStatement, Object)}for details on
470 * supported argument types.
473 * Arguments to the prepared statement.
477 * @throws SQLException
479 private void setPreparedParams(final Object[] aArgs,
480 PreparedStatement aStatement) throws SQLException {
481 for (int i = 1; i <= aArgs.length; i++) {
482 setPreparedParam(i, aStatement, aArgs[i - 1]);
487 * Sets a prepared statement parameter.
490 * Index of the parameter.
492 * Prepared statement.
494 * Value Must be of type Integer, Long, or String.
496 * @throws SQLException
498 private void setPreparedParam(int aIndex, PreparedStatement aStatement,
499 Object aObject) throws SQLException {
500 if (aObject instanceof Integer) {
501 aStatement.setInt(aIndex, ((Integer) aObject).intValue());
502 } else if (aObject instanceof Long) {
503 aStatement.setLong(aIndex, ((Integer) aObject).longValue());
504 } else if (aObject instanceof String) {
505 aStatement.setString(aIndex, (String) aObject);
507 TestCase.fail("Unsupported object type for prepared statement: " +
508 aObject.getClass() + " value: " + aObject + " statement: " +
513 private boolean isDatabaseConfigured() {
516 } catch (NoSuchBeanDefinitionException e) {
525 * @return Returns the dataSource.
527 public DataSource getDataSource() {
536 * @throws SQLException
538 public int getTableSize(final String aTable) throws SQLException {
539 ResultSet resultSet = executeQuery("select * from " + aTable);
542 while (resultSet.next()) {
549 public int countResultSet(ResultSet aResultSet) throws SQLException {
552 while (aResultSet.next()) {