Sur la route d'Oxiane digressions diverses

LeBlog OXiane

5 juin
2011

JPA dans un Servlet

On trouve de nombreux tutoriels pour débuter avec JPA. La question s’est posée récemment de l’utilisation de JPA dans un contexte Web simple, ce que l’on peut résumer par « Comment utiliser JPA dans un Servlet ? ». Là, plusieurs petits problèmes surviennent et le web est étonnement avare en explications claires et « bouts de code prêts à l’emploi ». Voici donc comment faire.

Exemple autonome

Tout d’abord, commençons par refaire un exemple simple avec une application autonome.

Pour cela, il nous faut :

  • Créer un projet Java standard dans son IDE préféré (j’utilise Eclipse)
  • Configurer son classpath pour utiliser JPA (j’ajoute la librairie JBoss 5.1 Runtime)
  • Créer une entité persistante (une simple classe annotée)
    package com.oxiane;
    
    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.Id;
    
    @Entity
    public class EntitePersistente {
    
      @Id @GeneratedValue
      private Long id;
    
      private String valeur;
    
      //accesseurs
      ...
    }
  • Créer un fichier persistence.xml dans le répertoire src/META-INF
    <?xml version="1.0" encoding="UTF-8"?>
    <persistence version="2.0"
      xmlns="http://java.sun.com/xml/ns/persistence"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
         http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
      <persistence-unit name="TestUnit">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>
        <properties>
          <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver" />
          <property name="hibernate.connection.username" value="root" />
          <property name="hibernate.connection.password" value="" />
          <property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/test" />
          <property name="hibernate.hbm2ddl.auto" value="update" />
          <property name="hibernate.show_sql" value="true" />
        </properties>
      </persistence-unit>
    </persistence>
  • Et une classe avec un « main » pour utiliser tout ça :
    package com.oxiane;
    
    import javax.persistence.EntityManager;
    import javax.persistence.EntityManagerFactory;
    import javax.persistence.EntityTransaction;
    import javax.persistence.Persistence;
    
    public class TestJPA {
      public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("TestUnit");
        EntityManager em = emf.createEntityManager();
        EntityTransaction tx = em.getTransaction();
        try {
          tx.begin();
          EntitePersistente entite = new EntitePersistente();
          entite.setValeur(""+System.currentTimeMillis());
          em.persist(entite);
          tx.commit();
          System.out.println("Entité persistée, id:"+entite.getId());
        } catch (Exception e) {
          e.printStackTrace();
          System.out.println("Oops ! petit problème");
          tx.rollback();
        } finally {
          em.close();
          emf.close();
        }
      }
    }
  • Il ne reste plus qu’à démarrer son MySql et à exécuter pour obtenir une trace dans la console du genre :
    Hibernate: insert into EntitePersistente (valeur) values (?)
    Entité persistée, id:1

On trouve facilement des infos pour faire la même chose dans un EJB

Dans ce cas, c’est même plus simple.

  • On passe par une DataSource, ce qui modifie le fichier persistence.xml :
  • <?xml version="1.0" encoding="UTF-8"?>
    <persistence version="2.0"
      xmlns="http://java.sun.com/xml/ns/persistence"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
         http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
      <persistence-unit name="TestUnit">
        <jta-data-source>jdbc/TestDS</jta-data-source>
        <properties>
          <property name="hibernate.hbm2ddl.auto" value="update" />
          <property name="hibernate.show_sql" value="true" />
        </properties>
      </persistence-unit>
    </persistence>
  • Le code est beaucoup plus simple puisqu’on se fait injecter l’EntityManager et que la transaction est gérée par le conteneur :
  • package com.oxiane;
    
    import javax.ejb.Remote;
    import javax.ejb.Stateless;
    import javax.persistence.EntityManager;
    import javax.persistence.PersistenceContext;
    
    @Stateless(mappedName="ejb/Test")
    @Remote(JPAServiceRemote.class)
    public class JPAServiceBean implements JPAServiceRemote, JPAServiceLocal {
    
      @PersistenceContext
      private EntityManager em;
    
      public JPAServiceBean() {
      }
    
      public Long test() {
          EntitePersistente entite = new EntitePersistente();
        entite.setValeur("" + System.currentTimeMillis());
        em.persist(entite);
        return entite.getId();
      }
    }

Que se passe-t-il maintenant si on veut faire la même chose dans un servlet ?

