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.apache.commons.logging.Log;
21 import org.apache.commons.logging.LogFactory;
23 import org.dbunit.DatabaseUnitException;
25 import org.dbunit.database.DatabaseConnection;
26 import org.dbunit.database.DatabaseSequenceFilter;
27 import org.dbunit.database.IDatabaseConnection;
29 import org.dbunit.dataset.FilteredDataSet;
30 import org.dbunit.dataset.IDataSet;
31 import org.dbunit.dataset.filter.ITableFilter;
33 import org.dbunit.operation.DatabaseOperation;
35 import org.hibernate.SessionFactory;
37 import org.springframework.beans.factory.NoSuchBeanDefinitionException;
39 import org.springframework.context.ApplicationContext;
41 import org.springframework.jdbc.core.JdbcTemplate;
42 import org.springframework.jdbc.datasource.DataSourceUtils;
44 import org.springframework.orm.hibernate3.HibernateTemplate;
46 import org.springframework.transaction.PlatformTransactionManager;
47 import org.springframework.transaction.TransactionDefinition;
48 import org.springframework.transaction.TransactionStatus;
49 import org.springframework.transaction.support.DefaultTransactionDefinition;
50 import org.springframework.transaction.support.TransactionCallback;
51 import org.springframework.transaction.support.TransactionCallbackWithoutResult;
52 import org.springframework.transaction.support.TransactionTemplate;
54 import org.wamblee.test.spring.TestTransactionCallback;
55 import org.wamblee.test.spring.TestTransactionCallbackWithoutResult;
57 import java.sql.Connection;
58 import java.sql.PreparedStatement;
59 import java.sql.ResultSet;
60 import java.sql.SQLException;
62 import java.util.ArrayList;
63 import java.util.Iterator;
64 import java.util.List;
66 import java.util.TreeMap;
68 import javax.sql.DataSource;
71 * Test support class for database testing. Currently, this still requires the
72 * spring platform transaction manager and hibernate template.
74 public class DatabaseTesterComponent {
75 private static final Log LOG = LogFactory
76 .getLog(DatabaseTesterComponent.class);
81 private static final String SCHEMA_PATTERN = "%";
84 * Cached spring application context.
86 private ApplicationContext context;
88 private HibernateTemplate hibernateTemplate;
90 private PlatformTransactionManager transactionManager;
92 private DataSource dataSource;
95 * Creates a new DatabaseTesterComponent object.
98 public DatabaseTesterComponent(HibernateTemplate aHibernateTemplate,
99 PlatformTransactionManager aTransactionManager, DataSource aDataSource) {
100 hibernateTemplate = aHibernateTemplate;
101 transactionManager = aTransactionManager;
102 dataSource = aDataSource;
107 * @return Hibernate session factory.
109 protected SessionFactory getSessionFactory() {
110 return hibernateTemplate.getSessionFactory();
114 * Performs common initialization for test cases:
116 * <li>Cleaning the database.</li>
121 public void setUp() throws Exception {
122 LOG.info("Performing setUp()");
128 * Performs common tear down after execution of a test case. Currenlty this
129 * method does nothing.
133 protected void tearDown() throws Exception {
139 * @return Transaction manager
141 protected PlatformTransactionManager getTransactionManager() {
142 return transactionManager;
147 * @return Starts a new transaction.
149 protected TransactionStatus getTransaction() {
150 DefaultTransactionDefinition def = new DefaultTransactionDefinition();
151 def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
153 return getTransactionManager().getTransaction(def);
157 * Returns the hibernate template for executing hibernate-specific
160 * @return Hibernate template.
162 public HibernateTemplate getTemplate() {
163 return hibernateTemplate;
167 * Flushes the session. Should be called after some Hibernate work and
168 * before JDBC is used to check results.
170 public void flush() {
171 getTemplate().flush();
175 * Flushes the session first and then removes all objects from the Session
176 * cache. Should be called after some Hibernate work and before JDBC is used
179 public void clear() {
181 getTemplate().clear();
185 * Evicts the object from the session. This is essential for the
186 * implementation of unit tests where first an object is saved and is
187 * retrieved later. By removing the object from the session, Hibernate must
188 * retrieve the object again from the database.
192 protected void evict(Object aObject) {
193 getTemplate().evict(aObject);
197 * Gets the connection.
199 * @return Connection.
201 public Connection getConnection() {
202 return DataSourceUtils.getConnection(getDataSource());
205 public void cleanDatabase() throws SQLException {
206 if (!isDatabaseConfigured()) {
210 String[] tables = getTableNames();
213 IDatabaseConnection connection = new DatabaseConnection(
215 ITableFilter filter = new DatabaseSequenceFilter(connection, tables);
216 IDataSet dataset = new FilteredDataSet(filter, connection
217 .createDataSet(tables));
219 DatabaseOperation.DELETE_ALL.execute(connection, dataset);
220 } catch (DatabaseUnitException e) {
221 SQLException exc = new SQLException(e.getMessage());
230 * @throws SQLException
232 public String[] getTableNames() throws SQLException {
233 List<String> result = new ArrayList<String>();
234 LOG.debug("Getting database table names to clean (schema: '" +
235 SCHEMA_PATTERN + "'");
237 ResultSet tables = getConnection().getMetaData().getTables(null,
238 SCHEMA_PATTERN, "%", new String[] { "TABLE" });
240 while (tables.next()) {
241 String table = tables.getString("TABLE_NAME");
243 // Make sure we do not touch hibernate's specific
244 // infrastructure tables.
245 if (!table.toLowerCase().startsWith("hibernate")) {
247 LOG.debug("Adding " + table +
248 " to list of tables to be cleaned.");
252 return (String[]) result.toArray(new String[0]);
258 * @throws SQLException
260 public void emptyTables(List aTableList) throws SQLException {
261 Iterator liTable = aTableList.iterator();
263 while (liTable.hasNext()) {
264 emptyTable((String) liTable.next());
271 * @throws SQLException
273 public void emptyTable(String aTable) throws SQLException {
274 executeSql("delete from " + aTable);
280 * @throws SQLException
282 public void dropTable(String aTable) throws SQLException {
283 executeQuery("drop table " + aTable);
287 * Executes an SQL statement within a transaction.
292 * @return Return code of the corresponding JDBC call.
294 public int executeSql(final String aSql) {
295 return executeSql(aSql, new Object[0]);
299 * Executes an SQL statement within a transaction. See
300 * {@link #setPreparedParam(int, PreparedStatement, Object)}for details on
301 * supported argument types.
306 * Argument of the sql statement.
308 * @return Return code of the corresponding JDBC call.
310 public int executeSql(final String aSql, final Object aArg) {
311 return executeSql(aSql, new Object[] { aArg });
315 * Executes an sql statement. See
316 * {@link #setPreparedParam(int, PreparedStatement, Object)}for details on
317 * supported argument types.
320 * SQL query to execute.
324 * @return Number of rows updated.
326 public int executeSql(final String aSql, final Object[] aArgs) {
327 Map results = executeTransaction(new TestTransactionCallback() {
328 public Map execute() throws Exception {
329 JdbcTemplate template = new JdbcTemplate(getDataSource());
330 int result = template.update(aSql, aArgs);
332 Map<String, Integer> map = new TreeMap<String, Integer>();
333 map.put("result", new Integer(result));
339 return ((Integer) results.get("result")).intValue();
343 * Executes a transaction with a result.
346 * Callback to do your transactional work.
350 public Object executeTransaction(TransactionCallback aCallback) {
351 TransactionTemplate lTemplate = new TransactionTemplate(
352 getTransactionManager());
354 return lTemplate.execute(aCallback);
358 * Executes a transaction without a result.
361 * Callback to do your transactional work. .
363 public void executeTransaction(TransactionCallbackWithoutResult aCallback) {
364 TransactionTemplate template = new TransactionTemplate(
365 getTransactionManager());
366 template.execute(aCallback);
370 * Executes a transaction with a result, causing the testcase to fail if any
371 * type of exception is thrown.
374 * Code to be executed within the transaction.
379 public Map executeTransaction(final TestTransactionCallback aCallback) {
380 return (Map) executeTransaction(new TransactionCallback() {
381 public Object doInTransaction(TransactionStatus aArg) {
383 return aCallback.execute();
384 } catch (Exception e) {
385 // test case must fail.
387 throw new RuntimeException(e);
394 * Executes a transaction with a result, causing the testcase to fail if any
395 * type of exception is thrown.
398 * Code to be executed within the transaction.
401 public void executeTransaction(
402 final TestTransactionCallbackWithoutResult aCallback) {
403 executeTransaction(new TransactionCallbackWithoutResult() {
404 public void doInTransactionWithoutResult(TransactionStatus aArg) {
407 } catch (Exception e) {
408 // test case must fail.
409 throw new RuntimeException(e.getMessage(), e);
416 * Executes an SQL query.
421 * @return Result set.
423 public ResultSet executeQuery(String aSql) {
424 return executeQuery(aSql, new Object[0]);
428 * Executes a query with a single argument. See
429 * {@link #setPreparedParam(int, PreparedStatement, Object)}for details on
430 * supported argument types.
437 * @return Result set.
439 public ResultSet executeQuery(String aSql, Object aArg) {
440 return executeQuery(aSql, new Object[] { aArg });
444 * Executes a query. See
445 * {@link #setPreparedParam(int, PreparedStatement, Object)}for details on
446 * supported argument types.
451 * Arguments to the query.
453 * @return Result set.
456 public ResultSet executeQuery(final String aSql, final Object[] aArgs) {
458 Connection connection = getConnection();
460 PreparedStatement statement = connection.prepareStatement(aSql);
461 setPreparedParams(aArgs, statement);
463 return statement.executeQuery();
464 } catch (SQLException e) {
465 throw new RuntimeException(e);
470 * Sets the values of a prepared statement. See
471 * {@link #setPreparedParam(int, PreparedStatement, Object)}for details on
472 * supported argument types.
475 * Arguments to the prepared statement.
479 * @throws SQLException
481 private void setPreparedParams(final Object[] aArgs,
482 PreparedStatement aStatement) throws SQLException {
483 for (int i = 1; i <= aArgs.length; i++) {
484 setPreparedParam(i, aStatement, aArgs[i - 1]);
489 * Sets a prepared statement parameter.
492 * Index of the parameter.
494 * Prepared statement.
496 * Value Must be of type Integer, Long, or String.
498 * @throws SQLException
500 private void setPreparedParam(int aIndex, PreparedStatement aStatement,
501 Object aObject) throws SQLException {
502 if (aObject instanceof Integer) {
503 aStatement.setInt(aIndex, ((Integer) aObject).intValue());
504 } else if (aObject instanceof Long) {
505 aStatement.setLong(aIndex, ((Integer) aObject).longValue());
506 } else if (aObject instanceof String) {
507 aStatement.setString(aIndex, (String) aObject);
509 TestCase.fail("Unsupported object type for prepared statement: " +
510 aObject.getClass() + " value: " + aObject + " statement: " +
515 private boolean isDatabaseConfigured() {
518 } catch (NoSuchBeanDefinitionException e) {
527 * @return Returns the dataSource.
529 public DataSource getDataSource() {
538 * @throws SQLException
540 public int getTableSize(final String aTable) throws SQLException {
541 ResultSet resultSet = executeQuery("select * from " + aTable);
544 while (resultSet.next()) {
551 public int countResultSet(ResultSet aResultSet) throws SQLException {
554 while (aResultSet.next()) {