項目的單元測試用到了dbunit進行數據組裝,最近遇到了org.dbunit.database.AmbiguousTableNameException: XXXX數據庫表 問題,在網上查找了不少資料,最終還是通過同事的幫助(數據庫工程師)一起把問題解決。網上通常的解決方案是對connection增加schema問題即可解決,原因是不同的schema可能會遇到相同的表名,導致拋出錯誤。 但是這個問題好解決(schema設置爲null也可以解決),如下代碼: properties.put(DatabaseConfig.FEATURE_QUALIFIED_TABLE_NAMES, "true");databaseConnection.getConfig().setPropertiesByString(stringProperties) 即可解決。 (由於導入數據庫的一個xml裏有不同的schema,所有connction不能指定schema)
那問題是什麼呢? 由於是測試數據庫,最後把"XXXX數據庫表"刪除掉,問題解決。最後通過數據庫工程師分析,通過select * from dba_tables where owner='schmae' and table_name='XXXX數據庫表' 可以查詢出兩條記錄,可能是oracle數據庫的問題, 從而可推出dbunit源代碼裏(“罪魁禍首”代碼)應該是通過此sql語句進行預加載的,由於有兩條重複的表,導致報錯。所以數據庫工程師將此“垃圾表”清除掉,問題解決。
問題整理:
1.dbunit問題的核心代碼如下:
private void initialize() throws DataSetException
{
logger.debug("initialize() - start");
if (_tableMap != null)
{
return;
}
try
{
logger.debug("Initializing the data set from the database...");
Connection jdbcConnection = _connection.getConnection();
DatabaseMetaData databaseMetaData = jdbcConnection.getMetaData();
String schema = _connection.getSchema();
if(SQLHelper.isSybaseDb(jdbcConnection.getMetaData()) && !jdbcConnection.getMetaData().getUserName().equals(schema) ){
logger.warn("For sybase the schema name should be equal to the user name. " +
"Otherwise the DatabaseMetaData#getTables() method might not return any columns. " +
"See dbunit tracker #1628896 and http://issues.apache.org/jira/browse/TORQUE-40?page=all");
}
DatabaseConfig config = _connection.getConfig();
String[] tableType = (String[])config.getProperty(DatabaseConfig.PROPERTY_TABLE_TYPE);
IMetadataHandler metadataHandler = (IMetadataHandler) config.getProperty(DatabaseConfig.PROPERTY_METADATA_HANDLER);
ResultSet resultSet = metadataHandler.getTables(databaseMetaData, schema, tableType);
if(logger.isDebugEnabled())
{
logger.debug(SQLHelper.getDatabaseInfo(jdbcConnection.getMetaData()));
logger.debug("metadata resultset={}", resultSet);
}
try
{
OrderedTableNameMap tableMap = super.createTableNameMap();
while (resultSet.next())
{
String schemaName = metadataHandler.getSchema(resultSet);
String tableName = resultSet.getString(3);
if(_tableFilter != null && !_tableFilter.accept(tableName))
{
logger.debug("Skipping table '{}'", tableName);
continue;
}
if(!_oracleRecycleBinTableFilter.accept(tableName))
{
logger.debug("Skipping oracle recycle bin table '{}'", tableName);
continue;
}
QualifiedTableName qualifiedTableName = new QualifiedTableName(tableName, schemaName);
tableName = qualifiedTableName.getQualifiedNameIfEnabled(config);
// Put the table into the table map
tableMap.add(tableName, null);
}
_tableMap = tableMap;
}
finally
{
resultSet.close();
}
}
catch (SQLException e)
{
throw new DataSetException(e);
}
}
分析:
1.ResultSet resultSet = metadataHandler.getTables(databaseMetaData, schema, tableType);
oracle用戶下指定的schema的表可全部查詢處理(如果schema爲null,則可查詢所有權限的表)
2.tableMap.add(tableName,null)
此add方法會先將map裏是否有重複的schema.table ,如果有則會拋出
org.dbunit.database.AmbiguousTableNameException: XXXX數據庫表 異常。
3.通過select * from dba_tables where owner='schema' and table_name='XXXX數據庫表' 分析,有兩條記錄(一個大寫的表,一個是小寫的表),刪除掉一個解決。
刨根問底:
爲什麼會出現數據庫裏有兩條表的記錄呢? 上網查詢後得知,有個笨蛋用戶在建表時,通過雙引號建的表,比如 create table "XXX數據庫表",建好表後估計是發現查不到此表,然後又重新建了一個,最終導致Exception.