为什么方法oracle.jdbc.driver.OracleStatement.getColumnIndex()花费这么多时间?

时间:2016-04-29 07:05:27

标签: java oracle hibernate jdbc

我有一个实体有201个字段(testId,test1 ... test200),id是long类型,其他是String。我用HQL在Hibernate中搜索了它

  

this.getTestDao()。getHibernateTemplate()。find(“from test where testId< = 10000”)

线程转到 spring-hibernate3.jar 然后

//Method from spring-hibernate3.jar
//org.springframework.orm.hibernate3.HibernateTemplate
public List find(final String queryString, final Object values[])
        throws DataAccessException
    {
        return (List)execute(new HibernateCallback() {

        public Object doInHibernate(Session session)
            throws HibernateException
        {
            Query queryObject = session.createQuery(queryString);
            prepareQuery(queryObject);
            if(values != null)
            {
                for(int i = 0; i < values.length; i++)
                    queryObject.setParameter(i, values[i]);

            }
            return queryObject.list();
        }
    }
, true);
    }

但Java VisualVm(JDK中的一个监控软件)告诉我,方法 oracle.jdbc.driver.OracleStatement.getColumnIndex()为10,000个数据花费4404ms 我知道Hibernate很慢,但实际上是不可接受的,因为在sqldeveloper中使用相同的SQL只花费55ms。

我确信没有打印任何异常且每个fileld都是合法的。还有反编译代码 oracle.jdbc.driver.OracleStatement.getColumnIndex

//Method from oracle10.2 jdbc14.jar
// oracle.jdbc.driver.OracleStatement
int getColumnIndex(String s)
    throws SQLException
{
    if(!describedWithNames)
        synchronized(connection)
        {
            synchronized(this)
            {
                connection.needLine();
                doDescribe(true);
                described = true;
                describedWithNames = true;
            }
        }
    for(int i = 0; i < numberOfDefinePositions; i++)
        if(accessors[i].columnName.equalsIgnoreCase(s))
            return i + 1;

    DatabaseError.throwSqlException(6);
    return 0;
}

来自显示器的图像 click to view

谢谢大家,感谢指出语法错误,如果存在的话。

1 个答案:

答案 0 :(得分:0)

我遇到了同样的问题。我们使用 Hibernate 将一个非常“宽”的表(~200 列)加载到对象中。对于结果集中的每一行,Hibernate 将尝试“提取”每一列中的值使用其名称。为此,它调用“OracleStatement.getColumnIndex”方法(见上文)。这种愚蠢的方法通过遍历所有字段字段来查找索引...尝试查看列的名称是否与输入中的名称匹配。

所以如果有 200 列和 100 行。而“getColumnIndex”方法必须遍历列的一半(平均)才能找到它要查找的列,然后...

200 * 100 * 100 = 2,000,000 次字符串比较操作...

好像结果集中列的名称发生了变化!!!

但这是旧版 oracle 中的代码......准确地说是“ojdbj6.jar”(12.1.0.2.0)。所以我反编译了“ojdbc8.jar”(18.3.0.0.0)以查看是否有任何变化......它确实发生了。Oracle 的一些聪明人决定添加一个缓存:

int getColumnIndex(String paramString) throws SQLException { 
    ensureOpen(); 
    Integer integer = (Integer)this.columnNameCache.get(paramString); 
    if (integer == null) { 
        integer = Integer.valueOf(getColumnIndexPrimitive(paramString)); 
        if (this.columnNameCache.size() <= this.accessors.length) 
            this.columnNameCache.put(paramString, integer);  
    }  
    return integer.intValue(); 
}  

所以@a_horse_with_no_name 说你的问题是 Oracle 驱动程序很旧时,它有点正确。

更新

对于我的应用程序,我做了一个测试,将旧的 (11.2.0.3) 驱动程序与较新的 (18.3.0.0) 驱动程序的性能进行了比较。此测试执行一项操作,该操作对“宽”表执行 Hibernate 查询并滚动结果,同时将结果转换为 XML。使用新驱动程序,运行时间快了 4 倍。如果我可以从等式中消除 XML 逻辑,那么性能改进会更加显着。

底线:

如果您必须使用 Hibernate 对“宽”表或结果中有很多行的表进行查询,请确保使用最新的 JDBC 驱动程序。新驱动程序与旧数据库兼容。