简单的hibernate查询返回非常慢

Try*_*rym 27 java hibernate objectinstantiation

我有以下hibernate查询:

Query query = session.createQuery("from MyHibernateClass");
List<MyHibernateClass> result = query.list();// executes in 7000ms
Run Code Online (Sandbox Code Playgroud)

当我在MySQL中记录正在执行的sql时,我看到了

select 
  myhibernat0_.myFirstColumn as myfirstcolumn92_, 
  myhibernat0_.mySecondColumn as mysecondcolumn92_, 
  myhibernat0_.mythirdcolumn as mythirdcolumn92_, 
  myhibernat0_.myFourthColumn as myfourthcolumn92_ 
from MyHibernateClass myhibernat0_ 
where (1=1);
Run Code Online (Sandbox Code Playgroud)

当在MyHibernateClass数据库表中的3500行的小数据集上测量jvm中的java代码时,这需要大约7000ms.

如果我在另一方面使用直接jdbc如下:

Statement statement = session.connection().createStatement();
ResultSet rs = statement.executeQuery("select * from MyHibernateClass");// 7ms
List<MyHibernateClass> result = convert(rs);// executes in 20ms
Run Code Online (Sandbox Code Playgroud)

我看到相同的sql进入数据库,但现在jvm中java代码的花费时间是7ms.

MyHibernateClass是一个带有getter和setter的简单java bean类,我没有使用特殊的resulttransformers,如示例中所示.我只需要该类的只读实例,并且不需要附加到hibernate会话.

我宁愿使用hibernate版本但不能接受执行时间.

补充信息:添加hibernate日志后我看到了

[2011-07-07 14:26:26,643]DEBUG [main] [logid: ] - 
  org.hibernate.jdbc.AbstractBatcher.logOpenResults(AbstractBatcher.java:426) - 
  about to open ResultSet (open ResultSets: 0, globally: 0)
Run Code Online (Sandbox Code Playgroud)

接下来是3500条以下日志语句

