【问题标题】:why the method oracle.jdbc.driver.OracleStatement.getColumnIndex() costs so much time?为什么 oracle.jdbc.driver.OracleStatement.getColumnIndex() 方法花费这么多时间?
【发布时间】:2016-08-24 05:22:14
【问题描述】:

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

this.getTestDao().getHibernateTemplate().find("来自测试 where testId

线程转到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() 10000 条数据需要 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

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

【问题讨论】:

  • 那么发布一些代码怎么样,或者我们是否打算猜测答案?
  • 不要使用HIbernateTemplate,不再需要这样做。同样在 sql developer 中执行查询并将其与在 hibernate 中获得结果进行比较是比较苹果和橙子。例如,Hibernate 将每一行转换为一个对象(图)并创建对象有点慢,你说你有 201 个字段,10000 行,这意味着你正在创建 2.000.000 个对象(至少)。 Hibernate 不一定要很慢,但它通常会变慢,因为人们使用错误或错误的期望。
  • 我编码的只是HQL,然后它在Hibernate中调用了“find”方法,最后它进入了“getColumnIndex”方法。我再次编辑问题,希望它有所帮助。
  • 不,你做了更多,你创建了一个实体,你创建了一个映射。但是,您似乎不理解(或不承认)hibernate 比您的 sql 开发人员工具做得更多。这不仅仅是返回结果的问题,它会进行转换、类型检查并创建大量对象。还有 hibernate 3 你为什么还在使用它,最新的版本是 5.1,而 3 已经被废弃多年了。
  • 你为什么要使用这样一个过时的驱动程序? ojdbc14.jar 适用于 Java 1.4 - 你真的还在使用 Java 1.4 吗?你真的还在使用 Oracle 10 吗?

标签: java oracle hibernate jdbc


【解决方案1】:

我遇到了同样的问题。我们使用 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 的一些聪明人决定添加一个 CACHE:

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 驱动程序。新驱动程序与旧数据库兼容。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-11-15
    • 1970-01-01
    • 1970-01-01
    • 2017-11-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多