27

我有以下休眠查询:

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

记录在 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);

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

另一方面,如果我使用直接 jdbc,如下所示:

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

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

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

我宁愿使用休眠版本,但不能接受执行时间。

添加信息:添加休眠日志记录后,我看到

[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)

后跟以下 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]

随后是 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]

这是什么意思?

Hibernate 在第一个实现中做了什么,我怎样才能知道?

4

8 回答 8

32

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

于 2011-07-08T09:07:20.487 回答
6

根据新信息,我觉得我应该提供另一个答案。不同之处在于您为 bean 中的 List 或 Set 属性指定了一对多关联。

您可能正在指定lazy=false将关闭延迟加载的选项。关闭延迟加载后,它将获取每个MyHibernateClass实体的每个关联记录,这就是为什么它需要这么长时间才能执行。

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

于 2011-07-07T13:59:18.090 回答
5

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

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

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

于 2011-07-07T11:37:30.077 回答
4

我知道这个线程很旧,但是为了更新,我遇到了同样的问题,但是使用 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

实际驱动程序发送查询:

@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

注意:我通过 SQL Profiler Tool 获得的这个查询直接在 DB 上侦听

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

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

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

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

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

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

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

设置后,我又遇到了一个问题,发送的查询就像

 Select * from customer c where c.name like **N**'somename' and c.country=**N**'somevalue'

这里在值之前有一个前缀,说明要转换编码方案,所以我禁用 JDBC url 来 sendUnicode = false

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

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

于 2015-01-01T00:52:06.113 回答
2

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

    Long count = getStoreCount(customerId);

    Query query = session.getNamedQuery("hqlGetStoresByCustomerId")
            .setString("i_customerid",customerId)
            .setFetchSize(count.intValue());

这样做时要小心;我的数据集大约有 100 行,它的范围是一个 Web 请求的生命周期。如果您有更大的数据集,那么您将在该数据存在期间使用 Java Heap,然后再将其返回到 Java Heap。

于 2015-07-22T13:40:08.310 回答
2

遇到 SQL Server 类似问题的其他任何人都可以sendStringParametersAsUnicode=false在 JDBC 查询字符串中使用,如以下答案所示:

准备好的语句的 JPA (Hibernate) 本机查询慢

如果您没有为准备好的语句参数使用 Unicode,并且想要利用varchar您正在使用的字段上的索引作为准备好的语句的参数,这会有所帮助。

于 2020-05-28T06:41:59.410 回答
1

我知道这是一个老问题,但这是为我解决的问题......

在你的 hibernate.cfg.xml 确保你有正确的 !DOCTYPE... 它应该如下:

<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
于 2015-06-18T21:01:18.587 回答
-1

在我发现 DOCTYPE 标签写错之前,我花了 10 秒执行一个简单的全选hibernate.cfg.xml查询*mapping object*.hbm.class

确保hibernate.cfg.xml

<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

并将 xml.class 映射为

<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

现在我花了 1-2 秒来执行任何查询。

于 2015-09-27T22:10:22.117 回答