[2011-07-07 14:26:26,649]DEBUG [main] [logid: ] - 
  org.hibernate.loader.Loader.getRow(Loader.java:1197) - 
  result row: EntityKey[com.mycom.MyHibernateClass#1]
Run Code Online (Sandbox Code Playgroud)

其次是3500日志语句,如

[2011-07-07 14:27:06,789]DEBUG [main] [logid: ] - 
  org.hibernate.engine.TwoPhaseLoad.initializeEntity(TwoPhaseLoad.java:130) - 
  resolving associations for [com.mycom.MyHibernateClass#1]
[2011-07-07 14:27:06,792]DEBUG [main] [logid: ] - 
  org.hibernate.engine.TwoPhaseLoad.initializeEntity(TwoPhaseLoad.java:226) - 
  done materializing entity [com.mycom.MyHibernateClass#1]
Run Code Online (Sandbox Code Playgroud)

这是什么意思?

Hibernate在第一次实现中做了什么,我该如何找到?

Try*_*rym 30

添加具有类的所有属性的构造函数就可以了,现在hibernate查询的执行时间是70ms.以前,该类只有一个没有参数的默认构造函数和一个带有实体id参数的构造函数.

  • 所以......在这种情况下,hibernate调用no-arg构造函数表单,然后调用setter方法,对于3500个对象,并且它需要花费1000倍的时间? (7认同)
  • 有意思......我没有意识到bean构造函数是那么重要.如果找不到合适的构造函数,Hibernate可能会尝试通过反射来查找bean属性.这可能解释了糟糕的表现.很好找到并感谢您发布答案! (3认同)
  • 为了后人的缘故,Hibernate不会使用除了arg形式之外的任何构造函数,除非你特别要求它,ala:`从MyHibernateClass中选择新的MyHibernateClass(...) (2认同)

map*_*aft 6

根据新的信息,我觉得我应该提供另一个答案.区别似乎是您为bean中的List或Set属性指定了一对多关联.

您可能正在指定lazy=false哪个将关闭延迟加载.关闭延迟加载后,它将获取每个MyHibernateClass实体的每个相关记录,这就是执行这么长时间的原因.

尝试设置lazy=true,这将执行得更快,然后只在从实体明确请求它们时检索关联的实体.


map*_*aft 5

如果在应用程序中使用Log4j,则可以设置特定于Hibernate的各种不同的日志记录选项,以更好地了解Hibernate幕后发生的事情。

http://docs.jboss.org/hibernate/core/3.3/reference/en/html/session-configuration.html#configuration-logging

我的猜测是,这是在应用程序中首次调用HQL查询时发生的典型初始加载时间。在第一个查询之后,随后的HQL查询应该明显且明显更快。


小智 5

我知道这个线程很旧,但是要更新,我遇到了同样的问题,但是对于 SQL Server,事实证明 Hibernate 打印的 SQL 和使用驱动程序发送的 SQL 是不同的。默认情况下,使用 MSSQL 驱动程序将查询作为 RPC 调用的存储过程发送,这是因为驱动程序尝试优化 MSSQL 标准的查询计划,因此它发送类似的查询

休眠查询:

select c.col1,c.col2 from customer c where c.name like @param1 and c.country like @param2
Run Code Online (Sandbox Code Playgroud)

实际驱动程序发送查询:

@param1=somevalue, @param2=somevalue 
declar sp ....

  select c.col1,c.col2 from customer c where c.name like @param1 and c.country like @param2
go
Run Code Online (Sandbox Code Playgroud)

注意:这个查询是我通过 SQL Profiler Tool 直接监听数据库得到的

事实证明,MSSQL 上的 sp_exec 优化往往会产生良好的查询计划并被缓存,但这会导致“参数嗅探”,要了解有关此问题的更多信息,请阅读此处...

因此,为了克服这个问题,我有以下选择:

  1. 将我的 HQL 更改为本机查询并添加 OPTION RECOMPILE FOR SOME PARAM

  2. 使用直接查询值而不是准备好的语句,因此不会对参数值进行转换,并且驱动程序不会将查询修改为存储过程

  3. 更改驱动程序设置以不发送存储过程(这仍然很糟糕,因为现在 MSSQL 服务器中的查询计划将特定于该查询,这与选项:2 相同,但在代码之外)

我不想使用选项 1 和 2,因为这消除了使用 ORM 框架的全部目的,我现在最终使用选项 3

所以我更改了 JDBC URL 来发送选项prepareStatement=false

设置此选项后,我在发送查询时又遇到了一个问题

 Select * from customer c where c.name like **N**'somename' and c.country=**N**'somevalue'
Run Code Online (Sandbox Code Playgroud)

这里的值之前有一个前缀,表示要转换编码方案,因此我禁用 JDBC url 来 sendUnicode = false

这一切都是我在 JTDS 驱动程序选项中所做的。就我而言,现在应用程序启动并运行得很快。我还引入了二级缓存来缓存一段时间。

希望这对某人有帮助,如果您有任何好的建议请告诉我。


小智 5

我遇到过一个事件,我的应用程序总是使用查询结果集中的每一行。我发现通过使用下面的 setFetchSize 方法设置提取大小,速度提高了 40 倍。(性能改进包括添加计数查询。)

    Long count = getStoreCount(customerId);

    Query query = session.getNamedQuery("hqlGetStoresByCustomerId")
            .setString("i_customerid",customerId)
            .setFetchSize(count.intValue());
Run Code Online (Sandbox Code Playgroud)

执行此操作时要小心;我的数据集大约有 100 行,其范围仅限于 Web 请求的生命周期。如果您有较大的数据集,则在将数据返回到 Java 堆之前,您将在该数据存在期间占用 Java 堆。

  • `setFetchSize` 是我的可取之处。我想要获取大约 1000 行的 ID,但花费的时间太长了。将 fetchSize 设置为 200,时间从大约 20-30 秒缩短到大约 2-3 秒。 (2认同)