View Javadoc

1   /*
2    *
3    * The DbUnit Database Testing Framework
4    * Copyright (C)2002-2004, DbUnit.org
5    *
6    * This library is free software; you can redistribute it and/or
7    * modify it under the terms of the GNU Lesser General Public
8    * License as published by the Free Software Foundation; either
9    * version 2.1 of the License, or (at your option) any later version.
10   *
11   * This library is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   * Lesser General Public License for more details.
15   *
16   * You should have received a copy of the GNU Lesser General Public
17   * License along with this library; if not, write to the Free Software
18   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19   *
20   */
21  
22  package org.dbunit.database;
23  
24  import java.sql.Connection;
25  import java.sql.DatabaseMetaData;
26  import java.sql.ResultSet;
27  import java.sql.SQLException;
28  
29  import org.dbunit.DatabaseUnitRuntimeException;
30  import org.dbunit.dataset.AbstractDataSet;
31  import org.dbunit.dataset.Column;
32  import org.dbunit.dataset.DataSetException;
33  import org.dbunit.dataset.DataSetUtils;
34  import org.dbunit.dataset.IDataSet;
35  import org.dbunit.dataset.ITable;
36  import org.dbunit.dataset.ITableIterator;
37  import org.dbunit.dataset.ITableMetaData;
38  import org.dbunit.dataset.NoSuchTableException;
39  import org.dbunit.dataset.OrderedTableNameMap;
40  import org.dbunit.dataset.filter.ITableFilterSimple;
41  import org.dbunit.util.QualifiedTableName;
42  import org.dbunit.util.SQLHelper;
43  import org.slf4j.Logger;
44  import org.slf4j.LoggerFactory;
45  
46  /**
47   * Provides access to a database instance as a {@link IDataSet}.
48   * 
49   * @author Manuel Laflamme
50   * @author Last changed by: $Author: jeffjensen $
51   * @version $Revision: 1189 $ $Date: 2010-06-06 00:49:22 +0200 (dom, 06 giu 2010) $
52   * @since 1.0 (Feb 17, 2002)
53   */
54  public class DatabaseDataSet extends AbstractDataSet
55  {
56  
57      /**
58       * Logger for this class
59       */
60      private static final Logger logger = LoggerFactory.getLogger(DatabaseDataSet.class);
61  
62      private final IDatabaseConnection _connection;
63      private OrderedTableNameMap _tableMap = null;
64  
65      private final ITableFilterSimple _tableFilter;
66      private final ITableFilterSimple _oracleRecycleBinTableFilter;
67  
68      /**
69       * Creates a new database data set
70       * @param connection
71       * @throws SQLException
72       */
73      DatabaseDataSet(IDatabaseConnection connection) throws SQLException
74      {
75          this(connection, connection.getConfig().getFeature(DatabaseConfig.FEATURE_CASE_SENSITIVE_TABLE_NAMES));
76      }
77  
78  
79      /**
80       * Creates a new database data set
81       * @param connection The database connection
82       * @param caseSensitiveTableNames Whether or not this dataset should use case sensitive table names
83       * @throws SQLException
84       * @since 2.4
85       */
86      public DatabaseDataSet(IDatabaseConnection connection, boolean caseSensitiveTableNames) throws SQLException
87      {
88          this(connection, caseSensitiveTableNames, null);
89      }
90  
91      /**
92       * Creates a new database data set
93       * @param connection The database connection
94       * @param caseSensitiveTableNames Whether or not this dataset should use case sensitive table names
95       * @param tableFilter Table filter to specify tables to be omitted in this dataset. Can be <code>null</code>.
96       * @throws SQLException
97       * @since 2.4.3
98       */
99      public DatabaseDataSet(IDatabaseConnection connection, boolean caseSensitiveTableNames, ITableFilterSimple tableFilter)
100     throws SQLException
101     {
102         super(caseSensitiveTableNames);
103         if (connection == null) {
104             throw new NullPointerException(
105             "The parameter 'connection' must not be null");
106         }
107         _connection = connection;
108         _tableFilter = tableFilter;
109         _oracleRecycleBinTableFilter = new OracleRecycleBinTableFilter(connection.getConfig());
110     }
111 
112 
113 
114     static String getSelectStatement(String schema, ITableMetaData metaData, String escapePattern)
115     throws DataSetException
116     {
117         if (logger.isDebugEnabled())
118         {
119             logger.debug("getSelectStatement(schema={}, metaData={}, escapePattern={}) - start",
120                     new Object[] { schema, metaData, escapePattern });
121         }
122 
123         Column[] columns = metaData.getColumns();
124         Column[] primaryKeys = metaData.getPrimaryKeys();
125 
126         if(columns.length==0){
127             throw new DatabaseUnitRuntimeException("At least one column is required to build a valid select statement. "+
128                     "Cannot load data for " + metaData);
129         }
130 
131         // select
132         StringBuffer sqlBuffer = new StringBuffer(128);
133         sqlBuffer.append("select ");
134         for (int i = 0; i < columns.length; i++)
135         {
136             if (i > 0)
137             {
138                 sqlBuffer.append(", ");
139             }
140             String columnName = new QualifiedTableName(
141                     columns[i].getColumnName(), null, escapePattern).getQualifiedName();
142             sqlBuffer.append(columnName);
143         }
144 
145         // from
146         sqlBuffer.append(" from ");
147         sqlBuffer.append(new QualifiedTableName(
148                 metaData.getTableName(), schema, escapePattern).getQualifiedName());
149 
150         // order by
151         for (int i = 0; i < primaryKeys.length; i++)
152         {
153             if (i == 0)
154             {
155                 sqlBuffer.append(" order by ");
156             }
157             else
158             {
159                 sqlBuffer.append(", ");
160             }
161             sqlBuffer.append(new QualifiedTableName(primaryKeys[i].getColumnName(), null, escapePattern).getQualifiedName());
162 
163         }
164 
165         return sqlBuffer.toString();
166     }
167 
168     /**
169      * Get all the table names form the database that are not system tables.
170      */
171     private void initialize() throws DataSetException
172     {
173         logger.debug("initialize() - start");
174 
175         if (_tableMap != null)
176         {
177             return;
178         }
179 
180         try
181         {
182             logger.debug("Initializing the data set from the database...");
183 
184             Connection jdbcConnection = _connection.getConnection();
185             DatabaseMetaData databaseMetaData = jdbcConnection.getMetaData();
186 
187             String schema = _connection.getSchema();
188 
189             if(SQLHelper.isSybaseDb(jdbcConnection.getMetaData()) && !jdbcConnection.getMetaData().getUserName().equals(schema) ){
190                 logger.warn("For sybase the schema name should be equal to the user name. " +
191                         "Otherwise the DatabaseMetaData#getTables() method might not return any columns. " +
192                 "See dbunit tracker #1628896 and http://issues.apache.org/jira/browse/TORQUE-40?page=all");
193             }
194 
195             DatabaseConfig config = _connection.getConfig();
196             String[] tableType = (String[])config.getProperty(DatabaseConfig.PROPERTY_TABLE_TYPE);
197             IMetadataHandler metadataHandler = (IMetadataHandler) config.getProperty(DatabaseConfig.PROPERTY_METADATA_HANDLER);
198 
199             ResultSet resultSet = metadataHandler.getTables(databaseMetaData, schema, tableType);
200 
201             if(logger.isDebugEnabled())
202             {
203                 logger.debug(SQLHelper.getDatabaseInfo(jdbcConnection.getMetaData()));
204                 logger.debug("metadata resultset={}", resultSet);
205             }
206 
207             try
208             {
209                 OrderedTableNameMap tableMap = super.createTableNameMap();
210                 while (resultSet.next())
211                 {
212                     String schemaName = metadataHandler.getSchema(resultSet);
213                     String tableName = resultSet.getString(3);
214 
215                     if(_tableFilter != null && !_tableFilter.accept(tableName))
216                     {
217                         logger.debug("Skipping table '{}'", tableName);
218                         continue;
219                     }
220                     if(!_oracleRecycleBinTableFilter.accept(tableName))
221                     {
222                         logger.debug("Skipping oracle recycle bin table '{}'", tableName);
223                         continue;
224                     }
225 
226 
227                     QualifiedTableName qualifiedTableName = new QualifiedTableName(tableName, schemaName);
228                     tableName = qualifiedTableName.getQualifiedNameIfEnabled(config);
229 
230                     // Put the table into the table map
231                     tableMap.add(tableName, null);
232                 }
233 
234                 _tableMap = tableMap;
235             }
236             finally
237             {
238                 resultSet.close();
239             }
240         }
241         catch (SQLException e)
242         {
243             throw new DataSetException(e);
244         }
245     }
246 
247     ////////////////////////////////////////////////////////////////////////////
248     // AbstractDataSet class
249 
250     protected ITableIterator createIterator(boolean reversed)
251     throws DataSetException
252     {
253         if(logger.isDebugEnabled())
254         {
255             logger.debug("createIterator(reversed={}) - start", String.valueOf(reversed));
256         }
257 
258         String[] names = getTableNames();
259         if (reversed)
260         {
261             names = DataSetUtils.reverseStringArray(names);
262         }
263 
264         return new DatabaseTableIterator(names, this);
265     }
266 
267     ////////////////////////////////////////////////////////////////////////////
268     // IDataSet interface
269 
270     public String[] getTableNames() throws DataSetException
271     {
272         initialize();
273 
274         return _tableMap.getTableNames();
275     }
276 
277     public ITableMetaData getTableMetaData(String tableName) throws DataSetException
278     {
279         logger.debug("getTableMetaData(tableName={}) - start", tableName);
280 
281         initialize();
282 
283         // Verify if table exist in the database
284         if (!_tableMap.containsTable(tableName))
285         {
286             logger.error("Table '{}' not found in tableMap={}", tableName,
287                     _tableMap);
288             throw new NoSuchTableException(tableName);
289         }
290 
291         // Try to find cached metadata
292         ITableMetaData metaData = (ITableMetaData)_tableMap.get(tableName);
293         if (metaData != null)
294         {
295             return metaData;
296         }
297 
298         // Create metadata and cache it
299         metaData = new DatabaseTableMetaData(tableName, _connection, true, super.isCaseSensitiveTableNames());
300         // Put the metadata object into the cache map
301         _tableMap.update(tableName, metaData);
302 
303         return metaData;
304     }
305 
306     public ITable getTable(String tableName) throws DataSetException
307     {
308         logger.debug("getTable(tableName={}) - start", tableName);
309 
310         initialize();
311 
312         try
313         {
314             ITableMetaData metaData = getTableMetaData(tableName);
315 
316             DatabaseConfig config = _connection.getConfig();
317             IResultSetTableFactory factory = (IResultSetTableFactory)config.getProperty(
318                     DatabaseConfig.PROPERTY_RESULTSET_TABLE_FACTORY);
319             return factory.createTable(metaData, _connection);
320         }
321         catch (SQLException e)
322         {
323             throw new DataSetException(e);
324         }
325     }
326 
327 
328     private static class OracleRecycleBinTableFilter implements ITableFilterSimple
329     {
330         private final DatabaseConfig _config;
331 
332         public OracleRecycleBinTableFilter(DatabaseConfig config)
333         {
334             this._config = config;
335         }
336 
337         public boolean accept(String tableName) throws DataSetException
338         {
339             // skip oracle 10g recycle bin system tables if enabled
340             if(_config.getFeature(DatabaseConfig.FEATURE_SKIP_ORACLE_RECYCLEBIN_TABLES)) {
341                 // Oracle 10g workaround
342                 // don't process system tables (oracle recycle bin tables) which
343                 // are reported to the application due a bug in the oracle JDBC driver
344                 if (tableName.startsWith("BIN$"))
345                 {
346                     return false;
347                 }
348             }
349 
350             return true;
351         }
352     }
353 
354 }