Hibernate + Java 性能较慢,但当我将 TOAD 与相同的本机 Oracle 查询一起使用时性能较快
Posted
技术标签:
【中文标题】Hibernate + Java 性能较慢,但当我将 TOAD 与相同的本机 Oracle 查询一起使用时性能较快【英文标题】:Slow performance on Hibernate + Java but fast when I use TOAD with the same native Oracle query 【发布时间】:2012-12-27 00:19:16 【问题描述】:我检测到 Oracle 上的休眠和本机查询存在性能问题。当我在 TOAD 上执行带有多个参数的复杂 SQL 查询时,我会以毫秒为单位得到结果。但是,当我使用 Hibernate 执行相同的查询时,这个时间会大大增加(最多四秒甚至更多)。
我的 SQL 查询相当复杂,返回一个唯一值(因此,问题与安装类所需的时间无关),它包含多个格式为“:nameParameter”的参数。此查询存储在字符串中。例如,
String myNamedNativeQuery = "select count(*) from tables "+
"where column1 = :nameParameter1 "+
"and column2 = :nameParameter2";
//actually my sentence is much more complex!!
当我在 TOAD 上执行句子时,它会在几毫秒内得到解决。但是在 Hibernate 中使用这句话
SQLQuery query = session.createSQLQuery("myNamedNativeQuery");
query.setParameter(nameParameter1, value1);
query.setParameter(nameParameter2, value2);
query.uniqueResult();
需要几秒钟才能获得相同的结果。
我意识到如果我直接在本机查询上替换参数,然后使用 Hibernate 执行语句,时间会大大减少。应该是这样的:
String strQuery = session.getNamedQuery("myNamedNativeQuery").getQueryString();
myNamedNativeQuery = myNamedNativeQuery.replace("nameParameter1", value1);
myNamedNativeQuery = myNamedNativeQuery.replace("nameParameter2", value2);
SQLQuery query = session.createSQLQuery("myNamedNativeQuery");
query.uniqueResult();
有人知道发生了什么吗??
提前致谢。
PS:Oracle版本是9i和Hibernate 3.2
【问题讨论】:
在休眠时打开“show sql”选项。它会向您显示 hibernate 正在运行的查询,并让您深入了解为什么它需要这么长时间。 【参考方案1】:我认为这段代码发生了什么:
SQLQuery query = session.createSQLQuery("myNamedNativeQuery");
query.setParameter(nameParameter1, value1);
query.setParameter(nameParameter2, value2);
query.uniqueResult();
这是:
在第 1 行:根据命名参数的一些预期值创建查询计划。
在第 4 行:使用 value1 和 value2 执行查询,但这些值不是第 1 行详细说明的查询计划的“好值”,因此,数据库正在为实际值执行一个非常不合适的计划而且需要很多时间。
为什么?
查看HibernateSessionImpl.createSQLQuery(...)
的源码找到了这行代码:
SQLQueryImpl query = new SQLQueryImpl(
sql,
this,
factory.getQueryPlanCache().getSQLParameterMetadata( sql )
);
使用一些参数元数据调用getQueryPlanCache()
。我认为这个元数据不够好。
【讨论】:
有趣...那么,在这些情况下,最佳做法是什么?也许在调用 createSQLQuery() 之前用它们的值替换参数? 也许自适应游标共享会有所帮助。但该功能直到 11g 才可用。 根据我的测试,这种情况也使用 session.getNamedQuery('myNamedQuery') 发生。似乎 Hibernate 正在缓存查询计划,并且使用某些参数选择的查询计划不是最好的。那么,是否可以配置查询计划缓存? 您写道“时间急剧减少”,所以我不明白您为什么现在说您的第二个解决方案效率不高?你能详细说明一下吗? 在我的项目中,我有很多 HQL 和本地 SQL 命名查询存储到 XML 中。它们中的大多数被执行为 session.getNamedQuery('namedQuery').setParameter(...).uniqueResult();如果为命名查询计算查询计划,然后我执行相同的命名查询,但使用其他参数,查询计划不可能是最好的,可以吗?那么,是不是可以用参数值来细化查询计划呢?【参考方案2】:我给你的答案是:
移除所有绑定参数,使用 StatelessSession 代替 Session
使用 SQLQuery 代替包含参数值的完整 SQL 查询
StatelessSession session = sessionFactory.openStatelessSession();
我有类似的问题,直到我得到更好的解决方案,这就是我设法使它工作的原因。 见Hibernate parameterized sql query slow and active oracle sessions
【讨论】:
【参考方案3】:<property name = "hibernate.temp.use_jdbc_metadata_defaults">false</property>
将此添加到您的 hibernate.cfg.xml 或更新您的应用程序属性文件。
【讨论】:
以上是关于Hibernate + Java 性能较慢,但当我将 TOAD 与相同的本机 Oracle 查询一起使用时性能较快的主要内容,如果未能解决你的问题,请参考以下文章
Java程序员注意——扼杀性能的 10 个常见 Hibernate 错误