Publié par

Il y a 10 années -

Temps de lecture 9 minutes

Intégrer Hibernate avec GWT sans douleur

Avez-vous essayé d’utiliser directement vos objets du Domaine directement dans votre application GWT sans avoir une longue liste d’erreurs ? Avez-vous essayé de résoudre le problème en créant des objets DTO (Data Transfer Object) ou en adaptant vos objets du Domaine ? Existe-t-il une solution légère et transparente pour faire cohabiter Hibernate avec GWT ?

Dans cet article, nous présenterons une solution simple et propre pour vous permettre une meilleure intégration d’Hibernate avec GWT.

Ce billet se découpe de la façon suivante :

Introduction

L’idée de base est d’utiliser mes objets du Domaine directement dans la couche présentation. Google Web Toolkit se basant sur Java, cela semblait l’une des forces de cette union. Mais malheureusement, les divergences techniques rendent la tâche difficile.

Plusieurs raisons empêchent d’utiliser les objets du Domaine directement dans la couche présentation :

  • Héritage différent : Les objets Java échangés entre le client et le serveur doivent implémenter une interface de sérialisation Google IsSerializable, tandis que les classes Hibernate implémentent l’interface Serializable. Autre problème gênant, le compilateur GWT dans sa version 1.4 ne supporte pas l’interface Java Seriablizable et affiche une erreur.
  • Types de données incompatibles : Les classes PersistentSet et java.sql.DateTime utilisées par Hibernate ne sont pas reconnues par le compilateur GWT.
  • Chargement partiel (Lazy-Loading) : GWT ne sait manipuler que des POJO (Plain Old Java Object) et non des objets Hibernate. À l’exécution, Hibernate remplace les instances par des Proxies, alors que JavaScript, mis en œuvre par GWT, accepte uniquement les types primitifs et les POJO qui en sont composés. Or une classe Proxy n’est pas un type primitif, ni un type déclaré comme sérialisable en JavaScript.

Toutefois, intégrer Hibernate avec GWT est possible. Mais doit-on abandonner l’idée d’utiliser les objets du Domaine directement dans la couche présentation ? GWT 1.5 est peut-être la solution.

Faire cohabiter GWT et Hibernate

Une solution consiste à créer une hiérarchie de classes DTO correspondant à la hiérarchie des classes Hibernate, puis à cloner les objets du Domaine en objets DTO afin de les véhiculer vers la couche présentation sans difficulté. L’opération de clonage est prise en charge par la couche service qui assure les conversions Hibernate<->DTO en utilisant la librairie bean-lib. Celle-ci gère la duplication dynamique des objets Hibernate et convertit les données non supportées par GWT. Reste donc le problème de chargement partiel (Lazy loading) qui doit être géré par les développeurs.

Cette solution s’est avérée complexe et coûteuse en temps de développement. De plus, elle entraîne beaucoup trop de dépendances techniques. Elle a cependant le mérite de fonctionner.

Pour plus d’informations n’hésitez pas à consulter le billet suivant.

Hibernate4GWT arrive … GWT 1.5 aussi

Hibernate4GWT est une librairie Java prenant en charge la cohabitation dynamique d’Hibernate avec GWT. Elle permet ainsi d’utiliser les objets du Domaine directement dans la couche présentation sans se soucier de créer une hiérarchie de classes DTO juste vos objets du Domaine et du Domain Driven Architecture.

À noter que si vous utilisez toujours GWT 1.4, vos objets du Domaine doivent respecter la syntaxe Java 1.4, ce qui n’est pas facile à gérer et à intégrer dans du code existant. Et même avec Hibernate4GWT, vous êtes contraints de créer des classes DTO pour vous en sortir. Les objets du Domaine qui proviennent du serveur sont rapatriés et organisés dans les DTO par la librairie (l’opération de clone) et les objets DTO renvoyés vers le serveur sont également restitués par la librairie (l’opération de merge).

Avec l’arrivée de GWT1.5, vous n’avez plus ni à créer des classes DTO, ni à utiliser explicitement les opérations de merge et de clone. il est désormais possible d’utiliser vos objets du Domaine directement sur la couche présentation : d’où la nécessité de passer à GWT 1.5 pour une intégration transparente entre Hibernate et GWT.