Là c’est moins simple et moins clair.
Tout d’abord, l’injection avec @PersistenceContexte n’est plus permise. Cela conduirait à partager l’EntityManager en cas de requêtes simultanées. A la place, il faut utiliser l’annotation @PersistenceUnit pour injecter l’EntityManagerFactory qui lui est partageable et « Thread-Safe ».

  • Injection du PersistenceUnit :
    @PersistenceUnit
    private EntityManagerFactory emf;
  • Il est donc à nouveau nécessaire de créer explicitement un EntityManager à partir de la factory :
    // !!! ne fonctionne pas !!!
    protected void doGet(HttpServletRequest request,
          HttpServletResponse response) throws ServletException, IOException {
    
        PrintWriter out = response.getWriter();
        EntityManager em = emf.createEntityManager();
        EntitePersistente entite = new EntitePersistente();
        entite.setValeur("" + System.currentTimeMillis());
        em.persist(entite);
        out.write("Entité persistée, id:" + entite.getId());
      }

Bien qu’il n’y ait pas d’erreur à l’exécution, cela ne fonctionne pas. En effet, on ne bénéficie plus de la gestion de transaction du conteneur. Il faut à nouveau gérer explicitement la transaction.
Ici deux approches sont possibles que l’on peut résumer par « JTA ou pas ? »

L’approche non-JTA

Elle signifie que l’on va gérer les transactions directement avec l’EntityManager (comme dans l’exemple autonome initial).

  • Dans ce cas, le fichier persistence.xml doit indiquer explicitement ce mode de gestion :
    <?xml version="1.0" encoding="UTF-8"?>
    <persistence version="2.0"
      xmlns="http://java.sun.com/xml/ns/persistence"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
         http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
      <persistence-unit name="TestUnit" transaction-type="RESOURCE_LOCAL">
        <non-jta-data-source>jdbc/TestDS</non-jta-data-source>
        <properties>
          <property name="hibernate.hbm2ddl.auto" value="update" />
          <property name="hibernate.show_sql" value="true" />
        </properties>
      </persistence-unit>
    </persistence>
  • Le code du Servlet devient alors :
    @PersistenceUnit
    private EntityManagerFactory emf;
    
    //gestion des transactions non JTA
    protected void doGet(HttpServletRequest request,
          HttpServletResponse response) throws ServletException, IOException {
    
        PrintWriter out = response.getWriter();
        EntityManager em = emf.createEntityManager();
        EntityTransaction tx = em.getTransaction();
        tx.begin();
        EntitePersistente entite = new EntitePersistente();
        entite.setValeur("" + System.currentTimeMillis());
        em.persist(entite);
        tx.commit();
        out.write("Entité persistée, id:" + entite.getId());
        em.close();
    }

L’approche JTA

L’autre approche consiste à utiliser des transactions JTA. Dans ce cas, le fichier de persistence.xml est le même que dans le cas des EJB mais il faut maintenant gérer explicitement une transaction JTA.
Le plus simple est de se faire injecter la UserTransaction avec l’annotation @Resource, en plus de la factory.

  • Injection de la UserTransaction
    @Resource
    private UserTransaction tx;
    
    @PersistenceUnit
    private EntityManagerFactory emf;
  • Remarque : il peut paraître surprenant d’avoir un tel attribut dans le Servlet. Il faut juste comprendre que le UserTransaction n’est pas un objet représentant la transaction mais une interface, Thread-Safe, de manipulation des transactions. Elle peut donc être partagée sans problème.

  • Le code devient alors :
    protected void doGet(HttpServletRequest request,
      HttpServletResponse response) throws ServletException, IOException {
    
      PrintWriter out = response.getWriter();
      EntityManager em = null;
      try {
        tx.begin();
        em = emf.createEntityManager();
        EntitePersistente entite = new EntitePersistente();
        entite.setValeur("" + System.currentTimeMillis());
        em.persist(entite);
        tx.commit();
        out.write("Entité persistée, id:" + entite.getId());
      } catch (Exception e) {
        try {
          tx.rollback();
        } catch (Exception e1) {
          e1.printStackTrace();
        }
        out.write("Oops ! petit problème");
      }
      finally {
        if (em != null) {
          em.close();
        }
      }
    }

