org.dbunit.database.ResultSetTableMetaData Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of xmlc-all-runtime Show documentation
Show all versions of xmlc-all-runtime Show documentation
old 2002 version of xmlc-xmlc-all-runtime
The newest version!
/*
*
* The DbUnit Database Testing Framework
* Copyright (C)2002-2008, DbUnit.org
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
package org.dbunit.database;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import org.dbunit.dataset.AbstractTableMetaData;
import org.dbunit.dataset.Column;
import org.dbunit.dataset.DataSetException;
import org.dbunit.dataset.DefaultTableMetaData;
import org.dbunit.dataset.datatype.DataType;
import org.dbunit.dataset.datatype.DataTypeException;
import org.dbunit.dataset.datatype.IDataTypeFactory;
import org.dbunit.util.SQLHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* {@link ResultSet} based {@link org.dbunit.dataset.ITableMetaData} implementation.
*
* The lookup for the information needed to create the {@link Column} objects is retrieved
* in two phases:
*
* - Try to find the information from the given {@link ResultSet} via a {@link DatabaseMetaData}
* object. Therefore the {@link ResultSetMetaData} is used to get the catalog/schema/table/column
* names which in turn are used to get column information via
* {@link DatabaseMetaData#getColumns(String, String, String, String)}. The reason for this is
* that the {@link DatabaseMetaData} is more precise and contains more information about columns
* than the {@link ResultSetMetaData} does. Another reason is that some JDBC drivers (currently known
* from MYSQL driver) provide an inconsistent implementation of those two MetaData objects
* and the {@link DatabaseMetaData} is hence considered to be the master by dbunit.
*
* -
* Since some JDBC drivers (one of them being Oracle) cannot (or just do not) provide the
* catalog/schema/table/column values on a {@link ResultSetMetaData} instance the second
* step will create the dbunit {@link Column} using the {@link ResultSetMetaData} methods
* directly (for example {@link ResultSetMetaData#getColumnType(int)}. (This is also the way
* dbunit worked until the 2.4 release)
*
*
*
*
* @author gommma (gommma AT users.sourceforge.net)
* @author Last changed by: $Author$
* @version $Revision$ $Date$
* @since 2.3.0
*/
public class ResultSetTableMetaData extends AbstractTableMetaData
{
/**
* Logger for this class
*/
private static final Logger logger = LoggerFactory.getLogger(DatabaseTableMetaData.class);
/**
* The actual table metadata
*/
private DefaultTableMetaData wrappedTableMetaData;
private boolean _caseSensitiveMetaData;
/**
* @param tableName The name of the database table
* @param resultSet The JDBC result set that is used to retrieve the columns
* @param connection The connection which is needed to retrieve some configuration values
* @param caseSensitiveMetaData Whether or not the metadata is case sensitive
* @throws DataSetException
* @throws SQLException
*/
public ResultSetTableMetaData(String tableName,
ResultSet resultSet, IDatabaseConnection connection, boolean caseSensitiveMetaData)
throws DataSetException, SQLException
{
super();
_caseSensitiveMetaData = caseSensitiveMetaData;
this.wrappedTableMetaData = createMetaData(tableName, resultSet, connection);
}
/**
* @param tableName The name of the database table
* @param resultSet The JDBC result set that is used to retrieve the columns
* @param dataTypeFactory
* @param caseSensitiveMetaData Whether or not the metadata is case sensitive
* @throws DataSetException
* @throws SQLException
* @deprecated since 2.4.4. use {@link ResultSetTableMetaData#ResultSetTableMetaData(String, ResultSet, IDatabaseConnection, boolean)}
*/
public ResultSetTableMetaData(String tableName,
ResultSet resultSet, IDataTypeFactory dataTypeFactory, boolean caseSensitiveMetaData)
throws DataSetException, SQLException
{
super();
_caseSensitiveMetaData = caseSensitiveMetaData;
this.wrappedTableMetaData = createMetaData(tableName, resultSet, dataTypeFactory, new DefaultMetadataHandler());
}
private DefaultTableMetaData createMetaData(String tableName,
ResultSet resultSet, IDatabaseConnection connection)
throws SQLException, DataSetException
{
if (logger.isTraceEnabled())
logger.trace("createMetaData(tableName={}, resultSet={}, connection={}) - start",
new Object[] { tableName, resultSet, connection });
DatabaseConfig dbConfig = connection.getConfig();
IMetadataHandler columnFactory = (IMetadataHandler)dbConfig.getProperty(DatabaseConfig.PROPERTY_METADATA_HANDLER);
IDataTypeFactory typeFactory = super.getDataTypeFactory(connection);
return createMetaData(tableName, resultSet, typeFactory, columnFactory);
}
private DefaultTableMetaData createMetaData(String tableName,
ResultSet resultSet, IDataTypeFactory dataTypeFactory, IMetadataHandler columnFactory)
throws DataSetException, SQLException
{
if (logger.isTraceEnabled())
logger.trace("createMetaData(tableName={}, resultSet={}, dataTypeFactory={}, columnFactory={}) - start",
new Object[]{ tableName, resultSet, dataTypeFactory, columnFactory });
Connection connection = resultSet.getStatement().getConnection();
DatabaseMetaData databaseMetaData = connection.getMetaData();
ResultSetMetaData metaData = resultSet.getMetaData();
Column[] columns = new Column[metaData.getColumnCount()];
for (int i = 0; i < columns.length; i++)
{
int rsIndex = i+1;
// 1. try to create the column from the DatabaseMetaData object. The DatabaseMetaData
// provides more information and is more precise so that it should always be used in
// preference to the ResultSetMetaData object.
columns[i] = createColumnFromDbMetaData(metaData, rsIndex, databaseMetaData, dataTypeFactory, columnFactory);
// 2. If we could not create the Column from a DatabaseMetaData object, try to create it
// from the ResultSetMetaData object directly
if(columns[i] == null)
{
columns[i] = createColumnFromRsMetaData(metaData, rsIndex, tableName, dataTypeFactory);
}
}
return new DefaultTableMetaData(tableName, columns);
}
private Column createColumnFromRsMetaData(ResultSetMetaData rsMetaData,
int rsIndex, String tableName, IDataTypeFactory dataTypeFactory)
throws SQLException, DataTypeException
{
if(logger.isTraceEnabled()){
logger.trace("createColumnFromRsMetaData(rsMetaData={}, rsIndex={}," +
" tableName={}, dataTypeFactory={}) - start",
new Object[]{rsMetaData, String.valueOf(rsIndex),
tableName, dataTypeFactory});
}
int columnType = rsMetaData.getColumnType(rsIndex);
String columnTypeName = rsMetaData.getColumnTypeName(rsIndex);
String columnName = rsMetaData.getColumnLabel(rsIndex);
int isNullable = rsMetaData.isNullable(rsIndex);
DataType dataType = dataTypeFactory.createDataType(
columnType, columnTypeName, tableName, columnName);
Column column = new Column(
columnName,
dataType,
columnTypeName,
Column.nullableValue(isNullable));
return column;
}
/**
* Try to create the Column using information from the given {@link ResultSetMetaData}
* to search the column via the given {@link DatabaseMetaData}. If the
* {@link ResultSetMetaData} does not provide the required information
* (one of catalog/schema/table is "")
* the search for the Column via {@link DatabaseMetaData} is not executed and null
* is returned immediately.
* @param rsMetaData The {@link ResultSetMetaData} from which to retrieve the {@link DatabaseMetaData}
* @param rsIndex The current index in the {@link ResultSetMetaData}
* @param databaseMetaData The {@link DatabaseMetaData} which is used to lookup detailed
* information about the column if possible
* @param dataTypeFactory dbunit {@link IDataTypeFactory} needed to create the Column
* @param metadataHandler the handler to be used for {@link DatabaseMetaData} handling
* @return The column or null
if it can be not created using a
* {@link DatabaseMetaData} object because of missing information in the
* {@link ResultSetMetaData} object
* @throws SQLException
* @throws DataTypeException
*/
private Column createColumnFromDbMetaData(ResultSetMetaData rsMetaData, int rsIndex,
DatabaseMetaData databaseMetaData, IDataTypeFactory dataTypeFactory,
IMetadataHandler metadataHandler)
throws SQLException, DataTypeException
{
if(logger.isTraceEnabled()){
logger.trace("createColumnFromMetaData(rsMetaData={}, rsIndex={}," +
" databaseMetaData={}, dataTypeFactory={}, columnFactory={}) - start",
new Object[]{rsMetaData, String.valueOf(rsIndex),
databaseMetaData, dataTypeFactory, metadataHandler});
}
// use DatabaseMetaData to retrieve the actual column definition
String catalogName = rsMetaData.getCatalogName(rsIndex);
String schemaName = rsMetaData.getSchemaName(rsIndex);
String tableName = rsMetaData.getTableName(rsIndex);
String columnName = rsMetaData.getColumnLabel(rsIndex);
// Due to a bug in the DB2 JDBC driver we have to trim the names
catalogName = trim(catalogName);
schemaName = trim(schemaName);
tableName = trim(tableName);
columnName = trim(columnName);
// Check if at least one of catalog/schema/table attributes is
// not applicable (i.e. "" is returned). If so do not try
// to get the column metadata from the DatabaseMetaData object.
// This is the case for all oracle JDBC drivers
if(catalogName != null && catalogName.equals("")) {
// Catalog name is not required
catalogName = null;
}
if(schemaName != null && schemaName.equals("")) {
logger.debug("The 'schemaName' from the ResultSetMetaData is empty-string and not applicable hence. " +
"Will not try to lookup column properties via DatabaseMetaData.getColumns.");
return null;
}
if(tableName != null && tableName.equals("")) {
logger.debug("The 'tableName' from the ResultSetMetaData is empty-string and not applicable hence. " +
"Will not try to lookup column properties via DatabaseMetaData.getColumns.");
return null;
}
if(logger.isDebugEnabled())
logger.debug("All attributes from the ResultSetMetaData are valid, " +
"trying to lookup values in DatabaseMetaData. catalog={}, schema={}, table={}, column={}",
new Object[]{catalogName, schemaName, tableName, columnName} );
// All of the retrieved attributes are valid,
// so lookup the column via DatabaseMetaData
ResultSet columnsResultSet = metadataHandler.getColumns(databaseMetaData, schemaName, tableName);
try
{
// Scroll resultset forward - must have one result which exactly matches the required parameters
scrollTo(columnsResultSet, metadataHandler, catalogName, schemaName, tableName, columnName);
Column column = SQLHelper.createColumn(columnsResultSet, dataTypeFactory, true);
return column;
}
catch(IllegalStateException e)
{
logger.warn("Cannot find column from ResultSetMetaData info via DatabaseMetaData. Returning null." +
" Even if this is expected to never happen it probably happened due to a JDBC driver bug." +
" To get around this you may want to configure a user defined " + IMetadataHandler.class, e);
return null;
}
finally
{
SQLHelper.close(columnsResultSet);
}
}
/**
* Trims the given string in a null-safe way
* @param value
* @return
* @since 2.4.6
*/
private String trim(String value)
{
return (value==null ? null : value.trim());
}
private void scrollTo(ResultSet columnsResultSet, IMetadataHandler metadataHandler,
String catalog, String schema, String table, String column)
throws SQLException
{
while(columnsResultSet.next())
{
boolean match = metadataHandler.matches(columnsResultSet, catalog, schema, table, column, _caseSensitiveMetaData);
if(match)
{
// All right. Return immediately because the resultSet is positioned on the correct row
return;
}
}
// If we get here the column could not be found
String msg =
"Did not find column '" + column +
"' for '" + schema + "." + table +
"' in catalog '" + catalog + "' because names do not exactly match.";
throw new IllegalStateException(msg);
}
public Column[] getColumns() throws DataSetException {
return this.wrappedTableMetaData.getColumns();
}
public Column[] getPrimaryKeys() throws DataSetException {
return this.wrappedTableMetaData.getPrimaryKeys();
}
public String getTableName() {
return this.wrappedTableMetaData.getTableName();
}
public String toString()
{
StringBuffer sb = new StringBuffer();
sb.append(getClass().getName()).append("[");
sb.append("wrappedTableMetaData=").append(this.wrappedTableMetaData);
sb.append("]");
return sb.toString();
}
}