项目的单元测试用到了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.