La classe HibernateBeanManager, qui est le cœur de l’API, s’occupe de la suppression des proxies Hibernate. Elle gère la conversion des types de données incompatibles avec GWT en leur équivalent valide. Puis elle stocke les informations des attributs partiellement chargés (Lazy attributes) pour restituer correctement les objets du Domaine.

Hibernate4GWT, avec un minimum de configuration et des impacts techniques très légers, fournit un socle technique qui masque la complexité introduite par l’intégration d’Hibernate avec GWT et nous permet ainsi de respecter les règles de l’art.

Et concrètement qu’est-ce que ça donne ?!

Hibernate4GWT s’adapte à plusieurs types de configurations, selon vos besoins et les contraintes du projet dans lequel vous souhaitez l’intégrer.

Plusieurs configurations sont possibles :

  • Stateless (configuration par défaut) : le serveur ne stocke aucune information sur l’état de la session. Les classes du Domaine doivent hériter de la classe LazyPojo pour stocker les attributs partiellement chargés (Lazy attributes).
  • Dynamic Proxy : Une classe Proxy est générée par la librairie pour stocker les informations sur les attributs partiellement chargés (Lazy attributes) côté serveur et côté client. Les classes du Domaine doivent implémenter l’interface Serializable.
  • Stateful : Les informations sur les attributs partiellement chargés (Lazy attributes) ne sont pas stockées dans les POJO mais dans la session HTTP. Les classes du Domaine ne nécessitent aucun héritage technique, mais votre serveur devient stateful (sticky session).
  • Java5 support : GWT 1.4 ne supporte pas la syntaxe Java 5, Hibernate4GWT permet de cloner dynamiquement les classes du Domaine Java 5 en DTO associés compatibles Java 1.4. Pour cela, ces classes DTO doivent avoir le même nom que les classes du Domaine (placées dans un package différent) et doivent hériter de la classe LazyPojo.

Pour démarrer, vous devez d’abord choisir une configuration pour votre projet. Pour un nouveau projet, ou si vous avez la possibilité d’ajouter des héritages techniques dans vos classes du Domaine, l’auteur de la librairie recommande la configuration Dynamic proxy ou le mode Stateless. Sinon, si vous ne voulez pas modifier l’existant, vous pouvez utiliser le mode Stateful ou la configuration Dynamic proxy si vos classes du Domaine implémentent l’interface Serializable.

À noter que si vous utilisez des classes du Domaine Java 5 et GWT 1.4, vous êtes contraints d’utiliser la configuration Java5 support.

Intégrer Hibernate4GWT dans votre projet

Installation

L’installation d’Hibernate4GWT dans un projet GWT :

  • Décompression de l’archive hibernate4gwt-1.x.x.zip,
  • Ajout d’hibernate4gwt.jar et des dépendances présentes dans le répertoire lib dans le classpath de votre projet GWT,
  • Édition du fichier de configuration GWT :

Avec GWT 1.4 :


       
       
       
       

       
       

       
       
       
       
       

       

       
       
          
       

       

Avec GWT 1.5 :


       
       
       
       
       
       

       
       

       
       
       
       
       

       

       
       
         
       

       

Classes du Domaine

Pour utiliser vos classes du Domaine dans GWT, il y a quelques détails à régler :

  • Ajouter vos classes du Domaine dans le classpath de l’application GWT,
  • Implémenter l’interface Serializable dans vos classes du Domaine pour le mode Stateful et la configuration Dynamic proxy,
  • Étendre la classe net.sf.hibernate4gwt.pojo.java14.LazyPojo (GWT 1.4) ou net.sf.hibernate4gwt.pojo.java5.LazyPojo (GWT 1.5) pour le mode Stateless,
  • Créer une hiérarchie de classes DTO compatibles avec GWT 1.4 pour la configuration Java5 support.

Initialisation d’HibernateBeanManager

Lors l’initialisation de l’instance d’HibernateBeanManager, il faut lui associer une instance d’Hibernate SessionFactory. Cette configuration est à faire dans le code d’initialisation serveur.

