终极解决dbunit使用中产生的AmbiguousTableNameException错误

来源:互联网 发布:java多线程的实现 编辑:程序博客网 时间:2024/06/11 22:37
    近来很烦,为公司开发的项目中做一个工具,以配合公司的项目管理。该工具具体的功能,主要是用于导入导出数据,并用于系统安装或者升级补丁。我主要是使用了dbunit来实现,但在使用的过程中遇到了一个很难搞的问题,就是导入数据时有某些版本或者复杂的数据库环境下会报AmbiguousTableNameException的错误。
    上网查找资料,却发现资料少得可怜,搜索AmbiguousTableNameException,只找到四条记录,而且里面也没有怎么说解决办法。不过,只指出了一些原因,主要是dbunit中使用了schema,因此必须指定schema。之于,schema是什么,在哪里指定根本没有提到。请教了公司的同事,用过的不超过两个,曾遇到过这样的错误,但是似乎没有解决。同时也请教了schema是什么东西,每个人都有不同的解释,搞到一头雾水。
    最后,经理建议我还是读原代码自己想办法。经过几个小时的分析,终于有些头绪了。在dbunit的源代码中package org.dbunit.database中有一个DatabaseDataSet.java,发现其中的一个方法:
    private void initialize() throws DataSetException
    {
        if (_nameList != null)
        {
            return;
        }

        try
        {
            Connection jdbcConnection = _connection.getConnection();
            String schema = _connection.getSchema();
            String[] tableType = (String[])_connection.getConfig().getProperty(
                    DatabaseConfig.PROPERTY_TABLE_TYPE);

            DatabaseMetaData databaseMetaData = jdbcConnection.getMetaData();
            ResultSet resultSet = databaseMetaData.getTables(
                    null, schema, "%", tableType);

            try
            {
                List nameList = new ArrayList();
                while (resultSet.next())
                {
                    String schemaName = resultSet.getString(2);
                    String tableName = resultSet.getString(3);
//                    String tableType = resultSet.getString(4);
//                    System.out.println("schema=" + schemaName + ", table=" + tableName + ", type=" + tableType + "");
                    tableName = getQualifiedName(schemaName, tableName);

                    // prevent table name conflict
                    if (_tableMap.containsKey(tableName.toUpperCase()))
                    {
                        throw new AmbiguousTableNameException(tableName);
                    }
                    nameList.add(tableName);
                    _tableMap.put(tableName.toUpperCase(), null);
                }

                _nameList = nameList;
            }
            finally
            {
                resultSet.close();
            }
        }
        catch (SQLException e)
        {
            throw new DataSetException(e);
        }
    }

    里面的ResultSet resultSet = databaseMetaData.getTables(null, schema, "%", tableType);是指定了使用schema,而我的调用函数为:

        DBConnectionManager dbManager = new DBConnectionManager();
        Connection jdbcConnection = dbManager.getConnection(null);
        return new DatabaseConnection(jdbcConnection);
    这里构造实例时根据没有把schema传入,因此导致错误。因此,只须传入schame的值就行了。
    另外,schema究竟是什么呢?我找一个公司的一位资深oracle数据库人员,终于找到了答案。数据库schema,相当于方案,每个数据库实例,里面可能会有多个用户,每个用户可能都有自己的表,过程等object,那oracle就会为每个用户创建一个与用户名相同的方案,就相当于schema。
    因此,问题就解决了。使用以下代码,把用户名传入就解决问题了:
        DBConnectionManager dbManager = new DBConnectionManager();
        Connection jdbcConnection = dbManager.getConnection(null);
        Config myConfig = null;
        myConfig = Config.getInstance();
        String schame= myConfig.getValue("DB.USER");
        return new DatabaseConnection(jdbcConnection,schame);

     值得注意的是,schema要求一定要大写的。
     到现在,可以总结一下,为什么会出现AmbiguousTableNameException的错误了。主要是因为,在同一个数据库中,存在多个方案(名字不同),但方案则里面的表,视图,过程等可能大部分都是相同的。我们公司就是这样在一个库中创建多个不同的用户进行不同版本的安装测试的。因此,dbunit在导入时就会无法区分,那么就必须在open一个connection时必须指定一个schema,即方案,来正确区分相同的表名。