简单的hibernate查询返回非常慢

时间:2011-07-07 11:08:51

标签: java hibernate objectinstantiation

我有以下hibernate查询:

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代码时,这需要大约7000ms。

如果我在另一方面使用直接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,如示例中所示。我只需要该类的只读实例,并且不需要将其附加到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)

后面跟着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在第一次实现中做了什么,我该如何找到?

8 个答案:

答案 0 :(得分:28)

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

答案 1 :(得分:6)

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

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

尝试设置lazy=true,这样可以更快地执行,然后只在从实体明确请求时检索关联的实体。

答案 2 :(得分:4)

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

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

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

答案 3 :(得分:4)

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

的查询

Hibernate查询:

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工具直接侦听DB

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

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

  1. 将我的HQL更改为本机查询并为某些参数添加OPTION RECOMPILE

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

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

  4. 我不想使用OPTION 1&amp; 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驱动程序选项中所做的一切..就我现在所关注的而言,应用程序启动并运行得很快。我还引入了二级缓存来缓存它一段时间..

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

答案 4 :(得分: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">

答案 5 :(得分:1)

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

    Long count = getStoreCount(customerId);

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

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

答案 6 :(得分:1)

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

JPA (Hibernate) Native Query for Prepared Statement SLOW

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

答案 7 :(得分:-1)

在我发现在hibernate.cfg.xml*mapping object*.hbm.class

中错误地写入了DOCTYPE标记之前,我花了10秒钟执行一个简单的全选查询

确保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秒执行任何查询。