<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Le Touilleur Express &#187; Java</title>
	<atom:link href="http://www.touilleur-express.fr/tag/java/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.touilleur-express.fr</link>
	<description>Blog sur Java, le métier de développeur et la vie de freelance par Nicolas Martignole</description>
	<lastBuildDate>Wed, 08 Feb 2012 11:54:37 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Google Guava, partie 1</title>
		<link>http://www.touilleur-express.fr/2010/11/03/google-guava-partie-1/</link>
		<comments>http://www.touilleur-express.fr/2010/11/03/google-guava-partie-1/#comments</comments>
		<pubDate>Wed, 03 Nov 2010 05:47:53 +0000</pubDate>
		<dc:creator>Nicolas Martignole</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[google]]></category>
		<category><![CDATA[guava]]></category>

		<guid isPermaLink="false">http://www.touilleur-express.fr/?p=4130</guid>
		<description><![CDATA[La librairie Java Google Guava est une librairie sous licence Apache 2.0 simple et pratique. Elle vous offre un ensemble de classes utiles pour des usages assez fréquents comme la gestion des collections, la gestion des fichiers, des types simples ou de la synchronisation. J&#8217;ai aussi appris quelques astuces en lisant le code et en travaillant sur différents exercices. Je vous propose de la découvrir ensemble.
Encore une librairie ?
Chacun de nous a ses petites habitudes. Je pense que la connaissance du vaste écosystème des projets open-source du monde Java est ...]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.touilleur-express.fr/wp-content/guava.jpg"><img src="http://www.touilleur-express.fr/wp-content/guava-300x224.jpg" alt="" title="guava" width="300" height="224" class="alignleft size-medium wp-image-4131" /></a>La librairie Java <a href="http://code.google.com/p/guava-libraries/" target="_blank">Google Guava</a> est une librairie sous licence Apache 2.0 simple et pratique. Elle vous offre un ensemble de classes utiles pour des usages assez fréquents comme la gestion des collections, la gestion des fichiers, des types simples ou de la synchronisation. J&#8217;ai aussi appris quelques astuces en lisant le code et en travaillant sur différents exercices. Je vous propose de la découvrir ensemble.</p>
<h3>Encore une librairie ?</h3>
<p>Chacun de nous a ses petites habitudes. Je pense que la connaissance du vaste écosystème des projets open-source du monde Java est assez difficile. Pourtant ce qui fait la force de notre plateforme, c&#8217;est bien cette quantité impressionnante de projets et de librairies. Quel est l&#8217;intérêt d&#8217;utiliser une librairie plutôt que de la développer soi-même ? Le gain de temps, le fait qu&#8217;une librairie populaire est plutôt bien testée par la communauté et enfin la possibilité de trouver de l&#8217;aide sur Internet, ou des articles d&#8217;exemples, comme celui-ci.</p>
<h3>Les packages</h3>
<p>La librairie propose les packages suivants:<br />
- com.google.common.annotations<br />
- com.google.common.base<br />
- com.google.common.collect<br />
- com.google.common.io<br />
- com.google.common.net<br />
- com.google.common.primitives<br />
- com.google.common.util.concurrent</p>
<p>La librairie Google Collections a été regroupée à l&#8217;intérieur de Google Guava. La librairie tire son nom de la goyave, fruit à peau verte et à coeur rouge, qui réserve une surprise à celui qui ouvre le fruit en deux.  </p>
<p>Le package <a href="http://guava-libraries.googlecode.com/svn/trunk/javadoc/index.html">com.google.common.annotations</a> ne contient que 4 annotations pour marquer certaines classes comme étant compatible ou non avec GWT, pour marquer en @Beta les classes susceptibles de changer. L&#8217;annotation @VisibleForTesting permet de marquer une méthode comme ayant été rendu visible uniquement pour les tests. Je pense à un setId dans une entité comme <a href="http://www.touilleur-express.fr/2010/08/06/jpa-et-tests-dintegrations/comment-page-1/">dans l&#8217;article sur JPA2</a>.  </p>
<p>Le package <a href="http://guava-libraries.googlecode.com/svn/trunk/javadoc/com/google/common/base/package-frame.html">com.google.common.base</a> regroupe plusieurs familles de classes. Tout ce qui permet de manipuler les chaînes de caractères, ensuite ce qui permet d&#8217;introduire des notions de programmation fonctionnelle en Java et enfin différentes classes pratiques comme Objects ou Preconditions que nous avions vu dans l&#8217;article sur JPA et les tests.</p>
<h3>Classes utilitaires pour la manipulation de caractères et de chaines</h3>
<ul>
<li><a HREF="http://guava-libraries.googlecode.com/svn/trunk/javadoc/com/google/common/base/CaseFormat.html" title="enum in com.google.common.base"><code>CaseFormat</code></a>
 </li>
<li><a HREF="http://guava-libraries.googlecode.com/svn/trunk/javadoc/com/google/common/base/CharMatcher.html" title="class in com.google.common.base"><code>CharMatcher</code></a>
 </li>
<li><a HREF="http://guava-libraries.googlecode.com/svn/trunk/javadoc/com/google/common/base/Charsets.html" title="class in com.google.common.base"><code>Charsets</code></a>
 </li>
<li><a HREF="http://guava-libraries.googlecode.com/svn/trunk/javadoc/com/google/common/base/Joiner.html" title="class in com.google.common.base"><code>Joiner</code></a>
 </li>
<li><a HREF="http://guava-libraries.googlecode.com/svn/trunk/javadoc/com/google/common/base/Splitter.html" title="class in com.google.common.base"><code>Splitter</code></a>
 </li>
<li><a HREF="http://guava-libraries.googlecode.com/svn/trunk/javadoc/com/google/common/base/Strings.html" title="class in com.google.common.base"><code>Strings</code></a>
 </li>
</ul>
<p>La classe CharMatcher est une version plus lisible des Regex et des Patterns matchers de Java. Je vous conseille l&#8217;usage de cette classe pour rendre votre code plus lisible. Par exemple je m&#8217;en servirai pour valider si le nom d&#8217;un utilisateur ne contient pas de caractères interdits dans mon application de cette façon :</p>
<pre class="brush:java">
package org.letouilleur.sample;

import com.google.common.base.CharMatcher;
import com.google.common.base.Preconditions;
import org.junit.Test;

import static org.junit.Assert.*;

public class TestGuava {

    @Test
    public void testCharMatcherIsin() {
        String username = "Nicolas";
        String badUserName = "Nic ! olas,, @ et ?";
        assertTrue(validateUsername(username));
        assertFalse(validateUsername(badUserName));
    }

    /**
     * Validates that the username does not contains one of !@?(,)
     *
     * @param username is the username to validate.
     *
     * @return true if none of the char in username is in the restricted list.
     */
    public static boolean validateUsername(String username) {
        Preconditions.checkNotNull(username);
        // Je ne veux pas de caractères spéciaux dans les mots de passe ou les noms d'utilisateur
        CharMatcher noSpecialChars = CharMatcher.noneOf("!@?(,)").negate();
        if (noSpecialChars.matchesNoneOf(username)) {
            System.out.println(username + " is a valid username");
            return true;
        }
        System.out.println("Please do not use one of !@?(,) in your username value.");
        return false;
    }

}
</pre>
<h3>Types fonctionnels</h3>
<p>Google Guava permet d&#8217;écrire certaines parties de votre programme Java de manière plus fonctionnelle.<br />
C&#8217;est ce qui m&#8217;intéresse le plus dans Guava. Google a regroupé un ensemble de fonctions qui apporte un peu de vision fonctionnelle à votre programmation. Un exemple simple : j&#8217;ai remplacé des NamedQuery assez compliquées sur des Entités JPA par des appels avec Guava. Ceci permet d&#8217;extraire la partie métier et de la rentre visible et testable dans le code. J&#8217;ai pu le faire car la table en base est simple et petite.</p>
<ul>
<li><a HREF="http://guava-libraries.googlecode.com/svn/trunk/javadoc/com/google/common/base/Function.html" title="interface in com.google.common.base"><code>Function</code></a>,<br />
     <a HREF="http://guava-libraries.googlecode.com/svn/trunk/javadoc/com/google/common/base/Functions.html" title="class in com.google.common.base"><code>Functions</code></a>
 </li>
<li><a HREF="http://guava-libraries.googlecode.com/svn/trunk/javadoc/com/google/common/base/Predicate.html" title="interface in com.google.common.base"><code>Predicate</code></a>,<br />
     <a HREF="http://guava-libraries.googlecode.com/svn/trunk/javadoc/com/google/common/base/Predicates.html" title="class in com.google.common.base"><code>Predicates</code></a>
 </li>
<li><a HREF="http://guava-libraries.googlecode.com/svn/trunk/javadoc/com/google/common/base/Supplier.html" title="interface in com.google.common.base"><code>Supplier</code></a>,<br />
     <a HREF="http://guava-libraries.googlecode.com/svn/trunk/javadoc/com/google/common/base/Suppliers.html" title="class in com.google.common.base"><code>Suppliers</code></a>
 </li>
</ul>
<p>Je vous proposerai dans un deuxième article une présentation détaillée de Google Guava.</p>
<h3>Autres</h3>
<ul>
<li><a HREF="http://guava-libraries.googlecode.com/svn/trunk/javadoc/com/google/common/base/Defaults.html" title="class in com.google.common.base"><code>Defaults</code></a>
 </li>
<li><a HREF="http://guava-libraries.googlecode.com/svn/trunk/javadoc/com/google/common/base/FinalizableReference.html" title="interface in com.google.common.base"><code>FinalizableReference</code></a> and subtypes
 </li>
<li><a HREF="http://guava-libraries.googlecode.com/svn/trunk/javadoc/com/google/common/base/Objects.html" title="class in com.google.common.base"><code>Objects</code></a>
 </li>
<li><a HREF="http://guava-libraries.googlecode.com/svn/trunk/javadoc/com/google/common/base/Preconditions.html" title="class in com.google.common.base"><code>Preconditions</code></a>
 </li>
<li><a HREF="http://guava-libraries.googlecode.com/svn/trunk/javadoc/com/google/common/base/Throwables.html" title="class in com.google.common.base"><code>Throwables</code></a>
 </li>
</ul>
<p>La classe Preconditions s&#8217;importe en général de manière statique dans votre code. Elle regroupe beaucoup de méthodes simples comme checkNotNull(Object o) qui remplace 3 lignes de if(o==null) throw new NullPointerException. Mais surtout, <a HREF="http://guava-libraries.googlecode.com/svn/trunk/javadoc/com/google/common/base/Preconditions.html" title="class in com.google.common.base">Preconditions</a> permet de rendre plus expressif votre code métier.</p>
<pre class="brush:java">
Sans Guava
if (count < = 0) {
       throw new IllegalArgumentException("must be positive: " + count);
}
devient
   checkArgument(count > 0, "must be positive: %s", count);
</pre>
<p>Continuer la lecture et aller à la partie 2 : <a href="http://www.touilleur-express.fr/2010/11/03/google-guava-faire-du-fonctionnel/">faire du fonctionnel avec Guava</a></p>
<h3>Références</h3>
<p>- <a href="http://code.google.com/p/guava-libraries/">Le site de Google Guava</a><br />
- <a href="http://codemunchies.com/2009/10/beautiful-code-with-google-collections-guava-and-static-imports-part-1/">Une série d&#8217;article en Anglais sur Google Guava</a><br />
- <a href="http://jnb.ociweb.com/jnb/jnbApr2010.html">Exploring Google Guava</a> par Dan Lewis d&#8217;OCI.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.touilleur-express.fr/2010/11/03/google-guava-partie-1/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>Oracle a racheté SUN Microsystems</title>
		<link>http://www.touilleur-express.fr/2009/04/20/oracle-a-rachete-sun-microsystems/</link>
		<comments>http://www.touilleur-express.fr/2009/04/20/oracle-a-rachete-sun-microsystems/#comments</comments>
		<pubDate>Mon, 20 Apr 2009 15:59:42 +0000</pubDate>
		<dc:creator>Nicolas Martignole</dc:creator>
				<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://www.touilleur-express.fr/?p=1173</guid>
		<description><![CDATA[Oracle a annoncé le rachat de SUN Microsystems pour quelques 7 milliards de dollars. Après le loupé il y a quelques semaines entre IBM et SUN, cette fois-ci l&#8217;opération a réussi. Dès 2009 cette acquisition rajoutera 15 cents par action aux résultats du nouveau groupe, simplement par l&#8217;effet du chiffre d&#8217;affaire de SUN.
La blogosphère, ma boîte GMail, Twitter, tout le monde ne parle que de ça&#8230; Encore une fois Twitter a été pour moi le premier moyen d&#8217;être informé, suivi par la mailing list JavaPosse toujours au courant de ce ...]]></description>
			<content:encoded><![CDATA[<p>Oracle a annoncé le rachat de SUN Microsystems pour quelques 7 milliards de dollars. Après le loupé il y a quelques semaines entre IBM et SUN, cette fois-ci l&#8217;opération a réussi. Dès 2009 cette acquisition rajoutera 15 cents par action aux résultats du nouveau groupe, simplement par l&#8217;effet du chiffre d&#8217;affaire de SUN.</p>
<p>La blogosphère, ma boîte GMail, Twitter, tout le monde ne parle que de ça&#8230; Encore une fois Twitter a été pour moi le premier moyen d&#8217;être informé, suivi par la mailing list JavaPosse toujours au courant de ce genre de rumeurs.</p>
<p>Bon mon avis sur la question: attendons quelques jours avant de crier au loup. Les avis sont partagés, certains prédisent la mort de MySQL, d&#8217;autres de la JVM de SUN qui ferait doublon avec &laquo;&nbsp;autre-jvm&nbsp;&raquo; d&#8217;oracle&#8230; Bien malin celui qui peut parler au nom d&#8217;Oracle&#8230; Projets secrets ? Rapprochement programmé ?</p>
<p>A suivre</p>
]]></content:encoded>
			<wfw:commentRss>http://www.touilleur-express.fr/2009/04/20/oracle-a-rachete-sun-microsystems/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Java et Groovy 1.6.1 supportés sur Google AppEngine</title>
		<link>http://www.touilleur-express.fr/2009/04/08/java-et-groovy-161-supportes-sur-google-appengine/</link>
		<comments>http://www.touilleur-express.fr/2009/04/08/java-et-groovy-161-supportes-sur-google-appengine/#comments</comments>
		<pubDate>Wed, 08 Apr 2009 05:35:06 +0000</pubDate>
		<dc:creator>Nicolas Martignole</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[groovy]]></category>

		<guid isPermaLink="false">http://www.touilleur-express.fr/?p=1074</guid>
		<description><![CDATA[Ce matin la nouvelle s&#8217;est propagée via Twitter en quelques heures. @DidierGirard a annoncé ce matin vers 06h00 l&#8217;information, j&#8217;ai vu aussi que les JavaPosses avaient assisté à un événement (@DickWall). C&#8217;est surtout Guillaume Laforge qui est fier d&#8217;annoncer que Groovy sera supporté par Google AppEngine. SpringSource a publié ce matin un tutorial en anglais pour écrire sa première Groovlet et la déployer sur GoogleAppEngine, qui est un serveur d&#8217;application déployé en cloud-computing. D&#8217;ici une semaine nous devrions avoir un Gwitter, twitter écrit en Groovy au rythme où vont les ...]]></description>
			<content:encoded><![CDATA[<p>Ce matin la nouvelle s&#8217;est propagée via Twitter en quelques heures. <a href="http://twitter.com/DidierGirard">@DidierGirard</a> a annoncé ce matin vers 06h00 l&#8217;information, j&#8217;ai vu aussi que les JavaPosses avaient assisté à un événement (<a href="http://twitter.com/dickwall">@DickWall</a>). C&#8217;est surtout <a href="http://twitter.com/glaforge">Guillaume Laforge</a> qui est fier d&#8217;annoncer que Groovy sera supporté <a href="http://appengine.google.com/start">par Google AppEngine</a>. SpringSource <a href="http://blog.springsource.com/2009/04/07/write-your-google-app-engine-applications-in-groovy/">a publié ce matin un tutorial</a> en anglais pour écrire sa première Groovlet et la déployer sur GoogleAppEngine, qui est un serveur d&#8217;application déployé en cloud-computing. D&#8217;ici une semaine nous devrions avoir un Gwitter, twitter écrit en Groovy au rythme où vont les choses !</p>
<p>Didier a déjà publié une news sur <a href="http://www.insideit.fr/post/2009/04/08/Java-et-Groovy-sont-maintenant-supportes-par-Google-App-Engine">son blog,</a> je le laisse donc passer devant, le Touilleur Express vous donnera d&#8217;autres informations demain dans la soirée.</p>
<p>Stay Tuned !<br />
Il y a encore d&#8217;autres choses à venir.</p>
<p>Voir :<br />
<a href="http://blog.springsource.com/2009/04/07/write-your-google-app-engine-applications-in-groovy/">L&#8217;annonce sur le blog SpringSource du support de Groovy</a><br />
<a href="http://googleappengine.blogspot.com/2009/04/seriously-this-time-new-language-on-app.html">http://googleappengine.blogspot.com/2009/04/seriously-this-time-new-language-on-app.html</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.touilleur-express.fr/2009/04/08/java-et-groovy-161-supportes-sur-google-appengine/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Hibernate : le chargement tardif c&#039;est extra</title>
		<link>http://www.touilleur-express.fr/2009/03/09/hibernate-le-chargement-tardif-cest-extra/</link>
		<comments>http://www.touilleur-express.fr/2009/03/09/hibernate-le-chargement-tardif-cest-extra/#comments</comments>
		<pubDate>Mon, 09 Mar 2009 21:42:50 +0000</pubDate>
		<dc:creator>Nicolas Martignole</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[hibernate]]></category>

		<guid isPermaLink="false">http://www.touilleur-express.fr/?p=849</guid>
		<description><![CDATA[Voici la suite de mon article précédent sur Hibernate et le chargement des associations.
Il existe 3 paramètres possible pour l&#8217;élément lazy lors de la définition d&#8217;une association dans une entité :
 &#8211; false
 &#8211; true
 &#8211; extra

...
        
            
            
        
...

Le test est simple : j&#8217;édite le fichier Item.hbm.xml et j&#8217;active le chargement tardif ...]]></description>
			<content:encoded><![CDATA[<p>Voici la suite de <a href="http://www.touilleur-express.fr/2009/03/03/hibernate-gerer-le-chargement-des-associations-efficacement/">mon article précédent</a> sur Hibernate et le chargement des associations.</p>
<p>Il existe 3 paramètres possible pour l&#8217;élément lazy lors de la définition d&#8217;une association dans une entité :<br />
 &#8211; false<br />
 &#8211; true<br />
 &#8211; extra</p>
<pre name="code" class="xml">
...
        <set name="bids" table="ITEM_BIDS" lazy="false|true|extra">
            <key column="ITEM_ID"/>
            <one -to-many class="org.touilleur.hibernate.v1.Bid"/>
        </set>
...
</pre>
<p>Le test est simple : j&#8217;édite le fichier Item.hbm.xml et j&#8217;active le chargement tardif (lazy=true). Le code Java affiche le nom de l&#8217;item chargé puis ensuite le nombre d&#8217;enchères sur l&#8217;Item en utilisant la méthode size() sur le HashSet.</p>
<p><strong>Code Java</strong></p>
<pre name="code" class="java">
        long start = System.currentTimeMillis();
        Session session = HibernateUtil.getSessionFactory().getCurrentSession();
        session.beginTransaction();
        item1 = (Item) session.load(Item.class, new Integer(id));
        logger.info("Item name is ["+item1.getName()+"]");
        logger.info("Size of Bids Set : "+ item1.getBids().size())  ;
        logger.info("Ellapsed: " + (System.currentTimeMillis() - start));
</pre>
<p><strong>Résultats de l&#8217;exécution</strong></p>
<pre>
   Hibernate: /* load org.touilleur.hibernate.v1.Item */
            select item0_.ITEM_ID as ITEM1_0_0_, item0_.name as name0_0_, item0_.price as price0_0_
            from ITEM item0_ where item0_.ITEM_ID=?
INFO 2009-03-09 22:12:20,234 [Demo Hibernate] : Item name is [Item 1]
   Hibernate: /* load one-to-many org.touilleur.hibernate.v1.Item.bids */
            select bids0_.ITEM_ID as ITEM4_1_, bids0_.BID_ID as BID1_1_,
                      bids0_.BID_ID as BID1_1_0_,
                      bids0_.amount as amount1_0_, bids0_.date as date1_0_,
                      bids0_.ITEM_ID as ITEM4_1_0_
           from BID bids0_ where bids0_.ITEM_ID=?
INFO 2009-03-09 22:12:20,244 [Demo Hibernate] : Size of Bids Set : 10
INFO 2009-03-09 22:12:20,244 [Demo Hibernate] : Ellapsed: 23
</pre>
<p>On constate qu&#8217;Hibernate effectue tout d&#8217;abord une requête sur la table Item, affiche le nom de l&#8217;item puis ensuite effectue le chargement de l&#8217;ensemble des Bids en spécifiant l&#8217;id de l&#8217;Item. Je vois plusieurs soucis : le chargement de l&#8217;ensemble des colonnes de la table Bid, la deuxième requête, et finalement la requête SQL n&#8217;est pas franchement optimisée : nous voulons afficher la taille de la collection, or Hibernate charge l&#8217;ensemble des éléments&#8230;<br />
Ce qui veut dire que 10 objets de type Bid seront créés et que la taille du hashSet de bids sera calculée en effectuant un appel à la méthode hashSet.size().<br />
Je ne suis pas sûr d&#8217;être clair : pour retourner la taille de la collection, Hibernate charge toute la collection et retourne la taille de la liste&#8230; Vous imaginez la consommation en terme de mémoire ? Je serai presque tenté de dire qu&#8217;Hibernate n&#8217;est pas franchement écologique.</p>
<p>Voyons ce qu&#8217;il se passe lorsque le chargement tardif est désactivé en mettant lazy=false dans l&#8217;attribut set, dans le fichier Item.hbm.xml</p>
<pre>
INFO 2009-03-09 22:17:20,756 [Demo Hibernate] : testLazyFalse
   Hibernate: /* load org.touilleur.hibernate.v1.Item */
            select item0_.ITEM_ID as ITEM1_0_0_, item0_.name as name0_0_,
                      item0_.price as price0_0_ from ITEM item0_
            where item0_.ITEM_ID=?
   Hibernate: /* load one-to-many org.touilleur.hibernate.v1.Item.bids */
            select bids0_.ITEM_ID as ITEM4_1_, bids0_.BID_ID as BID1_1_,
                      bids0_.BID_ID as BID1_1_0_, bids0_.amount as amount1_0_,
                     bids0_.date as date1_0_, bids0_.ITEM_ID as ITEM4_1_0_
            from BID bids0_
            where bids0_.ITEM_ID=?
INFO 2009-03-09 22:17:20,777 [Demo Hibernate] : Item name is [Item 1]
INFO 2009-03-09 22:17:20,778 [Demo Hibernate] : Size of Bids Set : 10
INFO 2009-03-09 22:17:20,778 [Demo Hibernate] : Ellapsed: 21
</pre>
<p>Nous constatons que la seule différence, c&#8217;est qu&#8217;Hibernate effectue toutes les requêtes avant l&#8217;exécution du code&#8230;<br />
Mais encore une fois, si notre code se contentait d&#8217;afficer par exemple le nombre d&#8217;enchères (ici 10) nous aurions chargé encore une fois l&#8217;arbre d&#8217;enchères pour rien. Bref pas d&#8217;optimisation en vue pour l&#8217;instant.</p>
<p>Voyons pour terminer ce qu&#8217;Hibernate fait lorsqe le paramètre lazy est mis à <strong>extra</strong> :</p>
<pre name="code" class="xml">
...
        <set name="bids" table="ITEM_BIDS" lazy="extra">
            <key column="ITEM_ID"/>
            <one -to-many class="org.touilleur.hibernate.v1.Bid"/>
        </set>
...
</pre>
<p>L&#8217;exécution nous donne le résultat suivant:</p>
<pre>
   Hibernate: /* load org.touilleur.hibernate.v1.Item */
          select item0_.ITEM_ID as ITEM1_0_0_, item0_.name as name0_0_,
                    item0_.price as price0_0_
          from ITEM item0_
          where item0_.ITEM_ID=?
INFO 2009-03-09 22:25:10,702 [Demo Hibernate] : Item name is [Item 1]
   Hibernate:
             select count(BID_ID) from BID where ITEM_ID =?
INFO 2009-03-09 22:25:10,707 [Demo Hibernate] : Size of Bids Set : 10
INFO 2009-03-09 22:25:10,707 [Demo Hibernate] : Ellapsed: 17
</pre>
<p>Cette fois-ci Hibernate utilise un SELECT COUNT pour calculer la taille de la collection <strong>sans</strong> la charger, ce qui est d&#8217;une part plus rapide, et d&#8217;autre part plus économique en terme de données chargées et d&#8217;instance préparées.</p>
<p>La valeur <strong>lazy</strong> est un moyen d&#8217;indiquer à Hibernate que l&#8217;objet Père (ici Item) contient une collection d&#8217;enfants (Bids) très grande. Si votre application calcule la taille du Set avec la méthode size() ou que vous testez par exemple si la collection est vide avec getBids().isEmpty(),  Hibernate effectue une requête de type SELECT COUNT, ce qui revient donc à éviter de charger le contenu des enchères.</p>
<p>C&#8217;est donc une optimisation possible dans une application afin d&#8217;éviter de charger un arbre d&#8217;objet trop important dans votre code.</p>
<p>Si vous utilisez les annotations voici comment déclarer ce paramère :</p>
<pre name="code" class="java">

// Code de la classe Item
@OneToMany
@org.hibernate.annoations.LazyCollection(org.hibernate.annotations.LazyCollectionOption.EXTRA)
private Set bids = new HashSet();
</pre>
<p>Voilà pour la deuxième partie, j&#8217;ai encore quelques idées que je vous proposerai dans d&#8217;autres articles prochainement.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.touilleur-express.fr/2009/03/09/hibernate-le-chargement-tardif-cest-extra/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Hibernate : gérer le chargement des associations efficacement</title>
		<link>http://www.touilleur-express.fr/2009/03/03/hibernate-gerer-le-chargement-des-associations-efficacement/</link>
		<comments>http://www.touilleur-express.fr/2009/03/03/hibernate-gerer-le-chargement-des-associations-efficacement/#comments</comments>
		<pubDate>Tue, 03 Mar 2009 14:17:25 +0000</pubDate>
		<dc:creator>Nicolas Martignole</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[hibernate]]></category>

		<guid isPermaLink="false">http://www.touilleur-express.fr/?p=800</guid>
		<description><![CDATA[Hibernate en quelques mots
Hibernate est un moteur de mapping object-relationnel (ORM) qui permet de charger des données venant d&#8217;une base de données, vers le monde Java en réduisant la quantité de code Java à écrire.
Vous définissez une classe Java, ensuite un fichier de mapping au format XML, et Hibernate offre alors un moyen pour charger, modifier et effacer vos données. Il dispose aussi d&#8217;un cache de requête afin d&#8217;optimiser les appels et d&#8217;éviter des aller-retours inutiles avec la base de données. Un cache de second niveau offre un moyen très ...]]></description>
			<content:encoded><![CDATA[<h3>Hibernate en quelques mots</h3>
<p>Hibernate est un moteur de mapping object-relationnel (ORM) qui permet de charger des données venant d&#8217;une base de données, vers le monde Java en réduisant la quantité de code Java à écrire.<br />
Vous définissez une classe Java, ensuite un fichier de mapping au format XML, et Hibernate offre alors un moyen pour charger, modifier et effacer vos données. Il dispose aussi d&#8217;un cache de requête afin d&#8217;optimiser les appels et d&#8217;éviter des aller-retours inutiles avec la base de données. Un cache de second niveau offre un moyen très puissant mais délicat à maîtriser pour partager vos données et réduire le coût de la base de données dans une application.<br />
Il est possible d&#8217;utiliser soit des annotations sur la classe Java, soit des fichiers XML. Sachez que si vous utilisez des annotations, un fichier de configuration XML peut vous permettre de surcharger le mapping des annotations, et donc d&#8217;éviter d&#8217;être bloqué par du code déjà compilé sur lequel vous n&#8217;avez pas le contrôle.</p>
<h2>Objectifs de l&#8217;article</h2>
<p>La première partie de l&#8217;article est assez dictatique pour ceux qui ne connaissent pas Hibernate 3. La seconde partie sera plus intéressante pour les utilisateurs qui souhaitent voir quelques options avancées d&#8217;Hibernate. Nous verrons comment gérer efficacement les relations entre les objets, les stratégies de chargement et les différentes types d&#8217;option de chargement tardif (Lazy instanciation).</p>
<h2>Code source de l&#8217;article</h2>
<p>Pour les besoins de cet article j&#8217;ai préparé un projet simple avec maven2, que vous pouvez télécharger en fin d&#8217;article. Le code est distribué sous licence Creatives Commons v2 avec quelques restrictions sur la non modification et l&#8217;interdiction d&#8217;utiliser le code à des fins commerciales sans mon accord.</p>
<p>Si vous souhaitez tester quelques fonctionnalités en lisant l&#8217;article, vous aurez besoin de Java 5 et de Maven 2.0.6 minimum pour compiler et tester le code. J&#8217;utilise un test unitaire JUnit 3.8 simple pour lancer les différents tests, un simple &laquo;&nbsp;mvn test&nbsp;&raquo; en ligne de commande vous permet de compiler le code et de lancer la base de données embarquée HSQLDB.</p>
<h2>Un peu de relationnel</h2>
<p>J&#8217;ai repris un exemple simple pour passer en revue quelques caractéristiques d&#8217;Hibernate plus ou moins connues.<br />
Prenons tout d&#8217;abord la classe Item, correspondant à un article.<br />
Un Item est caractérisé par son nom, son prix et une clé d&#8217;identité. Un constructeur vide sans argument est requis pour qu&#8217;Hibernate puisse décorer cette classe avec un proxy avec la librairie CGLIB, sans quoi une exception org.hibernate.InstantiationException sera levée par Hibernate. De même il est recommandé de ne pas déclarer de classe finale comme entité si vous souhaitez qu&#8217;Hibernate puisse utiliser un proxy. Si malgré tout vous êtes obligés de déclarer une méthode finale, il faudra alors désactiver le chargement tardif (lazy=false) dans la configuration de l&#8217;entité [<a href="http://www.hibernate.org/hib_docs/v3/reference/fr-FR/html_single/#persistent-classes-pojo-final" target="_blank">1</a>]</p>
<pre class="java" name="code">
public class Item {
    private Integer id;
    private String name;
    private float price;
    private Set&lt;Bid&gt; bids=new HashSet&lt;Bid&gt;();

    public Item() {

    }

    public Item(String name, float price){
        //...
    }
     // Getter, setter, equals, hashCode et toString
}</pre>
<p>La classe Bid (enchère en anglais) est une offre émise par un acheteur sur notre Item. Les attributs sont l&#8217;heure de l&#8217;enchère et son montant.</p>
<pre class="java" name="code">
public class Bid {
    private Integer id;
    private float amount;
    private Date date;
    private Item item;

    public Bid(){
    }

    // getter, setter, hashCode, equals
}</pre>
<p>Le fichier de mapping Hibernate Item.hbm.xml permet à Hibernate d&#8217;effectuer le mapping entre la classe Item et notre base de données. La relation un-vers-plusieurs (un Item contient plusieurs Bids) est définie avec un set. Nous utiliserons une table de jointure ITEM_BIDS, le chargement tardif sera désactivé (lazy=false), nous indiquons à Hibernate que la relation inverse est déclarée dans Bid (inverse=true).</p>
<pre class="xml" name="code">
    &lt;hibernate-mapping&gt;

    &lt;class name="org.touilleur.hibernate.v1.Item" table="ITEM"&gt;
        &lt;id name="id" column="ITEM_ID"&gt;
            &lt;generator class="native"/&gt;
        &lt;/id&gt;
        &lt;property name="name"/&gt;
        &lt;property name="price"/&gt;
        &lt;set name="bids" table="ITEM_BIDS"
                                       lazy="false"
                                       inverse="true"
                                       fetch="select"
                                       cascade="all"&gt;
            &lt;key column="ITEM_ID" not-null="true"/&gt;
            &lt;one-to-many class="org.touilleur.hibernate.v1.Bid"/&gt;
        &lt;/set&gt;
    &lt;/class&gt;

&lt;/hibernate-mapping&gt;
</pre>
<p>Hibernate recommande d&#8217;utiliser des associations symétriques. Dans notre application, une enchère sans article n&#8217;a pas de sens. Nous configurons donc notre association many-to-one avec l&#8217;attribut not-null à true dans le fichier Bid.hbm.xml:</p>
<pre class="xml" name="code">
&lt;hibernate-mapping&gt;
    &lt;class name="org.touilleur.hibernate.v1.Bid" table="BID"&gt;
        &lt;id name="id" column="BID_ID"&gt;
            &lt;generator class="native"/&gt;
        &lt;/id&gt;
        &lt;property name="amount"/&gt;
        &lt;property name="date"/&gt;
        &lt;many-to-one name="item"
                               column="ITEM_ID"
                               class="org.touilleur.hibernate.v1.Item"
                               not-null="true"/&gt;
    &lt;/class&gt;

&lt;/hibernate-mapping&gt;</pre>
<h2>Stratégie de chargement de l&#8217;association</h2>
<p>Hibernate évite au maximum de faire appel à la base de données, et tend à limiter le nombre de requête, afin d&#8217;alléger le traitement. Par défaut la stratégie de chargement tardive qui permet d&#8217;optimiser les performances, peut provoquer quelques moments difficile lorsque surgit l&#8217;exception <em>LazyInitializationException</em>.</p>
<p>Une exception de type LazyInitializationException sera renvoyée par Hibernate si une collection ou un proxy non initialisé est accédé en dehors de la portée de la Session, e.g. lorsque l&#8217;entité à laquelle appartient la collection ou qui a une référence vers le proxy est dans l&#8217;état &laquo;&nbsp;détachée&nbsp;&raquo;.</p>
<p>Cela se produit dans les architectures 3-tiers, où le tiers de présentation Web est séparé physiquement de l&#8217;ejbtiers pour des raisons de sécurité. Ce type d&#8217;architecture soufre de plusieurs soucis : si vous interdisez l&#8217;accès à la base de données à partir du web-tiers, il faudra donc que la stratégie de chargement tardive d&#8217;Hibernate soit désactivée (lazy=false). Dans le cas où nous souhaitons charger un Item, toutes les enchères associées seront alors chargées. Imaginez que pour seulement afficher le nom de l&#8217;Item, nous allons aussi charger 1500 Bids par exemple&#8230;<br />
Il est alors important de configurer Hibernate finement et d&#8217;utiliser des jointures afin d&#8217;éviter trop de requêtes.</p>
<p>Dans un premier temps, nous allons regarder les différentes possibilités de chargement d&#8217;une association, en essayant différentes valeurs pour l&#8217;attribut fetch de l&#8217;attribut set. La valeur par défaut lorsqu&#8217;elle n&#8217;est pas précisée est &laquo;&nbsp;select&nbsp;&raquo;. Les 3 valeurs possibles sont <em>select, join</em> et <em>subselect</em>. Nous désactivons le chargement tardif en ajoutant le mot clé &laquo;&nbsp;lazy=false&nbsp;&raquo; dans le fichier Item.hbm.xml.</p>
<h3>Fetch strategy par défaut en mode lazy=false</h3>
<p>Pour ce test, dans le test unitaire SelectTest, nous allons voir comment il est possible de changer la stratégie de chargement directement dans le code. Avant cela, regardons comment travaille Hibernate par défaut avant de tester d&#8217;autres modes de chargement. Par défault lorsque FetchMode est à DEFAULT comme ci-dessous, Hibernate se repose sur ce que vous avez déclaré dans votre fichier de mapping, Item.hbm.xml pour nous.</p>
<pre class="java" name="code">
// voir org.touilleur.hibernate.SelectTest
 Item item1 = (Item) session.createCriteria(Item.class)
                .setFetchMode("bids", FetchMode.DEFAULT)
                .add(Restrictions.idEq(id))
                .uniqueResult();
</pre>
<p>La configuration du chargement de l&#8217;association s&#8217;effectue dans le fichier de configuration hibernate.cfg.xml à l&#8217;aide de l&#8217;attribut fetch. La valeur par défaut est select, les 2 autres valeurs possibles sont join et subselect. Voyons d&#8217;abord par défaut le comportement d&#8217;Hibernate :</p>
<pre class="xml">
       &lt;set name="bids" table="ITEM_BIDS" lazy="false" inverse="false"
                        <strong>fetch="select"</strong> cascade="all"&gt;</pre>
<p>Pour continuer, exécutez la méthode <em>testSelectOneItemWithDefault</em>.<br />
La console affiche les requêtes SQL suivantes:<br />
<code>...<br />
INFO 2009-02-27 22:24:21,788 [Demo Hibernate] : Starting selectOneItemWithDefault<br />
Hibernate: /* criteria query */ select this_.ITEM_ID as ITEM1_0_0_, this_.name as name0_0_,<br />
    this_.price as price0_0_ from ITEM this_ where this_.ITEM_ID = ?<br />
Hibernate: /* load one-to-many org.touilleur.hibernate.v1.Item.bids */ select bids0_.ITEM_ID as ITEM4_1_,<br />
    bids0_.BID_ID as BID1_1_, bids0_.BID_ID as BID1_1_0_, bids0_.amount as amount1_0_, bids0_.date as date1_0_,<br />
    bids0_.ITEM_ID as ITEM4_1_0_ from BID bids0_ where bids0_.ITEM_ID=?<br />
INFO 2009-02-27 22:24:21,791 [Demo Hibernate] : Loaded item1 with a FetchMode set to DEFAULT...</code></p>
<p>2 requêtes sont nécessaires pour effectuer le chargement de l&#8217;unique Item.</p>
<p><strong>Chargement de l&#8217;ensemble des Items</strong></p>
<p>Le test unitaire testSelecTAllItemsWithDefaultFetchMode sélectionne les 2 Items de notre base et affiche ensuite les 2 items. La méthode toString de la class Item force le chargement de la liste des enchères. Nous avons désactivé le chargement tardif (lazy=false) et il est donc normal de voir Hibernate charger les Bids.</p>
<p>Que se passe-t-il lorsque nous exécutons ce code ?<br />
<code><br />
INFO 2009-03-03 10:18:30,670 [Demo Hibernate] : testSelecTAllItemsWithDefaultFetchMode - begin<br />
Hibernate: /* criteria query */ select this_.ITEM_ID as ITEM1_0_0_, this_.name as name0_0_, this_.price as price0_0_ from ITEM this_<br />
Hibernate: /* load one-to-many org.touilleur.hibernate.v1.Item.bids */ select bids0_.ITEM_ID as ITEM4_1_, bids0_.BID_ID as BID1_1_, bids0_.BID_ID as BID1_1_0_, bids0_.amount as amount1_0_, bids0_.date as date1_0_, bids0_.ITEM_ID as ITEM4_1_0_ from BID bids0_ where bids0_.ITEM_ID=?<br />
Hibernate: /* load one-to-many org.touilleur.hibernate.v1.Item.bids */ select bids0_.ITEM_ID as ITEM4_1_, bids0_.BID_ID as BID1_1_, bids0_.BID_ID as BID1_1_0_, bids0_.amount as amount1_0_, bids0_.date as date1_0_, bids0_.ITEM_ID as ITEM4_1_0_ from BID bids0_ where bids0_.ITEM_ID=?<br />
INFO 2009-03-03 10:18:30,704 [Demo Hibernate] : Item #1 : Item{id=1, bids=[Bid{id=7, amount=107.0, date=2009-03-03 10:18:30.637}, Bid{id=10, amount=110.0, date=2009-03-03 10:18:30.643}, Bid{id=6, amount=106.0, date=2009-03-03 10:18:30.632}, Bid{id=8, amount=108.0, date=2009-03-03 10:18:30.638}, Bid{id=2, amount=102.0, date=2009-03-03 10:18:30.626}, Bid{id=4, amount=104.0, date=2009-03-03 10:18:30.629}, Bid{id=1, amount=101.0, date=2009-03-03 10:18:30.625}, Bid{id=9, amount=109.0, date=2009-03-03 10:18:30.639}, Bid{id=5, amount=105.0, date=2009-03-03 10:18:30.63}, Bid{id=3, amount=103.0, date=2009-03-03 10:18:30.628}], name='TV LCD', price=340.0}<br />
INFO 2009-03-03 10:18:30,706 [Demo Hibernate] : Item #2 : Item{id=2, bids=[Bid{id=20, amount=144.0, date=2009-03-03 10:18:30.657}, Bid{id=15, amount=125.0, date=2009-03-03 10:18:30.651}, Bid{id=13, amount=43.0, date=2009-03-03 10:18:30.647}, Bid{id=16, amount=116.0, date=2009-03-03 10:18:30.652}, Bid{id=14, amount=84.0, date=2009-03-03 10:18:30.648}, Bid{id=11, amount=10.0, date=2009-03-03 10:18:30.645}, Bid{id=17, amount=137.0, date=2009-03-03 10:18:30.654}, Bid{id=19, amount=139.0, date=2009-03-03 10:18:30.656}, Bid{id=12, amount=22.0, date=2009-03-03 10:18:30.646}, Bid{id=18, amount=138.0, date=2009-03-03 10:18:30.655}], name='Plasma', price=1230.0}<br />
INFO 2009-03-03 10:18:30,709 [Demo Hibernate] : testSelecTAllItemsWithDefaultFetchMode - end</p>
<p>Hibernate utilise 3 requêtes pour effectuer le chargement. C'est un problème important. Imaginez que votre base contient 1000 items qui eux-mêmes référencent 10 Bids différents... Nous n'optimisons pas vraiment notre chargement, cela nous coûte cher en nombre de requêtes SQL. De plus, n'étant pas en mode de chargement tardif, nous voyons bien qu'Hibernate effectue les 3 requêtes avant d'attaquer l'affichage de notre résultat.</p>
<p><strong>Que se passe-t-il lorsque le mode lazy est actif ?</strong><br />
Rappelons que par défaut Hibernate étant bien fait, ce mode est implicite et il est le mode de fonctionnement par défaut d'Hibernate. Pour ce test, je me contente de changer dans le fichier Item.hbm.xml la configuration du chargement tardif et je relance le même test afin de voir comment Hibernate charge la collection:</p>
<pre class="xml" name="code">
&lt;hibernate-mapping&gt;

    &lt;class name="org.touilleur.hibernate.v1.Item" table="ITEM"&gt;
        &lt;id name="id" column="ITEM_ID"&gt;
            &lt;generator class="native"/&gt;
        &lt;/id&gt;
        &lt;property name="name"/&gt;
        &lt;!-- The fetch join strategy uses a select join query to load with one transact the set of bids --&gt;
        &lt;property name="price"/&gt;
 &lt;-- LAZY est à TRUE --&gt;
        &lt;set name="bids" table="ITEM_BIDS" lazy="true" inverse="true" fetch="select" cascade="all"&gt;
            &lt;key column="ITEM_ID" not-null="true"/&gt;
            &lt;one-to-many class="org.touilleur.hibernate.v1.Bid"/&gt;
        &lt;/set&gt;
    &lt;/class&gt;

&lt;/hibernate-mapping&gt;</pre>
<p>Je relance le même test unitaire :<br />
</code><code><br />
INFO 2009-03-03 10:26:38,910 [Demo Hibernate] : testSelecTAllItemsWithDefaultFetchMode - begin<br />
Hibernate: /* criteria query */ select this_.ITEM_ID as ITEM1_0_0_, this_.name as name0_0_, this_.price as price0_0_ from ITEM this_<br />
Hibernate: /* load one-to-many org.touilleur.hibernate.v1.Item.bids */ select bids0_.ITEM_ID as ITEM4_1_, bids0_.BID_ID as BID1_1_, bids0_.BID_ID as BID1_1_0_, bids0_.amount as amount1_0_, bids0_.date as date1_0_, bids0_.ITEM_ID as ITEM4_1_0_ from BID bids0_ where bids0_.ITEM_ID=?<br />
INFO 2009-03-03 10:26:38,937 [Demo Hibernate] : Item #1 : Item{id=1, bids=[Bid{id=1, amount=101.0, date=2009-03-03 10:26:38.864}, Bid{id=10, amount=110.0, date=2009-03-03 10:26:38.883}, Bid{id=8, amount=108.0, date=2009-03-03 10:26:38.878}, Bid{id=3, amount=103.0, date=2009-03-03 10:26:38.867}, Bid{id=6, amount=106.0, date=2009-03-03 10:26:38.871}, Bid{id=2, amount=102.0, date=2009-03-03 10:26:38.866}, Bid{id=9, amount=109.0, date=2009-03-03 10:26:38.879}, Bid{id=5, amount=105.0, date=2009-03-03 10:26:38.87}, Bid{id=7, amount=107.0, date=2009-03-03 10:26:38.876}, Bid{id=4, amount=104.0, date=2009-03-03 10:26:38.868}], name='TV LCD', price=340.0}<br />
Hibernate: /* load one-to-many org.touilleur.hibernate.v1.Item.bids */ select bids0_.ITEM_ID as ITEM4_1_, bids0_.BID_ID as BID1_1_, bids0_.BID_ID as BID1_1_0_, bids0_.amount as amount1_0_, bids0_.date as date1_0_, bids0_.ITEM_ID as ITEM4_1_0_ from BID bids0_ where bids0_.ITEM_ID=?<br />
INFO 2009-03-03 10:26:38,944 [Demo Hibernate] : Item #2 : Item{id=2, bids=[Bid{id=18, amount=138.0, date=2009-03-03 10:26:38.896}, Bid{id=11, amount=10.0, date=2009-03-03 10:26:38.885}, Bid{id=20, amount=144.0, date=2009-03-03 10:26:38.898}, Bid{id=16, amount=116.0, date=2009-03-03 10:26:38.893}, Bid{id=14, amount=84.0, date=2009-03-03 10:26:38.888}, Bid{id=19, amount=139.0, date=2009-03-03 10:26:38.897}, Bid{id=15, amount=125.0, date=2009-03-03 10:26:38.892}, Bid{id=12, amount=22.0, date=2009-03-03 10:26:38.886}, Bid{id=13, amount=43.0, date=2009-03-03 10:26:38.887}, Bid{id=17, amount=137.0, date=2009-03-03 10:26:38.895}], name='Plasma', price=1230.0}<br />
INFO 2009-03-03 10:26:38,947 [Demo Hibernate] : testSelecTAllItemsWithDefaultFetchMode - end<br />
</code></p>
<p>Cette fois-ci nous constatons qu&#8217;Hibernate effectue une requête que lorsque cela devient nécessaire, ce qui est adapté dans la majorité des cas.<br />
Lorsque le mode lazy est désactivé, Hibernate effectue n+1 requêtes sans jointure. Cela peut entraîner un très grand nombre de requêtes vers la base. Nous allons donc voir comment optimiser dans ce cas précis le chargement des associations.</p>
<h3>Fetch strategy à join et lazy=false</h3>
<p>Si nous ne pouvons pas utiliser le chargement tardif, il est alors intéressant d&#8217;utiliser une requête et d&#8217;effectuer une jointure entre la table Item et la table Bid. Pour cela il suffit de changer de stratégie lors de la requête. Nous passons maintenant le mode de récupération (FetchMode) à JOIN :</p>
<pre class="java" name="code">
// voir la class org.touilleur.hibernate.SelectTest
 Item item1 = (Item) session.createCriteria(Item.class)
                .setFetchMode("bids", FetchMode.JOIN)
                .add(Restrictions.idEq(id))
                .uniqueResult();</pre>
<p>Si vous savez qu&#8217;il sera toujours plus intéresant d&#8217;utiliser une requête par jointure vous pouvez aussi changer la stratégie dans le fichier Item.hbm.xml :<br />
<code><br />
&lt;set name="bids" table="ITEM_BIDS" lazy="false" inverse="true" <strong>fetch="join"</strong> cascade="all"&gt;<br />
</code></p>
<p>Le test selectOneItemWithJoin nous montre alors qu&#8217;une seule requête est exécutée, et qu&#8217;une jointure externe gauche entre la table Item et la table Bid permet de retrouver un Item avec 0 ou plusieurs Bid.</p>
<p><code><br />
INFO 2009-02-27 22:24:21,772 [Demo Hibernate] : Starting selectOneItemWithJoin<br />
Hibernate: /* criteria query */ select this_.ITEM_ID as ITEM1_0_1_, this_.name as name0_1_, this_.price as price0_1_,<br />
    bids2_.ITEM_ID as ITEM4_3_, bids2_.BID_ID as BID1_3_, bids2_.BID_ID as BID1_1_0_, bids2_.amount as amount1_0_,<br />
    bids2_.date as date1_0_, bids2_.ITEM_ID as ITEM4_1_0_<br />
    from ITEM this_ left outer join BID bids2_<br />
    on this_.ITEM_ID=bids2_.ITEM_ID where this_.ITEM_ID = ?<br />
INFO 2009-02-27 22:24:21,775 [Demo Hibernate] : Loaded item1 with a FetchMode set to JOIN<br />
</code><br />
Notez qu&#8217;Hibernate utilise une jointure externe de la table Item vers la table Bid, cela nous donne alors en simplifiant le code SQL la requête suivante :<br />
<code>select i.*,b* from ITEM i<br />
   outer join BID b<br />
   on i.ITEM_ID=b.ITEM_ID<br />
</code><br />
Par défaut il n&#8217;y a donc pas de critères de distinction, et nous nous retrouvons alors avec 20 résultats, ce qui sera pratique pour remplir un tableau mais qui n&#8217;est pas forcément souhaitable pour votre code.</p>
<p><code>INFO 2009-03-03 10:30:53,041 [Demo Hibernate] : testSelecTAllItemsWithJoinFetchMode - begin<br />
Hibernate: /* criteria query */ select this_.ITEM_ID as ITEM1_0_1_, this_.name as name0_1_, this_.price as price0_1_, bids2_.ITEM_ID as ITEM4_3_, bids2_.BID_ID as BID1_3_, bids2_.BID_ID as BID1_1_0_, bids2_.amount as amount1_0_, bids2_.date as date1_0_, bids2_.ITEM_ID as ITEM4_1_0_ from ITEM this_ left outer join BID bids2_ on this_.ITEM_ID=bids2_.ITEM_ID<br />
INFO 2009-03-03 10:30:53,072 [Demo Hibernate] : Item #1 : Item{id=1, bids=[Bid{id=3, amount=103.0, date=2009-03-03 10:30:52.999}, Bid{id=8, amount=108.0, date=2009-03-03 10:30:53.01}, Bid{id=6, amount=106.0, date=2009-03-03 10:30:53.003}, Bid{id=10, amount=110.0, date=2009-03-03 10:30:53.014}, Bid{id=5, amount=105.0, date=2009-03-03 10:30:53.002}, Bid{id=2, amount=102.0, date=2009-03-03 10:30:52.998}, Bid{id=7, amount=107.0, date=2009-03-03 10:30:53.009}, Bid{id=4, amount=104.0, date=2009-03-03 10:30:53.001}, Bid{id=9, amount=109.0, date=2009-03-03 10:30:53.011}, Bid{id=1, amount=101.0, date=2009-03-03 10:30:52.997}], name='TV LCD', price=340.0}<br />
INFO 2009-03-03 10:30:53,074 [Demo Hibernate] : Item #1 : Item{id=1, bids=[Bid{id=3, amount=103.0, date=2009-03-03 10:30:52.999}, Bid{id=8, amount=108.0, date=2009-03-03 10:30:53.01}, Bid{id=6, amount=106.0, date=2009-03-03 10:30:53.003}, Bid{id=10, amount=110.0, date=2009-03-03 10:30:53.014}, Bid{id=5, amount=105.0, date=2009-03-03 10:30:53.002}, Bid{id=2, amount=102.0, date=2009-03-03 10:30:52.998}, Bid{id=7, amount=107.0, date=2009-03-03 10:30:53.009}, Bid{id=4, amount=104.0, date=2009-03-03 10:30:53.001}, Bid{id=9, amount=109.0, date=2009-03-03 10:30:53.011}, Bid{id=1, amount=101.0, date=2009-03-03 10:30:52.997}], name='TV LCD', price=340.0}<br />
...<br />
<strong>Message répété 20 fois</strong><br />
...<br />
INFO 2009-03-03 10:30:53,125 [Demo Hibernate] : testSelecTAllItemsWithJoinFetchMode - end<br />
</code><br />
Pour ajouter une clause DISTINCT, nous pouvons modifier notre critère et utiliser un ResultTransformer :</p>
<pre class="java" class="code">
List l = session.createCriteria(Item.class)
                .setFetchMode("bids", FetchMode.JOIN)
                .setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)
                .list();
</pre>
<p>En exécutant à nouveau notre test voici le résultat :<br />
<code><br />
INFO 2009-03-03 10:43:31,196 [Demo Hibernate] : testSelecTAllItemsWithJoinFetchMode - begin<br />
Hibernate: /* criteria query */ select this_.ITEM_ID as ITEM1_0_1_, this_.name as name0_1_, this_.price as price0_1_, bids2_.ITEM_ID as ITEM4_3_, bids2_.BID_ID as BID1_3_, bids2_.BID_ID as BID1_1_0_, bids2_.amount as amount1_0_, bids2_.date as date1_0_, bids2_.ITEM_ID as ITEM4_1_0_ from ITEM this_ left outer join BID bids2_ on this_.ITEM_ID=bids2_.ITEM_ID<br />
INFO 2009-03-03 10:43:31,229 [Demo Hibernate] : Item #1 : Item{id=1, bids=[Bid{id=8, amount=108.0, date=2009-03-03 10:43:31.162}, Bid{id=7, amount=107.0, date=2009-03-03 10:43:31.16}, Bid{id=2, amount=102.0, date=2009-03-03 10:43:31.149}, Bid{id=6, amount=106.0, date=2009-03-03 10:43:31.154}, Bid{id=10, amount=110.0, date=2009-03-03 10:43:31.167}, Bid{id=9, amount=109.0, date=2009-03-03 10:43:31.163}, Bid{id=3, amount=103.0, date=2009-03-03 10:43:31.15}, Bid{id=5, amount=105.0, date=2009-03-03 10:43:31.153}, Bid{id=1, amount=101.0, date=2009-03-03 10:43:31.147}, Bid{id=4, amount=104.0, date=2009-03-03 10:43:31.151}], name='TV LCD', price=340.0}<br />
INFO 2009-03-03 10:43:31,232 [Demo Hibernate] : Item #2 : Item{id=2, bids=[Bid{id=19, amount=139.0, date=2009-03-03 10:43:31.181}, Bid{id=17, amount=137.0, date=2009-03-03 10:43:31.179}, Bid{id=15, amount=125.0, date=2009-03-03 10:43:31.176}, Bid{id=13, amount=43.0, date=2009-03-03 10:43:31.171}, Bid{id=11, amount=10.0, date=2009-03-03 10:43:31.169}, Bid{id=12, amount=22.0, date=2009-03-03 10:43:31.17}, Bid{id=14, amount=84.0, date=2009-03-03 10:43:31.172}, Bid{id=20, amount=144.0, date=2009-03-03 10:43:31.182}, Bid{id=18, amount=138.0, date=2009-03-03 10:43:31.18}, Bid{id=16, amount=116.0, date=2009-03-03 10:43:31.177}], name='Plasma', price=1230.0}<br />
INFO 2009-03-03 10:43:31,235 [Demo Hibernate] : testSelecTAllItemsWithJoinFetchMode - end<br />
</code></p>
<p>Voir la FAQ Hibernate [<a href="http://www.hibernate.org/117.html#A12">Hibernate does not return distinct results for a query with outer join fetching enabled for a collection (even if I use the distinct keyword)</a>]</p>
<p>La sélection par jointure est donc une option possible. Nous sommes passés de 3 requêtes à une seule requête, ce qui est une optimisation permettant de réduire le nombre de requête SQL. Encore une fois, c&#8217;est à vous et à votre DBA de décider de la meilleur stratégie, il ne faut donc pas appliquer à l&#8217;aveugle les paramètres. Les jointures sont efficaces si vos colonnes de jointure sont correctement indexées, il est donc important de s&#8217;assurer que votre administrateur de base de données a aussi validé vos changements.</p>
<h3>Fetch mode par subselect</h3>
<p>Hibernate propose un mode de sélection moins connu et pourtant très efficace sur les associations, le mode subselect. L&#8217;idée est simplement d&#8217;utiliser le résultat d&#8217;une première requête comme critère de la sélection de la deuxième requête pour effectuer une requête plus efficace.</p>
<p>Ce que nous avons vu en premier, en mode lazy=false et fetch=select, c&#8217;est qu&#8217;Hibernate effectue n+1 requêtes pour charger la liste des Bids pour chacun des Items.</p>
<pre>
/* Retrouve la liste des Items */
select * from ITEM

/* Retrouve la list des Bids pour chaque Item */
select * from BID  where ITEM_ID = 12
select * from BID  where ITEM_ID = 34
select * from BID  where ITEM_ID = 37
select * from BID  where ITEM_ID = 39
</pre>
<p>Il y deux façons d&#8217;optimiser ce type de requête : soit utiliser le paramètre batch-size dans l&#8217;element set du fichier Item.hbm.xml, soit utiliser une requête de sous sélection. Je ne sais pas si ce mode est supporté par toutes les bases de données, mais voici le principe :<br />
- Sélectionner les Items<br />
- Sélectionner les Bids en utilisant les ID des items précedemment trouvé</p>
<pre>
/* Retrouve la liste des Items */
select * from ITEM

/* Retrouve la list des Bids pour chaque Item */
select * from BID  where ITEM_ID IN( 12, 34, 37, 39)
</pre>
<p>Pour cela il n&#8217;est pas possible de changer le mode de sélection dans le code, il faut éditer le fichier Item.hbm.xml et préciser que le fetch type est <strong>subselect</strong></p>
<pre name="code" class="xml">
&lt;hibernate-mapping&gt;
  &lt;class name="org.touilleur.hibernate.v1.Item" table="ITEM"&gt;
        &lt;id name="id" column="ITEM_ID"&gt;
            &lt;generator class="native"/&gt;
        &lt;/id&gt;
        &lt;property name="name"/&gt;
        &lt;!-- Nous testons subselect --&gt;
        &lt;property name="price"/&gt;
        &lt;set name="bids" table="ITEM_BIDS" lazy="false"
               inverse="true"
               fetch="subselect"
               cascade="all"&gt;
            &lt;key column="ITEM_ID" not-null="true"/&gt;
            &lt;one-to-many class="org.touilleur.hibernate.v1.Bid"/&gt;
        &lt;/set&gt;
    &lt;/class&gt;

&lt;/hibernate-mapping&gt;</pre>
<p>Après avoir édité le fichier Item.hbm.xml, le test unitaire testSelecTAllItemsWithSubSelectFetchMode nous montre que deux requêtes sont exécutées au lieu de n+1 dans le cas du mode &laquo;&nbsp;select&nbsp;&raquo; ou une seule requête dans le cas du mode &laquo;&nbsp;join&nbsp;&raquo; :<br />
<code><br />
INFO 2009-03-03 11:06:36,678 [Demo Hibernate] : testSelecTAllItemsWithSubSelectFetchMode - begin<br />
Hibernate: /* criteria query */ select this_.ITEM_ID as ITEM1_0_0_, this_.name as name0_0_, this_.price as price0_0_ from ITEM this_<br />
Hibernate: /* load one-to-many org.touilleur.hibernate.v1.Item.bids */<br />
select bids0_.ITEM_ID as ITEM4_1_, bids0_.BID_ID as BID1_1_,<br />
  bids0_.BID_ID as BID1_1_0_, bids0_.amount as amount1_0_, bids0_.date as date1_0_,<br />
  bids0_.ITEM_ID as ITEM4_1_0_<br />
from BID bids0_<br />
where bids0_.ITEM_ID<br />
   in (select this_.ITEM_ID from ITEM this_)<br />
INFO 2009-03-03 11:06:36,711 [Demo Hibernate] : Item #1 : Item{id=1, bids=[Bid{id=7, amount=107.0, date=2009-03-03 11:06:36.646}, Bid{id=3, amount=103.0, date=2009-03-03 11:06:36.636}, Bid{id=8, amount=108.0, date=2009-03-03 11:06:36.647}, Bid{id=2, amount=102.0, date=2009-03-03 11:06:36.635}, Bid{id=6, amount=106.0, date=2009-03-03 11:06:36.64}, Bid{id=5, amount=105.0, date=2009-03-03 11:06:36.639}, Bid{id=1, amount=101.0, date=2009-03-03 11:06:36.633}, Bid{id=9, amount=109.0, date=2009-03-03 11:06:36.648}, Bid{id=4, amount=104.0, date=2009-03-03 11:06:36.637}, Bid{id=10, amount=110.0, date=2009-03-03 11:06:36.652}], name='TV LCD', price=340.0}<br />
INFO 2009-03-03 11:06:36,714 [Demo Hibernate] : Item #2 : Item{id=2, bids=[Bid{id=14, amount=84.0, date=2009-03-03 11:06:36.657}, Bid{id=18, amount=138.0, date=2009-03-03 11:06:36.664}, Bid{id=17, amount=137.0, date=2009-03-03 11:06:36.663}, Bid{id=20, amount=144.0, date=2009-03-03 11:06:36.666}, Bid{id=15, amount=125.0, date=2009-03-03 11:06:36.66}, Bid{id=12, amount=22.0, date=2009-03-03 11:06:36.655}, Bid{id=13, amount=43.0, date=2009-03-03 11:06:36.656}, Bid{id=19, amount=139.0, date=2009-03-03 11:06:36.665}, Bid{id=16, amount=116.0, date=2009-03-03 11:06:36.661}, Bid{id=11, amount=10.0, date=2009-03-03 11:06:36.654}], name='Plasma', price=1230.0}<br />
INFO 2009-03-03 11:06:36,716 [Demo Hibernate] : testSelecTAllItemsWithSubSelectFetchMode - end</code></p>
<p>L&#8217;intérêt de ce mode de sélection est de récuperer en deux requêtes l&#8217;ensemble des Items et ensuite des Bids. Si vous faites enfin le test en mode &laquo;&nbsp;lazy=true&nbsp;&raquo; vous verrez par ailleurs qu&#8217;Hibernate n&#8217;effectue pas de chargement tardif mais qu&#8217;il charge toutes les données. Le mode de sélection en &laquo;&nbsp;SUBSELECT&nbsp;&raquo; limite à deux requêtes le nombre d&#8217;interrogation nécessaire, le tout sans jointure. C&#8217;est donc une option intéressante à tester selon la logique de votre application.</p>
<h3>Fetch mode par batch size</h3>
<p>La 4ème technique pour améliorer les chargements par lot est d&#8217;activer l&#8217;option batch-size. Afin de montrer son fonctionnement nous allons d&#8217;abord limiter la taille de chargement à 1 pour le nombre d&#8217;Item et le nombre de collection de Bid :</p>
<pre name="code" class="xml">
&lt;hibernate-mapping&gt;

    &lt;class name="org.touilleur.hibernate.v1.Item" table="ITEM" batch-size="1"&gt;
        &lt;id name="id" column="ITEM_ID"&gt;
            &lt;generator class="native"/&gt;
        &lt;/id&gt;
        &lt;property name="name"/&gt;
        &lt;!-- The fetch join strategy uses a select join query to load with one transact the set of bids --&gt;
        &lt;property name="price"/&gt;
        &lt;set name="bids" table="ITEM_BIDS"
              lazy="false"
              inverse="true"
              fetch="select"
              cascade="all"
              batch-size="1"&gt;
            &lt;key column="ITEM_ID" not-null="true"/&gt;
            &lt;one-to-many class="org.touilleur.hibernate.v1.Bid"/&gt;
        &lt;/set&gt;
    &lt;/class&gt;

&lt;/hibernate-mapping&gt;
</pre>
<p>Nous voyons qu&#8217;à l&#8217;exécution, Hibernate effectue une requete pour récuperer une liste de deux Items puis ensuite deux requêtes pour chacun des Items. En mode lazy, nous aurions simplement vu que la deuxième requête de chargement aurait été effectuée plus tardivement</p>
<p><code><br />
INFO 2009-03-03 13:19:19,261 [Demo Hibernate] : testSelecTAllItemsWithSubSelectFetchMode - begin<br />
Hibernate: /* criteria query */ select this_.ITEM_ID as ITEM1_0_0_, this_.name as name0_0_, this_.price as price0_0_ from ITEM this_<br />
Hibernate: /* load one-to-many org.touilleur.hibernate.v1.Item.bids */ select bids0_.ITEM_ID as ITEM4_1_, bids0_.BID_ID as BID1_1_, bids0_.BID_ID as BID1_1_0_, bids0_.amount as amount1_0_, bids0_.date as date1_0_, bids0_.ITEM_ID as ITEM4_1_0_ from BID bids0_ where bids0_.ITEM_ID=?<br />
Hibernate: /* load one-to-many org.touilleur.hibernate.v1.Item.bids */ select bids0_.ITEM_ID as ITEM4_1_, bids0_.BID_ID as BID1_1_, bids0_.BID_ID as BID1_1_0_, bids0_.amount as amount1_0_, bids0_.date as date1_0_, bids0_.ITEM_ID as ITEM4_1_0_ from BID bids0_ where bids0_.ITEM_ID=?<br />
INFO 2009-03-03 13:19:19,294 [Demo Hibernate] : Item #1 : Item{id=1, bids=[Bid{id=2, amount=102.0, date=2009-03-03 13:19:19.217}, Bid{id=5, amount=105.0, date=2009-03-03 13:19:19.221}, Bid{id=3, amount=103.0, date=2009-03-03 13:19:19.218}, Bid{id=10, amount=110.0, date=2009-03-03 13:19:19.233}, Bid{id=7, amount=107.0, date=2009-03-03 13:19:19.227}, Bid{id=4, amount=104.0, date=2009-03-03 13:19:19.219}, Bid{id=1, amount=101.0, date=2009-03-03 13:19:19.215}, Bid{id=9, amount=109.0, date=2009-03-03 13:19:19.23}, Bid{id=8, amount=108.0, date=2009-03-03 13:19:19.229}, Bid{id=6, amount=106.0, date=2009-03-03 13:19:19.222}], name='TV LCD', price=340.0}<br />
INFO 2009-03-03 13:19:19,297 [Demo Hibernate] : Item #2 : Item{id=2, bids=[Bid{id=19, amount=139.0, date=2009-03-03 13:19:19.247}, Bid{id=16, amount=116.0, date=2009-03-03 13:19:19.243}, Bid{id=11, amount=10.0, date=2009-03-03 13:19:19.236}, Bid{id=18, amount=138.0, date=2009-03-03 13:19:19.246}, Bid{id=13, amount=43.0, date=2009-03-03 13:19:19.237}, Bid{id=20, amount=144.0, date=2009-03-03 13:19:19.248}, Bid{id=14, amount=84.0, date=2009-03-03 13:19:19.238}, Bid{id=15, amount=125.0, date=2009-03-03 13:19:19.242}, Bid{id=17, amount=137.0, date=2009-03-03 13:19:19.245}, Bid{id=12, amount=22.0, date=2009-03-03 13:19:19.236}], name='Plasma', price=1230.0}<br />
INFO 2009-03-03 13:19:19,300 [Demo Hibernate] : testSelecTAllItemsWithSubSelectFetchMode - end<br />
</code></p>
<p>Le chargement par lot est une technique efficace qui permet de précharger un nombre défini de proxy non initialisé si un premier proxy est accedé. Il y a donc l&#8217;optimisation au niveau du chargement de l&#8217;entité (Item) et l&#8217;optimisation au niveau du chargement de la collection (Bid).</p>
<p>Le chargement par lot pour notre classe Item fonctionne de la manière suivante : dans votre base imaginons que nous ayons 10 Items, chacun de ces Items référencent un Acheteur avec une relation un-à-un. La relation est mappée avec un proxy en mode lazy=&nbsp;&raquo;true&nbsp;&raquo;. Si par défaut vous chargez la liste des Items puis qu&#8217;ensuite vous appelez la méthode getBuyer() pour retrouver l&#8217;acheteur, Hibernate effectuera alors 10 requêtes SQL (10 items).<br />
Le chargement par lot permet de spécifier à Hibernate la taille d&#8217;une fenêtre de chargement, en utilisant les clés primaires ou les clés étrangères, qui ici seraient dans la table BUYER.</p>
<p><code><br />
&lt;class name="Item" batch-size="10"&gt;...&lt;/class&gt;<br />
</code><br />
Hibernate exécutera non plus 10 requêtes SQL mais une seule pour charger l&#8217;ensemble des acheteurs. Idéalement vous l&#8217;aurez deviné, ce batch-size correspond au nombre d&#8217;éléments affichés sur une page Web, dans un tableau paginé par exemple.</p>
<p>Ensuite il est possible d&#8217;activer le chargement par lot sur les collections. Notre Item a une collection de Bid. Imaginons que je charge mes 10 Items dans ma Session Hibernate. Par défaut, chaque appel à getBids() entrainera alors une requête SQL, soit 10 requêtes pour charger chacune des collections. En spécifiant une taille de batch comme ci-dessous il est possible d&#8217;optimiser le nombre d&#8217;éléments préchargés et donc de réduire le nombre de requête SQL :</p>
<pre name="code" class="xml">
&lt;class name="Item"&gt;
    &lt;set name="Bid" batch-size="3"&gt;
        ...
    &lt;/set&gt;
&lt;/class&gt;
</pre>
<p>Pour 7 enchères, Hibernate chargera alors 3,3,1 en effectuant 3 requêtes au lieu de 7 requêtes par exemple. Cela permet de réduire encore une fois le nombre de requête, et cette valeur doit être guidée par le code de votre application et l&#8217;usage que vous en faîtes.<br />
Pour cette raison il est souvent plus souhaitable de définir la taille du batch au niveau du code comme dans ci-dessous:</p>
<pre>
 List l = session.createCriteria(Item.class)
                    .setFetchSize(10)
                    .list();
</pre>
<p>Petite astuce au passage : si vous souhaitez limiter le nombre de résultat retourné, vous connaissez sans doute la commande setMaxResults(int i).</p>
<pre>
List l = session.createCriteria(Item.class)
                    .setMaxResults(10)
                    .list();
</pre>
<p>Par curiosité je me suis un peu demandé pourquoi Hibernate prend plus de temps que la même requête SQL exécutée sur ma base de test. En fouillant un peu je me suis aperçu qu&#8217;il est intéressant de préciser la taille du batch afin de ne récuperer qu&#8217;un seul lot :</p>
<pre>
List l = session.createCriteria(Item.class)
                    .setMaxResults(10)
                    .setFetchSize(10)
                    .list();
</pre>
<p>Essayez sur votre code vous verrez la différence.</p>
<h3>Conclusion de la première partie</h3>
<p>Lorsque le chargement tardif ne peut pas être utilisé, il est important de vérifier que la stratégie par défaut d&#8217;Hibernate n&#8217;entraine pas un nombre de requêtes SQL trop importantes. Il est possible de limiter ce nombre en utilisant des jointures ou des selections imbriquées. A vous ensuite d&#8217;optimiser vos réglages selon votre application.</p>
<p>Dans la deuxième partie nous allons voir les 3 paramètres différents de chargement des collections.</p>
<p>J&#8217;espère que vous aurez apprécié. Si vous voulez aller un peu plus loin, il ne vous reste plus qu&#8217;à télécharger le code de l&#8217;article et à commencer à tester les différentes configurations possibles d&#8217;Hibernate. Pour les personnes à la recherche d&#8217;un tutorial sur Hibernate, la première partie vous montre un exemple simple de mapping pour commencer à apprendre Hibernate.</p>
<p>Bonne lecture et à bientôt pour la deuxième partie !</p>
<p><strong>Code source de l&#8217;article</strong><br />
<a href="http://www.touilleur-express.fr/divers/ArticleTouilleur_hibernate.tar.gz">ArticleTouilleur_hibernate.tar.gz</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.touilleur-express.fr/2009/03/03/hibernate-gerer-le-chargement-des-associations-efficacement/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Devoxx : que contiendra Java 7 ?</title>
		<link>http://www.touilleur-express.fr/2008/12/13/devoxx-que-contiendra-java-7/</link>
		<comments>http://www.touilleur-express.fr/2008/12/13/devoxx-que-contiendra-java-7/#comments</comments>
		<pubDate>Sat, 13 Dec 2008 16:25:35 +0000</pubDate>
		<dc:creator>Nicolas Martignole</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[devoxx]]></category>

		<guid isPermaLink="false">http://www.touilleur-express.fr/?p=586</guid>
		<description><![CDATA[Un journaliste a fait la remarque : les éditeurs font maintenant des annonces à Devoxx, car le lieu est l&#8217;endroit idéal pour lancer un produit ou un nouveau service.
Tout d&#8217;abord c&#8217;est l&#8217;annonce officielle de la sortie de JavaFX 1.0. De nombreuses conférences sur ce thème, une BOF, bref il y a eu un effet d&#8217;annonce très bien calibré. Ce qui m&#8217;a le plus intéressé, c&#8217;est les surprises de Java 7. Finalement la prochaine version de Java ne sortira pas avant début 2010, ce qui me fait penser que SUN et ...]]></description>
			<content:encoded><![CDATA[<p>Un journaliste a fait la remarque : les éditeurs font maintenant des annonces à Devoxx, car le lieu est l&#8217;endroit idéal pour lancer un produit ou un nouveau service.</p>
<p>Tout d&#8217;abord c&#8217;est l&#8217;annonce officielle de la sortie de JavaFX 1.0. De nombreuses conférences sur ce thème, une BOF, bref il y a eu un effet d&#8217;annonce très bien calibré. Ce qui m&#8217;a le plus intéressé, c&#8217;est les surprises de Java 7. Finalement la prochaine version de Java ne sortira pas avant début 2010, ce qui me fait penser que SUN et la communauté veulent prendre le temps nécessaire pour sortir une version vraiment innovante.</p>
<p>Lors de la Keynote du jeudi matin, Mark Reinold a un peu changé le programme de la présentation, car au lieu de &laquo;&nbsp;Java SE 7 updated&nbsp;&raquo;, sa présentation a commencé par &laquo;&nbsp;Modularity in Java SE&nbsp;&raquo;. Avant tout, avis de déces : la JSR-277 (Java Modules) est morte et enterrée. Cela a bien fait rire tout le monde lors de la présentation des JavaPosse l&#8217;après-midi. On parle maintenant de la JSR-294. <a href="http://blogs.sun.com/mr/">Son blog</a> (avec un look un peu soviétique) vous donnera l&#8217;actualité de ce qu&#8217;il se passe&#8230; car il y a un coté  un peu people qui ne m&#8217;intéresse pas tellement.</p>
<p>De son point de vue, OSGi n&#8217;est pas suffisant pour répondre aux soucis d&#8217;obésité de la JVM. L&#8217;objectif est d&#8217;offrir un système plus modulaire pour que le kernel de base soit plus léger. Cela s&#8217;appliquera à la JVM, au JRE et au SDK. Le débat de fond reste de savoir si cette modularité doit faire partie de Java 7 de facto ou pas&#8230;</p>
<p>Concernant les nouveautés du JDK 7, Mark a parlé de beaucoup de JSR dont voici la liste (sans doute incomplète car je n&#8217;ai pas tout eu le temps de récuperer) :<br />
- JSR 292 pour le support des langages Dynamiques<br />
- JSR 203 NIO2 (entrees / sorties nouvelle implémentation)<br />
- JSR 296 Swing Application framework (<a href="http://www.touilleur-express.fr/2008/02/17/presentation-de-la-jsr-296-swing-application-framework/">voir cet article</a>)<br />
- JSR 308 vise à <a href="http://www.infoq.com/news/2008/05/JSR-308">étendre les endroits où nous pourrions utiliser les Annotations</a> en Java&#8230;<br />
- JSR 166 package Concurrency avec de nouvelles Class pour ce package de Doug Lea (Fork/Join, Phasers, LinkedTransferQueue, ConcurrentReferenceHashMap, et Fences)<br />
- Smart Rethrow et surtout MultiCatchException, petite astuce qui permettra d&#8217;éviter les multiples catch(x) catch(y) et catch(z) qui n&#8217;améliorent pas la lisibilité du code.<br />
- JSR 295 Beans binding (?)</p>
<p>Le plus fun sera d&#8217;apprendre que <strong>il n&#8217;y aura pas de closures en Java 7</strong> ce qui a bien fait rire tout le monde (JSR 295). Après tout ce temps perdu sur les blogs, on a pas vraiment compris ensuite ce qu&#8217;ils comptent faire&#8230; Bref longue vie à Groovy qui a déjà ce système depuis sa création. Plus sérieusement, cela veut peut-être dire que les closures&#8230; en s&#8217;en fiche en Java. Certes c&#8217;est élégant, mais moi ma bonne grosse application de gestion&#8230; elle s&#8217;en fiche.</p>
<p>Au final ce qu&#8217;il faut retenir : l&#8217;objectif de Java 7 sera d&#8217;offrir un système modulaire afin que selon le type d&#8217;application, le nombre de classes et le temps de démarrage soit encore plus léger que Java 6. Il y a aura peu de révolutions du côté de la syntaxe, plus des chantiers comme un support des langages dynamiques. Tout ceci devrait occuper SUN et la communauté l&#8217;an prochain.</p>
<p><strong>Références complémentaires:</strong><br />
<a href="http://java.dzone.com/articles/java-7-update-mark-reinhold-de">http://java.dzone.com/articles/java-7-update-mark-reinhold-de</a><br />
<a href="http://blogs.sun.com/darcy/">http://blogs.sun.com/darcy/</a><br />
<a href="http://blogs.sun.com/mr/">Blog de Mark Reinhold</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.touilleur-express.fr/2008/12/13/devoxx-que-contiendra-java-7/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Mock JavaMail</title>
		<link>http://www.touilleur-express.fr/2008/12/09/mock-javamail/</link>
		<comments>http://www.touilleur-express.fr/2008/12/09/mock-javamail/#comments</comments>
		<pubDate>Tue, 09 Dec 2008 19:01:55 +0000</pubDate>
		<dc:creator>Nicolas Martignole</dc:creator>
				<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://www.touilleur-express.fr/?p=558</guid>
		<description><![CDATA[A quelques heures de mon départ pour Devoxx je termine une petite démo basée sur Apache Camel et Apache Wicket. Je vous proposerai bientôt un grand article à tremper dans votre café mais là aujourd&#8217;hui je fais la version courte.
Si vous avez déjà eu à tester JavaMail dans votre application, je parle de tests d&#8217;intégrations, sachez qu&#8217;il existe une petite librairie bien sympathique pour &#171;&#160;mocker&#160;&#187; du code Java. Disons que vous souhaitez envoyer un email de votre interface Web pour remettre un mot de passe à zéro. Comment écrire un ...]]></description>
			<content:encoded><![CDATA[<p>A quelques heures de mon départ pour Devoxx je termine une petite démo basée sur Apache Camel et Apache Wicket. Je vous proposerai bientôt un grand article à tremper dans votre café mais là aujourd&#8217;hui je fais la version courte.</p>
<p>Si vous avez déjà eu à tester JavaMail dans votre application, je parle de tests d&#8217;intégrations, sachez qu&#8217;il existe une petite librairie bien sympathique pour &laquo;&nbsp;mocker&nbsp;&raquo; du code Java. Disons que vous souhaitez envoyer un email de votre interface Web pour remettre un mot de passe à zéro. Comment écrire un test unitaire sans utiliser un vrai serveur SMTP ?</p>
<p><a href="https://mock-javamail.dev.java.net/">Mock JavaMail </a> permet de simuler une boîte aux lettres et de se comporter comme un serveur SMTP pour l&#8217;envoi, un serveur POP3 ou IMAP pour la réception.</p>
<p>Prenons par exemple un projet très simple. Voici tout d&#8217;abord la dépendance que j&#8217;ai ajoutée à mon fichier pom.xml :</p>
<pre name="code" class="xml">

       <dependency>
            <groupid>org.jvnet.mock-javamail</groupid>
            <artifactid>mock-javamail</artifactid>
            <version>1.7</version>
        </dependency>
</pre>
<p>Prenons ensuite un test unitaire qui génère un email afin de tester l&#8217;envoi. Tout d&#8217;abord voici le code à améliorer qui génère un email afin de l&#8217;envoyer via smtp</p>
<pre name="code"  class="java">
 private void sendSmtpEmail(Address from) throws MessagingException {
        Properties props = new Properties();
        //props.put("mail.smtp.host", "localhost");

        Session session = Session.getDefaultInstance(props, null);
        session.setDebug(true);

        // create a message
        SMTPMessage msg = new SMTPMessage(session);
        msg.setSubject("A simple test email");
        msg.setFrom(from);
        msg.setContent("Email content","text/plain");
        msg.setRecipients(MimeMessage.RecipientType.TO , "destinataire@yourdomain.com");

        Transport tr = session.getTransport("smtp");
        tr.connect("localhost", "anyUsername",  "password");

        msg.saveChanges();      // don't forget this
        tr.sendMessage(msg, msg.getAllRecipients());
        tr.close();
    }
</pre>
<p>Mock JavaMail se fiche pas mal du username et du password, donc vous pouvez mettre ce que vous voulez, tant que le serveur SMTP dans la construction de l&#8217;instance Transport est bien &laquo;&nbsp;localhost&nbsp;&raquo;.</p>
<p>Ensuite un exemple de code dans lequel je déclare un objet de type Mailbox, afin d&#8217;envoyer un email.</p>
<pre name="code" class="java">
    public void testSendMail() throws Exception {
        Address address = new InternetAddress("destinataire@yourdomain.com");
        Mailbox mailbox = new Mailbox(address);

        assertTrue("The mock mailbox is not empty whereas it should be", mailbox.isEmpty());

        // Classical code to send an email to a smtp server
        sendSmtpEmail(address);

        // Get a fresh reference to the mailbox
        mailbox=Mailbox.get("destinataire@yourdomain.com");
        // Check that a mail has been received
        assertFalse("The mock mailbox is empty", mailbox.isEmpty());

        Message message=mailbox.get(0);
        assertEquals("A simple test email",message.getSubject());
    }
</pre>
<p>Vous pouvez maintenant facilement tester l&#8217;envoi des messages de votre application et vous assurer que tout fonctionne sans devoir passer par un FakeSMTP <img src='http://www.touilleur-express.fr/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<p>Allez, rendez-vous à Devoxx.<br />
J&#8217;ai déjà récupéré mon teeshirt du Paris JUG grâce à David, la fin de semaine s&#8217;annonce chargée.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.touilleur-express.fr/2008/12/09/mock-javamail/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Brillez en société avec votre BlockingDeque</title>
		<link>http://www.touilleur-express.fr/2008/11/27/brillez-en-societe-avec-votre-blockingdeque/</link>
		<comments>http://www.touilleur-express.fr/2008/11/27/brillez-en-societe-avec-votre-blockingdeque/#comments</comments>
		<pubDate>Thu, 27 Nov 2008 09:49:53 +0000</pubDate>
		<dc:creator>Nicolas Martignole</dc:creator>
				<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://www.touilleur-express.fr/?p=543</guid>
		<description><![CDATA[Encore un café qui se transforme en article sur le Touilleur. Avec Florent Ramiere de Jaxio nous discutions sur des techniques de synchronisation. Voici le problème à résoudre : soit une liste d&#8217;éléments à maintenir correspondant à des tâches. Des threads empilent des tâches à effectuer dans cette queue pendant que d&#8217;autres threads récupèrent à la sortie des tâches. Cependant on souhaite que cette liste chaînée offre des services supplémentaires afin que les consommateurs attendent qu&#8217;une certaine quantité de tâches soit disponible. Nous voulons aussi pouvoir prendre soit la tâche ...]]></description>
			<content:encoded><![CDATA[<p>Encore un café qui se transforme en article sur le Touilleur. Avec Florent Ramiere de <a href="http://www.jaxio.com">Jaxio</a> nous discutions sur des techniques de synchronisation. Voici le problème à résoudre : soit une liste d&#8217;éléments à maintenir correspondant à des tâches. Des threads empilent des tâches à effectuer dans cette queue pendant que d&#8217;autres threads récupèrent à la sortie des tâches. Cependant on souhaite que cette liste chaînée offre des services supplémentaires afin que les consommateurs attendent qu&#8217;une certaine quantité de tâches soit disponible. Nous voulons aussi pouvoir prendre soit la tâche la plus ancienne (type FIFO) soit au contraire la tâche la plus récente de cette liste.</p>
<p>Là vous êtes perplexe&#8230; ne faites pas les timides je sais que vous n&#8217;avez rien compris.<br />
Reprenons le problème avec un oeil différent : dans un restaurant, nous avons 5 serveurs qui se chargent de poser des commandes sur un passe-plat. Le passe-plat est une table où les serveurs posent les commandes. Les commandes les plus récentes sont posées à droite, de sorte que la commande du client qui attend depuis le plus longtemps se trouve à gauche.</p>
<p>Les commandes sont enregistrées sur un tableau. Les 3 cuisiniers surveillent la liste des commandes. Un cuisinier dépile toujours la commande la plus à gauche posée sur ce passe-plat. Peu importe qu&#8217;un serveur l&#8217;ai posé à l&#8217;instant ou pas, il doit toujours traiter la commande la plus à gauche sur le passe-plat.</p>
<p>Je sais que c&#8217;est simpliste comme image, mais vous allez comprendre pourquoi lorsque je vais sortir la nouveauté de la saison 2008&#8230;</p>
<p>Ensuite nous voulons que les cuisiniers attendent qu&#8217;au moins 3 commandes soient présentes, afin d&#8217;optimiser la cuisson des aliments. L&#8217;idée est que lorsque 3 commandes sont présentes et que les cuisiniers n&#8217;ont rien à faire, ces 3 commandes sont retirées du passe-plat.<br />
Imaginez comment vos cuisiniers vont surveiller le passe-plat et attendre de manière synchrone que 3 commandes soient présentes&#8230;</p>
<p>Enfin comme nous ne voulons pas non plus laisser les clients en attente trop longtemps, une commande qui a plus de 5 mn d&#8217;ancienneté a le droit d&#8217;être dépilée&#8230; sans qu&#8217;il ne soit nécessaire d&#8217;attendre 2 autres commandes&#8230; Ok ?</p>
<p>Enfin pour terminer il faut aussi penser aux serveurs : nous souhaitons qu&#8217;ils attendent lorsque 5 commandes sont posées sur le passe-plat afin d&#8217;éviter d&#8217;engorger notre passe-plat pour rien. Dès qu&#8217;un cuisinier dépile une commande, le serveur sera alors débloqué et pourra poser sa commande sur le passe-plat.</p>
<p>Donc nous avons une liste chaînée, avec de la synchronisation. Nous avons aussi une notion de seuil à respecter pour les consommateurs. Et enfin nous avons une notion de temps limite pour s&#8217;amuser encore plus&#8230;</p>
<p>Il est temps de ressortir ses manuels et de regarder ce que nous avons en stock. Parlons d&#8217;abord &laquo;&nbsp;deque&nbsp;&raquo; (prononcer deck).</p>
<p><strong>C&#8217;est quoi une deque ?</strong><br />
A prononcer &laquo;&nbsp;Deck&nbsp;&raquo;, une deque est une &laquo;&nbsp;double-ended queue&nbsp;&raquo; soit une liste à double entrée en français dans le texte (on ne dit paspas double sortie en français, cela fait un peu trop tunning&#8230;). C&#8217;est une liste chainée composée d&#8217;une tête et d&#8217;une queue. La différence avec une Fifo est qu&#8217;il est possible de dépiler soit au début, soit à la fin là où une Fifo ne vous laisse accéder qu&#8217;à la fin. Java 6 propose une interface <a href="http://java.sun.com/javase/6/docs/api/java/util/Deque.html">Deque</a>. Les méthodes remarquables sont limpides :<br />
 </p>
<table border="0" cellspacing="1" cellpadding="3">
<tbody>
<tr>
<td> </td>
<td colspan="2" align="CENTER"><strong>First Element (Head)</strong></td>
<td colspan="2" align="CENTER"><strong>Last Element (Tail)</strong></td>
</tr>
<tr>
<td> </td>
<td align="CENTER"><em>Throws exception</em></td>
<td align="CENTER"><em>Special value</em></td>
<td align="CENTER"><em>Throws exception</em></td>
<td align="CENTER"><em>Special value</em></td>
</tr>
<tr>
<td><strong>Insert</strong></td>
<td><a href="http://java.sun.com/javase/6/docs/api/java/util/Deque.html#addFirst(E)"><code>addFirst(e)</code></a></td>
<td><a href="http://java.sun.com/javase/6/docs/api/java/util/Deque.html#offerFirst(E)"><code>offerFirst(e)</code></a></td>
<td><a href="http://java.sun.com/javase/6/docs/api/java/util/Deque.html#addLast(E)"><code>addLast(e)</code></a></td>
<td><a href="http://java.sun.com/javase/6/docs/api/java/util/Deque.html#offerLast(E)"><code>offerLast(e)</code></a></td>
</tr>
<tr>
<td><strong>Remove</strong></td>
<td><a href="http://java.sun.com/javase/6/docs/api/java/util/Deque.html#removeFirst()"><code>removeFirst()</code></a></td>
<td><a href="http://java.sun.com/javase/6/docs/api/java/util/Deque.html#pollFirst()"><code>pollFirst()</code></a></td>
<td><a href="http://java.sun.com/javase/6/docs/api/java/util/Deque.html#removeLast()"><code>removeLast()</code></a></td>
<td><a href="http://java.sun.com/javase/6/docs/api/java/util/Deque.html#pollLast()"><code>pollLast()</code></a></td>
</tr>
<tr>
<td><strong>Examine</strong></td>
<td><a href="http://java.sun.com/javase/6/docs/api/java/util/Deque.html#getFirst()"><code>getFirst()</code></a></td>
<td><a href="http://java.sun.com/javase/6/docs/api/java/util/Deque.html#peekFirst()"><code>peekFirst()</code></a></td>
<td><a href="http://java.sun.com/javase/6/docs/api/java/util/Deque.html#getLast()"><code>getLast()</code></a></td>
<td><code><a href="http://java.sun.com/javase/6/docs/api/java/util/Deque.html#peekLast()">peekLast()</a></p>
<p></code></td>
</tr>
</tbody>
</table>
<p>La méthode addFirst permet d&#8217;ajouter une nouvelle commande au début de notre passe-plat. Cependant si nous souhaitons faire passer une commande avant toutes les autres, la méthode addLast nous donne un droit d&#8217;écriture afin de placer notre commande à la fin.</p>
<p>La méthode peekLast permet à un cuisinier de s&#8217;assurer que la dernière tâche en attente n&#8217;est pas dans la pile depuis un certain temps&#8230; Mais ce n&#8217;est pas encore la solution que je souhaite vous présenter.</p>
<p><strong>BlockingDeque</strong><br />
Une BlockingDeque représentée par l&#8217;interface java.util.concurrent.BlockingDeque est une deque qui va attendre d&#8217;être non vide avant de laisser un consommateur dépiler un élément. Clairement : nos cuisiniers attendent sagement qu&#8217;une commande arrive. C&#8217;est aussi une Queue capable de faire attendre un serveur lorsque la liste est pleine, c&#8217;est de cette manière que nous allons faire attendre les serveurs lorsque la BlockingQueue est pleine.</p>
<p>En Java, <a href="http://java.sun.com/javase/6/docs/api/java/util/concurrent/BlockingDeque.html">l&#8217;interface BlockingDeque<e></e></a> est constituée entre autres des méthodes suivantes:</p>
<pre name="code" class="java">
public boolean offer (E element, long timeout, TimeUnit unit)

public boolean offerFirst(E e, long timeout, TimeUnit unit) throws InterruptedException

public boolean offerLast(E element,long timeout, TimeUnit unit)
</pre>
<p>La méthode offer permet d&#8217;ajouter un nouvel élément à la fin de la liste, en attendant une certaine quantité de temps précisée par la valeur timeout précisé par l&#8217;unité de <a href="http://java.sun.com/javase/6/docs/api/java/util/concurrent/TimeUnit.html">TimeUnit</a>. Si jamais finalement l&#8217;entrée n&#8217;a pas été ajoutée au bout du temps t, alors la méthode retournera false. Sinon elle retourne true. Pratique non ?</p>
<p>La méthode offerFirst permet de faire passer une commande devant toutes les autres, tout en restant avec la contrainte du timeout comme précédemment.</p>
<p>Du côté de nos consommateurs, les méthodes suivantes permettent soit de bloquer jusqu&#8217;à ce qu&#8217;une commande soit disponible, soit d&#8217;attendre tout en respectant un timeout.</p>
<p>Les méthodes takeFirst et takeLast sont bloquantes :</p>
<pre name="code" class="java">
public E takeFirst()
public E takeLast()
</pre>
<p>Les méthodes pollFirst() et pollLast() permettent d&#8217;examiner et d&#8217;attendre que la liste se remplisse, tout en restant dans un certain intervalle de temps comme précédemment :</p>
<pre name="code" class="java">
public E pollFirst(long timeout, TimeUnit unit)
public E pollLast(long timeout, TimeUnit unit)
</pre>
<p><strong>LinkedBlockingDeque</strong><br />
Cette classe est une implémentation en Java de l&#8217;interface et permet de fixer une capacité maximum afin de limiter le nombre d&#8217;éléments dans la liste :</p>
<pre name="code" class="java">
public LinkedBlockingDeque()
public LinkedBlockingDeque(java.util.Collection< ? extends E> c)
public LinkedBlockingDeque(int capacity)
</pre>
<p><strong>Un exemple complet</strong></p>
<p>Voici un exemple complet avec un Serveur et un Cuisinier. Ces deux objets accèdent la même BlockingQueue. La capacité de la queue est de 5 commandes.<br />
Pour faire simple : le serveur ajoute un Integer à la BlockingQueue toutes les 300 millisecondes. Le cuisinier prend un élément toutes les 3000 millisecondes. Si le Serveur fonctionne correctement, au bout de 1500 ms (5 x 300) la queue des commandes sera pleine. Dans ce cas les 5 autres commandes suivantes seront mises en attente automatiquement, le Serveur sera bloqué.</p>
<pre name="code" class="java">

import java.util.concurrent.BlockingDeque;

/**
 * A very simple producer class to demonstrate BlockingDeque functionnality.
 *
 * @author N.Martignole
 */
public class Serveur implements Runnable {
    private String name;
    private BlockingDeque&lt;Integer&gt; deque;
    // nous allons avoir 10 commandes a la suite
    private int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

    public Serveur(String name, BlockingDeque&lt;Integer&gt; deque) {
        this.name = name;
        this.deque = deque;
    }

    public void run() {
        for (int i = 0; i &lt; 10; i++) {
            try {
                deque.putFirst(numbers[i]);
                System.out.println(name + " place une nouvelle commande #" + numbers[i]);
                System.out.println(name + " affiche l'état de la queue: " + deque +"\n");
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
         }
   }
}
</pre>
<p>Voyons la classe du cuisinier :</p>
<pre name="code" class="java">
import java.util.concurrent.BlockingDeque;

public class Cuisinier implements Runnable {
    private String name;
    private BlockingDeque&lt;Integer&gt; deque;

    public Cuisinier(String name, BlockingDeque&lt;Integer&gt; deque) {
        this.name = name;
        this.deque = deque;
    }

    public void run() {
        for (int i = 0; i &lt; 10; i++) {
            try {
                int j= deque.takeLast();
                System.out.println("Cuisinier "+ name + " prend " + j);
                System.out.println(name + " regarde la queue: " + deque + "\n");
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
</pre>
<p>Enfin il nous faut une class de Test pour tester le tout:</p>
<pre name="code" class="java">
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;

public class Tester {

    public static void main(String[] args) {
        // Creation d'une queue pour notre passe plat de 5 cases
        BlockingDeque&lt;Integer&gt; deque = new LinkedBlockingDeque&lt;Integer&gt;(5);

        // Creation d'un serveur et d'un cuisinier pour faire simple
        Runnable serveur = new Serveur("Serveur", deque);
        Runnable cuisinier = new Cuisinier("Cuisinier", deque);

        // Lance le tout
        new Thread(serveur).start();
        new Thread(cuisinier).start();
    }
}
</pre>
<p>Avez-vous constaté que le Serveur attend 300 millisecondes avant de poser une nouvelle commande là où le cuisinier attend 3000 ms ? Pour 10 éléments, au bout de 1500 ms le Serveur sera donc bloqué car la queue sera pleine. Nous devrions donc le voir attendre jusqu&#8217;à ce qu&#8217;une place se libère.</p>
<p>Le résultat est intéressant :</p>
<pre name="code" class="java">
Serveur place une nouvelle commande #1
Serveur affiche l'état de la queue: [1]

Cuisinier commence la commande #1
Cuisinier regarde le passe-plat []

Serveur place une nouvelle commande #2
Serveur affiche l'état de la queue: [2]

Serveur place une nouvelle commande #3
Serveur affiche l'état de la queue: [3, 2]

Serveur place une nouvelle commande #4
Serveur affiche l'état de la queue: [4, 3, 2]

Serveur place une nouvelle commande #5
Serveur affiche l'état de la queue: [5, 4, 3, 2]

Serveur place une nouvelle commande #6
Serveur affiche l'état de la queue: [6, 5, 4, 3, 2]

Cuisinier a terminé la commande #1
Cuisinier commence la commande #2
Serveur place une nouvelle commande #7
Cuisinier regarde le passe-plat [7, 6, 5, 4, 3]

Serveur affiche l'état de la queue: [7, 6, 5, 4, 3]

Cuisinier a terminé la commande #2
Cuisinier commence la commande #3
Serveur place une nouvelle commande #8
Cuisinier regarde le passe-plat [8, 7, 6, 5, 4]

Serveur affiche l'état de la queue: [8, 7, 6, 5, 4]

Cuisinier a terminé la commande #3
Cuisinier commence la commande #4
Cuisinier regarde le passe-plat [8, 7, 6, 5]

Serveur place une nouvelle commande #9
Serveur affiche l'état de la queue: [9, 8, 7, 6, 5]

Cuisinier a terminé la commande #4
Cuisinier commence la commande #5
Serveur place une nouvelle commande #10
Cuisinier regarde le passe-plat [10, 9, 8, 7, 6]

Serveur affiche l'état de la queue: [10, 9, 8, 7, 6]

Le serveur a terminé de placer les commandes
Cuisinier a terminé la commande #5
Cuisinier commence la commande #6
Cuisinier regarde le passe-plat [10, 9, 8, 7]

Cuisinier a terminé la commande #6
Cuisinier commence la commande #7
Cuisinier regarde le passe-plat [10, 9, 8]

Cuisinier a terminé la commande #7
Cuisinier commence la commande #8
Cuisinier regarde le passe-plat [10, 9]

Cuisinier a terminé la commande #8
Cuisinier commence la commande #9
Cuisinier regarde le passe-plat [10]

Cuisinier a terminé la commande #9
Cuisinier commence la commande #10
Cuisinier regarde le passe-plat []

Cuisinier a terminé la commande #10
Cuisinier a terminé de traiter les commandes
</pre>
<p>Comme on peut le constater, avec peu d&#8217;effort il est possible d&#8217;implémenter un système synchrone propre, sans faire appel à du vieux code comme synchronized, wait ou join. C&#8217;est l&#8217;une des forces de l&#8217;api Concurrent en Java qui permet de programmer des cas complexes comme celui-ci en quelques lignes.</p>
<p>Pour terminer voyons le code du Tester avec 2 Cuisniers. J&#8217;y ajoute une notion de timeout afin que les cuisiniers attendent 1000 ms au maximum lorsque la queue est vide. Pourquoi cela ? Car chacun des 2 cuisiniers va traiter 5 commandes, bien que cela ne soit pas déterministe. Je ne peux pas baser mon code uniquement sur la supposition que chacun des cuisiniers traite 5 commandes, rien ne me dit que par exemple le GarbageCollector ne va pas perturber mon code. Pour cela la solution est simple, il suffit d&#8217;ajouter un timeout comme nous l&#8217;avons vu au début de cette article, dans la class Cuisinier .</p>
<p>La méthode <strong>run() </strong>de Cuisinier devient alors :</p>
<pre name="code" class="java">
 public void run() {
        for (int i = 0; i &lt; 5; i++) {
            try {
                int j= deque.pollLast(1000, TimeUnit.MILLISECONDS);

                System.out.println(name + " commence la commande #" + j);
                System.out.println(name + " regarde le passe-plat " + deque + "\n");
                Thread.sleep(3000);
                System.out.println(name+ " a terminé la commande #"+j);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(name+" a terminé de traiter les commandes");
    }
</pre>
<p>Le résultat de l&#8217;exécution donnera alors :</p>
<pre name="code" class="java">
Serveur place une nouvelle commande #1
Serveur affiche l'état de la queue: [1]

Cuisinier 1 commence la commande #1
Cuisinier 1 regarde le passe-plat []

Serveur place une nouvelle commande #2
Serveur affiche l'état de la queue: [2]

Cuisinier 2 commence la commande #2
Cuisinier 2 regarde le passe-plat []

Serveur place une nouvelle commande #3
Serveur affiche l'état de la queue: [3]

Serveur place une nouvelle commande #4
Serveur affiche l'état de la queue: [4, 3]

Serveur place une nouvelle commande #5
Serveur affiche l'état de la queue: [5, 4, 3]

Serveur place une nouvelle commande #6
Serveur affiche l'état de la queue: [6, 5, 4, 3]

Serveur place une nouvelle commande #7
Serveur affiche l'état de la queue: [7, 6, 5, 4, 3]

Cuisinier 1 a terminé la commande #1
Cuisinier 1 commence la commande #3
Serveur place une nouvelle commande #8
Cuisinier 1 regarde le passe-plat [8, 7, 6, 5, 4]

Serveur affiche l'état de la queue: [8, 7, 6, 5, 4]

Cuisinier 2 a terminé la commande #2
Cuisinier 2 commence la commande #4
Cuisinier 2 regarde le passe-plat [9, 8, 7, 6, 5]

Serveur place une nouvelle commande #9
Serveur affiche l'état de la queue: [9, 8, 7, 6, 5]

Cuisinier 1 a terminé la commande #3
Cuisinier 1 commence la commande #5
Cuisinier 1 regarde le passe-plat [9, 8, 7, 6]

Serveur place une nouvelle commande #10
Serveur affiche l'état de la queue: [10, 9, 8, 7, 6]

Le serveur a terminé de placer les commandes
Cuisinier 2 a terminé la commande #4
Cuisinier 2 commence la commande #6
Cuisinier 2 regarde le passe-plat [10, 9, 8, 7]

Cuisinier 1 a terminé la commande #5
Cuisinier 1 commence la commande #7
Cuisinier 1 regarde le passe-plat [10, 9, 8]

Cuisinier 2 a terminé la commande #6
Cuisinier 2 commence la commande #8
Cuisinier 2 regarde le passe-plat [10, 9]

Cuisinier 1 a terminé la commande #7
Cuisinier 1 commence la commande #9
Cuisinier 1 regarde le passe-plat [10]

Cuisinier 2 a terminé la commande #8
Cuisinier 2 commence la commande #10
Cuisinier 2 regarde le passe-plat []

Cuisinier 1 a terminé la commande #9
Cuisinier 1 a terminé de traiter les commandes
Cuisinier 2 a terminé la commande #10
Cuisinier 2 a terminé de traiter les commandes
</pre>
<p>En conclusion nous avons vu un exemple simple de l&#8217;utilisation de la class LinkedBlockingQueue. Cette classe du package Concurrent de Java 6 offre des fonctionnalités puissantes suffisantes pour répondre à un grand nombre de cas pratiques et réalistes.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.touilleur-express.fr/2008/11/27/brillez-en-societe-avec-votre-blockingdeque/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Nouveau : un podcast le Touilleur Express</title>
		<link>http://www.touilleur-express.fr/2008/11/15/nouveau-un-podcast-le-touilleur-express/</link>
		<comments>http://www.touilleur-express.fr/2008/11/15/nouveau-un-podcast-le-touilleur-express/#comments</comments>
		<pubDate>Sat, 15 Nov 2008 11:23:48 +0000</pubDate>
		<dc:creator>Nicolas Martignole</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[exo]]></category>
		<category><![CDATA[groovy]]></category>
		<category><![CDATA[podcast]]></category>
		<category><![CDATA[spring]]></category>

		<guid isPermaLink="false">http://www.touilleur-express.fr/?p=489</guid>
		<description><![CDATA[C&#8217;est fait, le premier podcast du Touilleur Express est en ligne sur sa page dédiée. Le premier épisode vous propose d&#8217;écouter Guillaume Laforge, le chef de projet du langage Groovy. SpringSource a annoncé le 11 novembre dernier le rachat de sa société, G2One.inc. A l&#8217;occasion de la journée &#171;&#160;Les Rencontres Spring&#160;&#187; nous avons été invité par SFEIR à rencontrer plusieurs personnes de SpringSource. J&#8217;ai enregistré une petite interview de guillaume avec Olivier Croisier, auteur du blog &#171;&#160;The Coder&#8217;s Breakfast&#171;&#160;.
Voici des pointeurs vers des sujets abordés dans le podcast :

article sur ...]]></description>
			<content:encoded><![CDATA[<div id="attachment_501" class="wp-caption alignnone" style="width: 310px"><a href="http://www.touilleur-express.fr/wp-content/nicolas_martignole_guillaume_laforge.jpg"><img src="http://www.touilleur-express.fr/wp-content/nicolas_martignole_guillaume_laforge-300x187.jpg" alt="N.Martignole et G.Laforge" title="nicolas_martignole_guillaume_laforge" width="300" height="187" class="size-medium wp-image-501" /></a><p class="wp-caption-text">N.Martignole et G.Laforge</p></div>
<p>C&#8217;est fait, le premier podcast du Touilleur Express est en ligne sur <a href="http://www.touilleur-express.fr/podcast_page/">sa page dédiée</a>. Le premier épisode vous propose d&#8217;écouter Guillaume Laforge, le chef de projet du langage Groovy. SpringSource a annoncé le 11 novembre dernier le rachat de sa société, G2One.inc. A l&#8217;occasion de la journée &laquo;&nbsp;Les Rencontres Spring&nbsp;&raquo; nous avons été invité par SFEIR à rencontrer plusieurs personnes de SpringSource. J&#8217;ai enregistré une petite interview de guillaume avec Olivier Croisier, auteur du blog &laquo;&nbsp;<a href="http://olivier.croisier.free.fr/blog/">The Coder&#8217;s Breakfast</a>&laquo;&nbsp;.</p>
<p>Voici des pointeurs vers des sujets abordés dans le podcast :</p>
<ul>
<li><a href="http://www.infoq.com/news/2008/11/springsource-g2one">article sur InfoQ</a></li>
<li><a href="http://glaforge.free.fr/weblog/">Le Blog de Guillaume</a></li>
<li><a href="http://www.exoplatform.com/portal/public/en/">The eXo Platform</a></li>
<li><a href="http://www.pilotsystems.net/actus/benjamin-mestrallet-exo-platform-prix-jeune-dirigeant-technologie">Benjamin Mestrallet, fondateur d&#8217;eXo, gagne le prix du jeune dirigeant de la Technologie</a></li>
</ul>
<p>A venir prochainement : interview avec Peter Cooper-Ellis. La qualité n&#8217;étant pas assez bonne, je vous proposerai une transcription de notre rencontre avec Peter prochainement.</p>
<p>En attendant voici une photo prise chez SFEIR, avec donc de gauche à droite : Peter Cooper-Ellis, moi-même et enfin <a href="http://olivier.croisier.free.fr/blog/">Olivier Croisier</a>.<br />
<div id="attachment_490" class="wp-caption alignnone" style="width: 310px"><a href="http://www.touilleur-express.fr/wp-content/img_5744.jpg"><img src="http://www.touilleur-express.fr/wp-content/img_5744-300x200.jpg" alt="Interview Peter Cooper-Ellis avec Nicolas Martignole et Olivier Croisier" title="Interview Peter Cooper-Ellis avec Nicolas Martignole et Olivier Croisier" width="300" height="200" class="size-medium wp-image-490" /></a><p class="wp-caption-text">Interview Peter Cooper-Ellis avec Nicolas Martignole et Olivier Croisier</p></div></p>
]]></content:encoded>
			<wfw:commentRss>http://www.touilleur-express.fr/2008/11/15/nouveau-un-podcast-le-touilleur-express/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>LA grosse nouvelle : SpringSource rachète G2One Groovy</title>
		<link>http://www.touilleur-express.fr/2008/11/11/la-grosse-nouvelle-springsource-rachete-g2one-groovy/</link>
		<comments>http://www.touilleur-express.fr/2008/11/11/la-grosse-nouvelle-springsource-rachete-g2one-groovy/#comments</comments>
		<pubDate>Tue, 11 Nov 2008 09:36:08 +0000</pubDate>
		<dc:creator>Nicolas Martignole</dc:creator>
				<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://www.touilleur-express.fr/?p=469</guid>
		<description><![CDATA[Merci à Didier Girard et Twitter, finalement c&#8217;est grâce à lui que ce matin j&#8217;ai appris la nouvelle dont nous avait parlé Julien il y a quelques semaines : SpringSource rachète la société G2one, la société qui édite le langage Groovy et l&#8217;outil Grails.
Guillaume Laforge sera donc présent jeudi matin aux &#171;&#160;Rencontres Spring 2008&#160;&#187; après sa participation la veille à la journée &#171;&#160;Open Source Exchange&#160;&#187; organisée par Xebia et Skills Matter.
]]></description>
			<content:encoded><![CDATA[<p>Merci à <a href="http://www.application-servers.com/post/2008/11/11/SpringSource-rachegravete-Groovy-et-Grails">Didier Girard et Twitter</a>, finalement c&#8217;est grâce à lui que ce matin j&#8217;ai appris la nouvelle dont nous avait parlé Julien il y a quelques semaines : <a href="http://www.theregister.co.uk/2008/11/11/springsource_g21/">SpringSource rachète la société G2one</a>, la société qui édite le langage Groovy et l&#8217;outil Grails.<br />
Guillaume Laforge sera donc présent jeudi matin aux &laquo;&nbsp;<a href="http://www.rencontres-spring.com/">Rencontres Spring 2008</a>&nbsp;&raquo; après sa participation la veille à la journée &laquo;&nbsp;<a href="http://skillsmatter.com/event-details/home/skills-matter-open-source-exchange">Open Source Exchange</a>&nbsp;&raquo; organisée par Xebia et Skills Matter.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.touilleur-express.fr/2008/11/11/la-grosse-nouvelle-springsource-rachete-g2one-groovy/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