Notez au passage une subtilité un peu déroutante : dans le cas des transactions JTA, l’EntityManager doit être créé après le début de la transaction pour qu’il soit géré par celle-ci.
Si on ne respecte pas cette contrainte, on retombe dans la situation précédente où il ne se produit pas d’erreur mais rien n’est enregistré.

Jean-Francois Lefevre

jflefevre

2 juin
2011

Développeurs graphistes chefs de projet assistantes

Une humble contribution à la compréhension du microcosme de l’informaticien !

L’article original duquel l’image est tirée, et à laquelle je n’ai rajouté que les lignes « assistantes » est ici.

 

Alain Boudard

aboudard

25 mai
2011

OXiane Studio : KiteSpirit.com

Créée en 2004, par Arnaud et Sébastien Murcia, 2 passionnés de sport de glisse, la société « Kite Spirit » est spécialisée dans la vente de matériel de kitesurf, et plus généralement, de sports de glisse.

Impliquée dans le E-commerce depuis 2005, la boutique « kite-spirit.com » développée à partir de la solution Os Commerce, fait aujourd’hui peau-neuve, et change de plate-forme, puisque c’est désormais sous Presta Shop qu’elle sera développée, la réalisation est assurée chez OXiane Studio par Alain Boudard.

Avec cette version plus moderne, la E-boutique devrait bénéficier d’un meilleur référencement, avec des pages plus lisibles pour les moteurs de recherche, une architecture, et une ergonomie très abouties.

L’interface  Prestashop permet de mettre en place des fonctions plus avancées, telles que la gestion des packs matériels, des bons de réduction, une version anglaise, et une présentation plus dynamique des fiches produits…

En 2011, Kite Spirit transfère également ses locaux du centre ville d’Auray, pour se retrouver en périphérie de la ville dans un magasin plus spacieux. La société a pour ambition d’augmenter ses stocks, tout en diversifiant l’offre de son catalogue, avec notamment l’arrivée du Stand Up Paddle Surf, une pratique émergente au sein de la famille des sports de glisse.

Alain Boudard

aboudard

4 mai
2011

Présentation OXiane sous Sencha Touch

Voici un petit aperçu de ce que l’on peut réaliser avec l’API Sencha Touch.

http://www.oxiane.com/touch/

Ici une mini application mobile avec un mélange de Javascript et de Media Queries pour adapter les slides à l’écran.

Même si je ne suis pas fan de l’API, il y a des choses intéressantes.

Il faut quand même s’habituer à ne pas coder de HTML du tout, par exemple.

var carousel1 = new Ext.Carousel({ ... }

Je préfère réellement l’API JQuery qui se situe plus en tant qu’inspecteur de code, plutôt qu’en tant que générateur, ou de pseudo-template. Mais chacun ses gouts ! Bientôt nous verrons cette même présentation avec quelques lignes de code JQuery. On profite de la sortie de la version 1.6 de cette API ;)

Alain Boudard

aboudard

26 avr
2011

Composants mobiles : Adobe CS5.5 et IBM se lancent

Depuis quelque semaines les news tombent. Le monde du développement mobile évolue très rapidement.

IBM sponsorise un projet open-source ambitieux : Maqetta. Le but : fournir un IDE complet et performant pour développer vos applications HTML5 / CSS3 ! Ambitieux étant donné la stabilité des specs qui n’en sont toujours pas à l’heure où on écrit ces lignes :) Contrairement à ce qu’on peut lire à droite à gauche, ce n’est pas du tout une alternative à Flash, au mieux ce serait un cousin de Flex / FlashBuilder, car le focus est mis sur la conception d’interface web complexe en se basant sur les outils reconnus que sont :

  • JQueryUI
  • Dojo (normal le projet est maintenu par dojofoundation)
  • YUI (Yahoo User Interface)

Il reste à installer le produit, pour l’instant je n’ai pas pu tester, c’est malheureusement un peu classique avec ce genre de « truc » open-source, il faut déjà être affuté pour comprendre comment ne serait-ce qu’installer le machin !

Pour l’instant, le concurrent de Flash est encore loin, mais il arrivera, et certainement via Adobe, qui sortait il y a peu la version 5.5 de sa Creative Suite, avec en particulier les outils de développement Mobile :

Le support de HTML5, les API mobiles avec en particulier l’intégration de JQuery Mobile dans Dreamweaver ! Le développement mobile rentre en phase « sérieuse », les outils en sont la marque.

Alain

Alain Boudard

aboudard