Mode Stateless :

HibernateBeanManager.getInstance().setSessionFactory(sessionFactory);
// or HibernateBeanManager.getInstance().setEntityManagerFactory(emf);

Configuration Java5 support :

De plus, il faut également associer HibernateBeanManager à une instance de la classe DirectoryCloneMapper pour le mapping entre les classes du Domaine et les classes DTO.

HibernateBeanManager.getInstance().setSessionFactory(sessionFactory);
// or HibernateBeanManager.getInstance().setEntityManagerFactory(emf);
DirectoryCloneMapper cloneMapper = new DirectoryClassMapper();
cloneMapper.setRootDomainPackage("com.testing.server.domain");
cloneMapper.setRootClonePackage("com.testing.dto");
cloneMapper.setCloneSuffix("DTO");
HibernateBeanManager.getInstance().setClassMapper(cloneMapper);

Mode Stateful :

En plus de la configuration de base, il faut associer une instance d’HttpSessionPojoStore à l’instance d’HibernateBeanManager.

HibernateLazyManager.getInstance().setPojoStore(new HttpSessionPojoStore());
HibernateLazyManager.getInstance().setSessionFactory(sessionFactory);
// or HibernateLazyManager.getInstance().setEntityManagerFactory(emf);

Configuration Dynamic proxy :

Il est aussi nécessaire d’associer une instance de la classe ProxyClassMapper à l’instance d’HibernateBeanManager pour les opérations de clone et de merge.

HibernateBeanManager.getInstance().setSessionFactory(sessionFactory);
// or HibernateBeanManager.getInstance().setEntityManagerFactory(emf);
HibernateBeanManager.getInstance().setClassMapper(new ProxyClassMapper());

Service distant

L’implémentation du service GWT distant doit hériter de la classe HibernateRemoteService au lieu de la classe RemoteServiceServlet.

public class ProductRemoteImpl extends HibernateRemoteService
                            implements ProductRemote
{
    ...
    public Product getProduct(int id)
    {
         return _productDAO.getProductById(id);
    }

    public Product updateUser(Product product)
    {
         // Update the product in database
         _productDAO.updateProduct(product);

         // Send back a new clone (optimistic lock)
         return product;
    }
}

A noter qu’avec la configuration Java5 support, il faut merger les objets DTO en entrée et cloner les objets du Domaine en sortie.

public class ProductRemoteImpl extends HibernateRemoteService
                            implements ProductRemote
{
    ...
    public ProductDTO getProduct(int id)
    {
         return (ProductDTO) clone(_productDAO.getProductById(id));
    }

    public ProductDTO updateUser(ProductDTO productDTO)
    {
         // Update the product in database
         product = (Product)merge(productDTO);
         _productDAO.updateProduct(product);

         // Send back a new clone (optimistic lock)
         return (ProductDTO) clone(product);
    }
}

Conclusion

J’avais intégré l’API Hibernate4GWT sur un projet de petite taille avec une base de données de 10 tables et un nombre d’utilisateurs simultanés limité à 20. Lors de la mise en œuvre, l’API m’a facilité grandement la vie, et m’a permis une meilleure intégration d’Hibernate avec GWT. Je n’ai pas eu des problèmes particuliers en production ni d’impacts sur les performances de l’application, mais des questions se posent : peut-on utiliser l’API sur des grands projets ? Quel sera l’impact sur les performances lors de la montée en charge ?

Comme nous avons pu le voir, Hibernate4GWT est une petite librairie qui permet une intégration transparente et légère d’Hibernate avec GWT. L’auteur de la librairie nous promet beaucoup de choses dans l’avenir en renommant la librairie en Gilead ou Generic Light Entity Adapter, une nouvelle version Hibernate4ALL pour les technologies (Flex/BlazeDS, Web Services …).

Liens annexes :

Publié par

Commentaire

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Nous recrutons

Être un Xebian, c'est faire partie d'un groupe de passionnés ; C'est l'opportunité de travailler et de partager avec des pairs parmi les plus talentueux.