<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Artisan Numérique</title>
  <link rel="alternate" type="text/html" href="http://artisan.karma-lab.net/node/1568"/>
  <link rel="self" type="application/atom+xml" href="http://artisan.karma-lab.net/node/1568/atom/feed"/>
  <id>http://artisan.karma-lab.net/node/1568/atom/feed</id>
  <updated>2008-07-17T12:32:50+02:00</updated>
  <entry>
    <title>Hibernate vs JDBC - Performances comparées</title>
    <link rel="alternate" type="text/html" href="http://artisan.karma-lab.net/node/1568" />
    <id>http://artisan.karma-lab.net/node/1568</id>
    <published>2008-06-16T10:13:31+02:00</published>
    <updated>2008-07-17T12:32:50+02:00</updated>
    <author>
      <name>Ulhume</name>
    </author>
    <category term="java" />
    <category term="OK" />
    <category term="Planet Libre" />
    <category term="Article" />
    <summary type="html"><![CDATA[<p>
  Ce n'est pas un scoop même si cela mérite souvent d'être rappelé, la base de données est un élément crucial pour à peu prés toute application professionnelle. Cruciale car c'est elle qui héberge le système d’information et qui, à ce titre, a beaucoup plus de valeur que tous les applicatifs, aussi complexes soient-ils, qui gravitent autour. Cruciale aussi en terme de performance car une base mal choisie, mal paramétrée ou mal utilisée, détruira systématiquement les plus louables efforts d'optimisation. Un point à garder en tête lorsqu'en Java (ou ailleurs) arrive le moment d'écrire ou de lire dans une base et que se pose la Grande Question : persistance ou pas persistance... 
</p>
    ]]></summary>
    <content type="html"><![CDATA[<p>
  Ce n'est pas un scoop même si cela mérite souvent d'être rappelé, la base de données est un élément crucial pour à peu prés toute application professionnelle. Cruciale car c'est elle qui héberge le système d’information et qui, à ce titre, a beaucoup plus de valeur que tous les applicatifs, aussi complexes soient-ils, qui gravitent autour. Cruciale aussi en terme de performance car une base mal choisie, mal paramétrée ou mal utilisée, détruira systématiquement les plus louables efforts d'optimisation. Un point à garder en tête lorsqu'en Java (ou ailleurs) arrive le moment d'écrire ou de lire dans une base et que se pose la Grande Question : persistance ou pas persistance... 
</p>
<!--break-->

	<a name='chapter_1'></a>
  <h2>Cadre de l’évaluation</h2>
	
<h3>Hibernate</h3>
<p>
Je suis parti sur l’exemple fournit dans le <a class='external' target='_blank' href='http://www.hibernate.org/hib_docs/v3/reference/en/html/tutorial.html' >tutoriel d’Hibernate</a>. Dans cet exemple nous avons une très simple table <kbd>Event</kbd> et l’objet qui lui est associé avec seulement trois champs. Pas de relation, pas d’exotisme. J’ai juste rajouté une méthode pour sauvegarder un objet modifié et encadré les fonctions histoire de répéter 1000 fois l’opération. La session Hibernate est créée à l'extérieur de la boucle et n’est pas prise en compte dans le calcul du temps passé :

  <div class='code-block code-block-fragment'>
  <div class='container'>
  &nbsp; &nbsp; session <span class="sy0">=</span> HibernateUtil.<span class="me1">getSessionFactory</span><span class="br0">&#40;</span><span class="br0">&#41;</span>.<span class="me1">openSession</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
<br />
&nbsp; <span class="co1">// je regarde l’heure</span><br />
&nbsp; &nbsp; <span class="kw1">for</span> <span class="br0">&#40;</span><span class="kw4">int</span> i <span class="sy0">=</span> <span class="nu0">0</span><span class="sy0">;</span> i <span class="sy0">&lt;</span> parameter<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span> i<span class="sy0">++</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; session.<span class="me1">beginTransaction</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; session.<span class="me1">save</span><span class="br0">&#40;</span><span class="kw1">new</span> <a target="blank" href="http://www.google.com/search?hl=en&amp;q=allinurl%3AEvent+java.sun.com&amp;btnI=I%27m%20Feeling%20Lucky"><span class="kw3">Event</span></a><span class="br0">&#40;</span><span class="st0">&quot;My Event&quot;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; session.<span class="me1">getTransaction</span><span class="br0">&#40;</span><span class="br0">&#41;</span>.<span class="me1">commit</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp;<span class="co1">// je regarde le temps passé ici</span>
  </div>
  
  </div>
</p>
<p>
J’ai choisi délibérément de faire une transaction par itération car c’est ainsi que cela se passe dans la vraie vie. Je ferais la même chose avec JDBC en faisant un commit à chaque tour. 
</p>
<p>
Je prends tout ce luxe de précaution d’introduction pour éviter toute envolée lyriquo-trollesque. En gros, si vous pensez que ce code n’est pas optimisé, il faut aller engueuler les gens qui ont écrit le tutorial. Moi je n’y suis pour rien, j’ai fais juste comme on m’a dit de faire. 
</p>
<p>
  Pour terminer sur le cadre, j'inclus ici le fichier de paramétrage (en version compactée) utilisé pour ce test. Au passage, merci à Stéphane pour son aide sur les caches. Si vous avez des remarques sur un mauvaise usage rendant Hibernate sous-optimum, merci de me le faire savoir. Le but est d'avoir une comparaison le plus juste possible. 

  <div class='code-block code-block-fragment'>
  <div class='container'>
  <span class="sc3"><span class="re1">&lt;?xml</span> <span class="re0">version</span>=<span class="st0">'1.0'</span> <span class="re0">encoding</span>=<span class="st0">'utf-8'</span><span class="re2">?&gt;</span></span><br />
<span class="sc3"><span class="re1">&lt;hibernate-configuration<span class="re2">&gt;</span></span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;session-factory<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;property</span> <span class="re0">name</span>=<span class="st0">&quot;connection.driver_class&quot;</span><span class="re2">&gt;</span></span>org.postgresql.Driver<span class="sc3"><span class="re1">&lt;/property<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;property</span> <span class="re0">name</span>=<span class="st0">&quot;connection.url&quot;</span><span class="re2">&gt;</span></span>jdbc:postgresql://mon_serveur/ma_base<span class="sc3"><span class="re1">&lt;/property<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;property</span> <span class="re0">name</span>=<span class="st0">&quot;connection.username&quot;</span><span class="re2">&gt;</span></span>mon_user<span class="sc3"><span class="re1">&lt;/property<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;property</span> <span class="re0">name</span>=<span class="st0">&quot;connection.password&quot;</span><span class="re2">&gt;</span></span>mon_password<span class="sc3"><span class="re1">&lt;/property<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="coMULTI">&lt;!-- &lt;property name=&quot;connection.pool_size&quot;&gt;</span></span>1<span class="sc3"><span class="re1">&lt;/property<span class="re2">&gt;</span></span></span> &nbsp;--&gt;<br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;property</span> <span class="re0">name</span>=<span class="st0">&quot;dialect&quot;</span><span class="re2">&gt;</span></span>org.hibernate.dialect.PostgreSQLDialect<span class="sc3"><span class="re1">&lt;/property<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;property</span> <span class="re0">name</span>=<span class="st0">&quot;current_session_context_class&quot;</span><span class="re2">&gt;</span></span>thread<span class="sc3"><span class="re1">&lt;/property<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;property</span> <span class="re0">name</span>=<span class="st0">&quot;cache.provider_class&quot;</span><span class="re2">&gt;</span></span>org.hibernate.cache.NoCacheProvider<span class="sc3"><span class="re1">&lt;/property<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;property</span> <span class="re0">name</span>=<span class="st0">&quot;c3p0.acquire_increment&quot;</span><span class="re2">&gt;</span></span>1<span class="sc3"><span class="re1">&lt;/property<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;property</span> <span class="re0">name</span>=<span class="st0">&quot;c3p0.idle_test_period&quot;</span><span class="re2">&gt;</span></span>100<span class="sc3"><span class="re1">&lt;/property<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;property</span> <span class="re0">name</span>=<span class="st0">&quot;c3p0.max_size&quot;</span><span class="re2">&gt;</span></span>100<span class="sc3"><span class="re1">&lt;/property<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;property</span> <span class="re0">name</span>=<span class="st0">&quot;c3p0.max_statements&quot;</span><span class="re2">&gt;</span></span>20<span class="sc3"><span class="re1">&lt;/property<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;property</span> <span class="re0">name</span>=<span class="st0">&quot;c3p0.min_size&quot;</span><span class="re2">&gt;</span></span>10<span class="sc3"><span class="re1">&lt;/property<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;property</span> <span class="re0">name</span>=<span class="st0">&quot;c3p0.timeout&quot;</span><span class="re2">&gt;</span></span>100<span class="sc3"><span class="re1">&lt;/property<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;property</span> <span class="re0">name</span>=<span class="st0">&quot;show_sql&quot;</span><span class="re2">&gt;</span></span>false<span class="sc3"><span class="re1">&lt;/property<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;property</span> <span class="re0">name</span>=<span class="st0">&quot;hbm2ddl.auto&quot;</span><span class="re2">&gt;</span></span>create<span class="sc3"><span class="re1">&lt;/property<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;mapping</span> <span class="re0">resource</span>=<span class="st0">&quot;net/karmaLab/benchmark/hibernate/Event.hbm.xml&quot;</span> <span class="re2">/&gt;</span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;/session-factory<span class="re2">&gt;</span></span></span><br />
<span class="sc3"><span class="re1">&lt;/hibernate-configuration<span class="re2">&gt;</span></span></span>
  </div>
  
  </div>
</p>
<h3>JDBC</h3>
<p>
  Pour la version "à la main", je travaille sur la même base qu'hibernate, avec le mêmes pilote JDBC. L’équivalent de la méthode donnée plus haut est donc :

  <div class='code-block code-block-fragment'>
  <div class='container'>
  &nbsp; &nbsp; <span class="kw1">Class</span>.<span class="me1">forName</span><span class="br0">&#40;</span><span class="st0">&quot;org.postgresql.Driver&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; connection <span class="sy0">=</span> <a target="blank" href="http://www.google.com/search?hl=en&amp;q=allinurl%3ADriverManager+java.sun.com&amp;btnI=I%27m%20Feeling%20Lucky"><span class="kw3">DriverManager</span></a>.<span class="me1">getConnection</span><span class="br0">&#40;</span><span class="st0">&quot;jdbc:postgresql://mon_serveur/tests&quot;</span>, <span class="st0">&quot;mon_user&quot;</span>, <span class="st0">&quot;mon_pass&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
<br />
&nbsp; &nbsp;<span class="co1">// Je regarde l’heure ici</span><br />
<br />
&nbsp; &nbsp; <a target="blank" href="http://www.google.com/search?hl=en&amp;q=allinurl%3APreparedStatement+java.sun.com&amp;btnI=I%27m%20Feeling%20Lucky"><span class="kw3">PreparedStatement</span></a> statement <span class="sy0">=</span> connection<br />
&nbsp; &nbsp; &nbsp; &nbsp; .<span class="me1">prepareStatement</span><span class="br0">&#40;</span><span class="st0">&quot;insert into EVENTS (EVENT_DATE, title, EVENT_ID) values (?, ?, ?)&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <a target="blank" href="http://www.google.com/search?hl=en&amp;q=allinurl%3AEvent+java.sun.com&amp;btnI=I%27m%20Feeling%20Lucky"><span class="kw3">Event</span></a> event <span class="sy0">=</span> <span class="kw1">new</span> <a target="blank" href="http://www.google.com/search?hl=en&amp;q=allinurl%3AEvent+java.sun.com&amp;btnI=I%27m%20Feeling%20Lucky"><span class="kw3">Event</span></a><span class="br0">&#40;</span><span class="st0">&quot;My Event&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">for</span> <span class="br0">&#40;</span><span class="kw4">int</span> i <span class="sy0">=</span> <span class="nu0">0</span><span class="sy0">;</span> i <span class="sy0">&lt;</span> parameter<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span> i<span class="sy0">++</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
<br />
&nbsp; &nbsp; &nbsp; statement.<span class="me1">setDate</span><span class="br0">&#40;</span><span class="nu0">1</span>, <span class="kw1">new</span> <a target="blank" href="http://www.google.com/search?hl=en&amp;q=allinurl%3ADate+java.sun.com&amp;btnI=I%27m%20Feeling%20Lucky"><span class="kw3">Date</span></a><span class="br0">&#40;</span>event.<span class="me1">getDate</span><span class="br0">&#40;</span><span class="br0">&#41;</span>.<span class="me1">getTime</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; statement.<span class="me1">setString</span><span class="br0">&#40;</span><span class="nu0">2</span>, event.<span class="me1">getTitle</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; statement.<span class="me1">setLong</span><span class="br0">&#40;</span><span class="nu0">3</span>, id<span class="sy0">++</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; statement.<span class="me1">execute</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; connection.<span class="me1">commit</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<span class="co1">// je regarde le temps passé ici</span>
  </div>
  
  </div>
</p>
<p>
Alors oui, le code JDBC est bien moins joli que le code Hibernate, mais voyons ce que cela donne côté performances. 
</p>

	<a name='chapter_2'></a>
  <h2>Résultats</h2>
	
<p>Les tests suivants ont été effectués avec le cache des PreparedStatement activé (c3p0.max_statements=20). Chaque test a été réitéré 10 fois de suite et seul le meilleur temps est gardé. Enfin le cache disque est vidé à chaque lancé et la base de données PostgreSQL est posée sur une machine distante sur un réseau local Gigabit. </p>
<h3 style="clear:left">Inserts</h3><image file="Inserts.png" style="clear:none"/>
<h3 style="clear:left">Selects</h3><image file="Selects.png" style="clear:none"/>
<h3 style="clear:left">Updates</h3><image file="Updates.png" style="clear:none"/>

	<a name='chapter_3'></a>
  <h2>Conclusion</h2>
	
<p>
Bon, j'ai comme l'impression que tout cela se passe de commentaire ou presque : Hibernate est littéralement écrasé.</p>
<p>
 Bon, c'est pas un scoop non plus,  mais cela a le mérite de tordre un peu le coup à la légende urbaine laissant croire jusqu'à un gain de performance avec Hibernate. Je veux bien accepter l'idée que dans des cas plus complexes de tables liées les unes aux autres Hibernate puisse obtenir des performances comparativement moins désastreuses mais pour des choses simples, le résultat est là.</p>
<p>
 Une couche de persistance, quelle qu'elle soit, a un coût. Et plus la volumétrie augmente, plus ce coup devient prépondérant. Un coût qui ne s'évalue pas seulement comme le pensent certains à la qualité et au nombre des requêtes émises, mais aussi aux ressources utilisées pour les fabriquer, et c'est bien là que le bât blesse.</p>
<p>
Maintenant utiliser ou pas Hibernate ou équivalent (comme le système des EJB 2.0) est une question de choix d'architecture et de besoin. L'idée n'est pas ici de jeter le bébé avec l'eau du bain mais bien de prendre conscience que chaque "facilité" a une contrepartie et que dans certains cas cette dernière peut être létale à un projet. 
</p>    ]]></content>
  </entry>
</feed>
