web-dev-qa-db-fra.com

Requête d'hibernation simple renvoyant très lentement

J'ai la requête d'hibernation suivante:

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

Lors de la journalisation du SQL exécuté dans MySQL, je vois

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

Lors de la mesure du Java dans le jvm sur un petit ensemble de données de 3500 lignes dans la table de base de données MyHibernateClass, cela prend environ 7000 ms.

Si, par contre, j'utilise jdbc direct comme suit:

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

Je vois le même sql entrer dans la base de données mais maintenant le temps passé dans le code Java dans le jvm est de 7 ms.

MyHibernateClass est une simple classe de bean Java avec des getters et setters, je n'utilise aucun transformateur de résultat spécial comme on peut le voir dans l'exemple. Je n'ai besoin que d'une instance en lecture seule de la classe, et elle ne fonctionne pas pas besoin d'être attaché à la session d'hibernation.

Je préfère utiliser la version hibernate mais je ne peux pas accepter les temps d'exécution.

Ajout d'informations: Après avoir ajouté la journalisation d'hibernation, je vois

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

suivi par 3500 des instructions de journal suivantes

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

suivi de 3500 instructions de journal comme

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

Qu'est-ce que ça veut dire?

Que fait Hibernate dans la première implémentation, comment le savoir?

27
Trym

L'ajout d'un constructeur avec tous les attributs de la classe a fait l'affaire, maintenant les temps d'exécution sont de 70 ms pour la requête d'hibernation. Auparavant, la classe n'avait qu'un constructeur par défaut sans arguments et un constructeur avec l'argument id d'entité.

28
Trym

Sur la base des nouvelles informations, j'ai pensé que je devrais fournir une autre réponse. La différence semble être que vous avez une association un-à-plusieurs spécifiée pour une propriété List ou Set dans votre bean.

Vous spécifiez probablement que lazy=false qui désactivera le chargement paresseux. Lorsque le chargement différé est désactivé, il récupérera tous les enregistrements associés pour chaque entité MyHibernateClass et c'est pourquoi son exécution prend tellement de temps.

Essayez de définir lazy=true et cela fonctionnera beaucoup plus rapidement et ne récupérera ensuite les entités associées que lors d'une demande explicite auprès de l'entité.

6
maple_shaft

Si vous utilisez Log4j dans votre application, vous pouvez définir différentes options de journalisation spécifiques à Hibernate pour obtenir une meilleure image de ce qui se passe dans les coulisses d'Hibernate.

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

Je suppose que c'est le temps de chargement initial typique qui se produit lors du premier appel d'une requête HQL dans une application. Les requêtes HQL suivantes devraient être sensiblement et considérablement plus rapides après cette première.

4
maple_shaft

Je sais que ce fil est ancien, mais pour la mise à jour, je suis tombé sur le même problème mais avec SQL Server et il s'avère que SQL imprimé par Hibernate et SQL Sent à l'aide du pilote est différent. L'utilisation du pilote MSSQL par défaut envoie les requêtes en tant que procédures stockées lorsque RPC l'appelle, car le pilote essaie d'optimiser le plan de requête pour les normes MSSQL, il envoie donc les requêtes quelque chose comme

Hibernate Query:

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

Requête envoyée par le pilote réel:

@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

Remarque: cette requête que j'ai obtenue via SQL Profiler Tool en écoutant directement sur DB

Il s'avère que les optimisations sp_exec sur le MSSQL ont tendance à produire de bons plans de requête qui sont mis en cache, mais cela entraînerait un `` reniflement des paramètres '' pour en savoir plus sur ce problème, lisez ici ...

Donc, pour surmonter cela, j'avais les options suivantes:

  1. Changer mon HQL en requêtes natives et ajouter OPTION RECOMPILE POUR QUELQUE PARAM

  2. Utilisez des valeurs de requête directes au lieu d'instructions préparées afin qu'il n'y ait pas de traduction pour les valeurs de paramètres et les requêtes ne seront pas modifiées en tant que procédures stockées par le pilote

  3. Modifiez les paramètres du pilote pour ne pas envoyer les procédures stockées (c'est toujours mauvais car maintenant les plans de requête dans le serveur MSSQL seront spécifiques à cette requête, c'est la même chose que l'option: 2 mais en dehors du code)

Je ne voulais pas utiliser OPTION 1 & 2 car cela élimine tout le but de l'utilisation des cadres ORM et je finis par utiliser OPTION 3 pour l'instant

J'ai donc changé l'URL JDBC pour envoyer l'option prepareStatement = false

Après avoir réglé cela, j'ai eu un autre problème, la requête étant envoyée comme

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

Ici, il y a un préfixe avant les valeurs qui indique que pour convertir le schéma de codage, je désactive donc l'URL JDBC pour envoyerUnicode = false

C'est tout ce que j'ai fait dans les options du pilote JTDS. En ce qui me concerne, l'application est maintenant opérationnelle et rapide. J'ai également introduit des caches de deuxième niveau pour le mettre en cache pendant un certain temps ..

J'espère que cela aide quelqu'un, si vous avez une bonne suggestion, faites-le moi savoir.

4
jamb

J'ai eu un incident où mon application utilisait toujours chaque ligne du jeu de résultats d'une requête. J'ai trouvé une augmentation de 40 fois la vitesse en définissant ma taille de récupération à l'aide de la méthode setFetchSize ci-dessous. (L'amélioration des performances comprend l'ajout de la requête de comptage.)

    Long count = getStoreCount(customerId);

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

Soyez prudent en faisant cela; mon ensemble de données contenait environ 100 lignes et il était limité à la durée de vie d'une demande Web. Si vous avez des ensembles de données plus volumineux, vous mangerez Java Heap pendant la durée d'existence de ces données, avant de les renvoyer au Java Heap.

1
gregd

Je sais que c'est une vieille question mais voici ce qui l'a corrigée pour moi ...

Dans votre hibernate.cfg.xml, assurez-vous d'avoir le bon! DOCTYPE ... il doit être comme suit:

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