28

I have the following hibernate query:

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

When logging the sql being executed in MySQL I see

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

When measurering the java code in the jvm on a small dataset of 3500 rows in MyHibernateClass database table this takes about 7000ms.

If I on the otherhand uses direct jdbc as follows:

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

I see the same sql going into the database but now the time spend in the java code in the jvm is 7ms.

The MyHibernateClass is a simple java bean class with getters and setters, I use no special resulttransformers as can be seen in the example. I only need a read-only instance of the class, and it doesn't need to be attached to the hibernate session.

I would rather like to use the hibernate version but cannot accept the execution times.

Added information: After adding hibernate logging I see

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

followed by 3500 of the following log statements

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

followed by 3500 log statements like

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

What does this mean?

What is Hibernate doing in the first implementation, how can I find out?

abhi
  • 1,760
  • 1
  • 24
  • 40
Trym
  • 681
  • 1
  • 6
  • 10
  • Why are you really worried about 7ms? ;) – bhagyas Jul 07 '11 at 11:24
  • 6
    Thanks for asking a clarifying question. I would rather like to use the hibernate version but the execution times of 7000ms is unacceptable. – Trym Jul 07 '11 at 11:36
  • I know this thread is old, but to update I ran into the same problem but with SQL Server and it turns out to be that SQL being printed by Hibernate and SQL Sent using the driver is different. Using MSSQL Driver by default send the queries as stored procedures as RPC calls – jamb Jan 01 '15 at 00:35

8 Answers8

33

Adding a constructor with all attributes of the class did the trick, now the execution times are 70ms for the hibernate query. Previously the class only had a default constructor without arguments and a constructor with the entity id argument.

Trym
  • 681
  • 1
  • 6
  • 10
  • 3
    Interesting... I didn't realize the bean constructor was that important. Perhaps Hibernate tries to find bean properties through reflection if it can't find an appropriate constructor. That might explain the poor performance. Good find and thank you for posting the answer! – maple_shaft Jul 08 '11 at 10:52
  • 2
    For posterity sake, Hibernate will not use any constructor(s) of your entity aside from the no-arg form unless you specifically ask it to, ala: `select new MyHibernateClass( ... ) from MyHibernateClass` – Steve Ebersole Nov 13 '12 at 19:57
  • 7
    so...hibernate in this case was calling the no-arg constructor form, then calling the setter methods, for 3500 objects, and it was taking 1000x as long? – rogerdpack Mar 05 '13 at 20:59
  • 1
    I tried this and it didn't work. I don't think it's using the all-attributes constructor. Help? – pete Feb 11 '15 at 00:15
  • 1
    What would happen if there is a mapped super class? still would this help? – Yasitha Waduge Jun 01 '16 at 10:39
  • It seems it doesn't work for me. Do you know where can I read about this feature. I mean when exactly hibernate will call constructor with arguments.? I was not able to find any documentation about it. – Vahe Harutyunyan Jun 30 '17 at 12:33
6

Based on the new information I felt I should provide another answer. The difference looks like that you have a one-to-many association specified for a List or Set property in your bean.

You are probably specifying that lazy=false which will turn off lazy loading. With lazy loading turned off it will fetch every associated record for every MyHibernateClass entity and this is why it is taking so long to execute.

Try setting lazy=true and this will perform much faster and then only retrieve the associated entities when explicitly requesting them from the entity.

maple_shaft
  • 10,435
  • 6
  • 46
  • 74
  • 1
    Thanks for your patience. MyHibernateClass contains attributes of type String, int, long and a single java enum attribute (hibernate annotated with @Enumerated(EnumType.STRING)) and no associations to other objects. – Trym Jul 07 '11 at 14:11
  • Do these object properties correspond with your `*.hbm.xml` file for this bean? This is strange. Perhaps it needs to resolve the Enum type and it is having trouble doing so? – maple_shaft Jul 07 '11 at 14:37
  • 1
    Thanks for your hints. By accident I added a constructor with all arguments and the default Hibernate object instantiation was running like a charm. – Trym Jul 08 '11 at 09:09
  • 1
    I am seeing the exact same behavior. I have a class that has 0 associations to any other objects, yet Hibernate is doing n+1 selects against it. You said that by adding a constructor with all the args solved the problem? That seems really strange. Worth a try I guess. – Kevin M Sep 19 '13 at 19:36
5

I know this thread is old, but to update I ran into the same problem but with SQL Server and it turns out to be that SQL being printed by Hibernate and SQL Sent using the driver is different. Using MSSQL Driver by default sends the queries as stored procedures as RPC calls it's because the driver tries to optimize the query plan for MSSQL Standards , so it sends the queries something like

Hibernate Query:

select c.col1,c.col2 from customer c where c.name like @param1 and c.country like @param2

Actual Driver Sent Query:

@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

Note: This Query I got through SQL Profiler Tool directly listening on DB

It turns out to be that sp_exec optimizations on the MSSQL tend to produce good Query plans that's get cached, but this would result in 'parameter sniffing' to know more about this problem read here...

So to overcome this I had following options:

  1. Change my HQL to native Queries and add OPTION RECOMPILE FOR SOME PARAM

  2. Use Direct query values instead of prepared statements so there will be no translation for param values and queries will not be modified as Stored Procedures by the Driver

  3. Change the driver settings to not send the stored procedures (this is still bad because now the query plans in MSSQL server will be specific to this query, this is same as Option:2 but outside the code)

I didn't want to use OPTION 1 & 2 since that eliminates the whole purpose of using ORM Frameworks and I end up using OPTION 3 for now

So I changed the JDBC URL to send option prepareStatement=false

After setting this I had one more problem the query being sent like

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

Here there is a prefix before the values which states that to convert the encoding scheme , so I disable the JDBC url to sendUnicode = false

This all I did in JTDS driver options.. As far as I am concerned now the application is up and running fast. I have also introduced second level caches to cache it for some time..

Hope this helps for someone, if you have any good suggestion please let me know.

jamb
  • 186
  • 1
  • 8
5

If you utilize Log4j in your application you can set a variety of different logging options specific to Hibernate to get a better picture of what is going on behind the scenes in Hibernate.

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

My guess is that this is the typical initial load time that occurs when first calling an HQL query in an application. Subsequent HQL queries should be noticeably and considerably faster after this first one.

maple_shaft
  • 10,435
  • 6
  • 46
  • 74
  • 1
    1. I have added output from the extra log information, logging seems to slow the system down. 2. I tried executing another hql query just before the one under test and it didn't make any difference. – Trym Jul 07 '11 at 12:39
  • 1
    Logging and running Hibernate in debug mode will slow it down. Try turning logging and debug off in your `hibernate.cfg.xml` file and see if that makes a difference. If not this then the only thing I can think of is that your application (unit test, app, etc..) is running on a VERY SLOW workstation or server. – maple_shaft Jul 07 '11 at 13:24
  • Both versions of the code are runnning in the same environment (incl. hardware), so I think the measurements are comparable. When using my "own" ResultTransformer execution times goes down to 2500ms, but this is still way to high. The provided measurements are without logging and debugging (excempt the hibernate log statements). – Trym Jul 07 '11 at 13:39
3

I had an incident where my application was always using every row in the result set of a query. I found a 40-fold increase in speed by setting my fetch size using the setFetchSize method below. (The performance improvement includes the addition of the count query.)

    Long count = getStoreCount(customerId);

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

Be careful while doing this; my data set had about 100 rows, and it was scoped to a the life of a web request. If you have larger data sets, you will be eating Java Heap for the duration of the existence of that data, prior to returning it to the Java Heap.

gregd
  • 41
  • 1
  • 1
    `setFetchSize` was my saving grace. I wanted to get the ID's of about 1000 rows, and it was taking just too long. Setting the fetchSize to 200, it went from about 20-30 seconds to about 2-3 seconds. – VitorMM Jul 10 '20 at 21:24
2

I know this is an old question but here is what fixed it for me...

In your hibernate.cfg.xml make sure you have the correct !DOCTYPE... it should be as follows:

<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
Jeffrey Hawkins
  • 269
  • 2
  • 2
2

Anyone else who is facing a similar issue with SQL Server can use sendStringParametersAsUnicode=false in the JDBC Query String as shown in this answer:

JPA (Hibernate) Native Query for Prepared Statement SLOW

If you're not using Unicode for your prepared statement parameters and want to utilize the index on the varchar field which you're using as a parameter for the prepared statement, this can help.

Raman
  • 1,221
  • 13
  • 20
0

It took me 10 seconds to execute a simple select all query before I found out that DOCTYPE tag is written wrongly in hibernate.cfg.xml and *mapping object*.hbm.class

Make sure that hibernate.cfg.xml start with

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

And mapping xml.class with

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

Now it took me 1-2 seconds to execute any queries.

Hendra Anggrian
  • 5,780
  • 13
  • 57
  • 97
  • I know i am asking late, but i followed ur points and still no improvments in the query. i am calling native sql query and it is just a simple sql and it is taking lot of time to fetch result. – Ranjeet SIngh Apr 26 '16 at 13:07
  • @RanjeetSIngh if I remember correctly and if there is no significant improvement on Hibernate since the answer is submitted, it is normal for Hibernate to take several seconds to open a connection. Once opened, executing query shouldn't take more than 1-2 seconds. Check back in your code, perhaps you open up and close a connection every query execution. If that's the case, you should open a connection on app startup and close it down upon exit. – Hendra Anggrian Apr 26 '16 at 14:03