Affichage des articles dont le libellé est hibernate. Afficher tous les articles
Affichage des articles dont le libellé est hibernate. Afficher tous les articles

mardi 4 décembre 2007

(partie 2) Intégration de Maven dans un projet Java J2EE Hibernate/Spring/Eclipse RCP: pas une mince affaire mais une bonne affaire tout de même !

Intégration de Maven dans un projet Java J2EE Hibernate, Spring, Eclipse RCP: pas une mince affaire mais une bonne affaire tout de même ! (partie 2)

Voici la suite de l'article concernant l'utilisation de Maven avec WTP d'Eclipse.

Le but étant de trouver une solution pour utiliser à la fois Maven et ainsi bénéficier de l'avantage de la gestion de la configuration des dépendances tout en restant WTP "compliant" et ainsi être capable de debugger le serveur dans l'environnement Eclipse.

En fait c'est assez simple, vous prenez votre projet "assembly" maven, celui qui vous permet via maven de générer votre archive web, votre war par exemple et vous exécutez la commande suivante dans son répertoire :
mvn -Dwtpversion=1.0 eclipse:eclipse
Bien entendu vous remplacez "1.0" par R7, 1.5 ou 2.0 en fonction de votre version WTP.

Après ça, un répertoire .settings va être créé à la racine du projet avec un fichier ".component" dans notre cas (version 1.0). Dans ce fichier on va retrouver toutes les dépendances du projet que ce soit les librairies ou les projets dont il dépend. Toutes les dépendances apparaissent sous forme de jars situés dans le dépôt maven.

Problème: en mettant les projets dépendants via leur archive jar sous maven, il n'est pas possible de les modifier en debug sous eclipse. Il faut supprimer les références vers les projets dépendants dans le fichier ".component" pour ne laisser que les librairies utilisées. Ensuite il faut faire un clic droit sur le projet et sélectionner "properties" puis dans la fenêtre qui apparaît il faut cliquer sur "modules dependencies". La prochaine fenêtre permet de sélectionner les projets dépendants, il faut alors cocher tous les projets que vous avez supprimé du fichier .component à l'étape précédente et terminer en validant en cliquant sur OK.

Après quoi il faut vérifier que le fichier .component contient bien les nouvelles dépendances vers les projets fraîchement supprimés puis ajoutés, mais cette fois avec des dépendances de type module ! (et non jar)

Après quoi, il faut ajouter un serveur à WTP si ce n'est pas déjà fait puis ajouter le projet ainsi modifié au serveur WTP... puis le lancer, mettre des points d'arrêts et s'y rendre, modifier le code source "en live", sauvegarder la classe modifiée et prendre soin de builder via eclipse (Ctrl + B ou build auto) et là: contempler le debugger d'Eclipse continuer de debugger normalement.

En fait il n'y a rien de magique dans tout ça, c'est simplement un projet assembly Maven auquel on ajoute la fonctionnalité WTP permettant de debugger... Cette solution à un inconvénient MAJEUR !!! La maintenabilité !!! A chaque dépendance maven ajoutée qu'elle soit de type librairie ou projet... il faudra l'ajouter manuellement (via clic droit properties modules dependencies...)

Je compte me pencher sur ce point ASAP... des pistes ? utiliser WTP 2, tester le nouveau plugin eclipse pour maven, utiliser un script de synchronisation, réécrire WTP... etc...

mardi 21 août 2007

Problème de suppression d'objets dans les collections avec Hibernate !

Hibernate est un framework de gestion de la persistance renommé et très connu qui permet bien des choses et avec lequel on peut s'arracher bien des cheveux !

Hibernate à la "facheuse" tendance à utiliser des types collection qui lui sont specifique pour gérer tout ce qui concerne les list, set... Les attributs de type list et set sont remplacés par des objets de type PersistentList et PersistentSet. Cela permet par exemple à hibernate de charger dynamiquement la collection lors de l'appelle à une méthode comme size() add() ou remove() dans le cas par exemple d'une collection mappée avec le lazy loading. Pour résumer, tant qu'on n'a pas besoin du contenu de la liste, elle est remplacé par un proxy qui est pret à al charger à la première demande.

Quand on utilise Hibernate dans le cadre d'une application ayant une architecture client-serveur, les classes d'hibernate ne sont accéssibles que coté serveur, coté base de données. Or dans une architecture client-serveur on fait transiter des objets métiers java de part et d'autre via le réseau (en HTTP par exemple avec Spring remoting) mais sous une condition : Que tous les objets envoyés soit accessibles dans les dépendances des 2 projets.

Or comme je l'ai expliqué juste avant, Hibernate remplace les listes par des objets qui lui sont propres et qui ne sont accéssibles qu'au projet serveur via la librairie hibernate (hibernate3.jar par exemple dans répertoire lib du serveur). Donc si on envoie au client une grappe métier contenant des listes après extraction de la base via hibernate, cela va poser problème et on va voir fleurir dans la console des erreurs du type "Exception : Ahhhhhh je ne connais pas la classe PersistentSet"

Il se peut qu'on ne soit pas impacté par ce problème car il est coutume d'utiliser des DTOs et des transformateurs pour faire transiter les objets sur le réseau et par conséquent les types spécifiques d'hibernate sont transformés automatiquement (par les transformateurs). Dans le cas ou nous n'avons pas de couche de DTOs, on peut utiliser des mappeurs qui remplacent les PersistentList et PersistentSet en ArrayList et HashpSet par exemple.

Bref, une fois les objets récupérés transformés (sans PersistentXXX) sur le client, on joue avec, ou les modifie et on peut être amené à supprimer des éléments d'une liste. Et c'est la que l'affaire se corse car il se peut, sans qu'on sache réellement pourquoi, que Hibernate ne puisse pas détecter la suppression d'un élément donné lors de la sauvegarde.

Effectivement, puisqu'on a remplacer les gestionnaire de liste à la sauce hibernate quand on renvoie la grappe coté serveur... il ne se rend pas compte qu'il y a eu suppression même avec les options type delete-orphan all-delete-orphan dans le mapping... La seule solution, l'ultime chance restante consiste à mapper la liste d'une manière bien différente.

Ce problème commun est connu sur le site hibernate.org :

------------------------------
I removed an object from a collection mapped with cascade="all" but the object was not deleted!

cascade="all" cascades the delete() operation from parent to child. If this is a one-to-many association, try using cascade="all,delete-orphan".

Another solution is to model your child objects as composite elements (a kind of value type) rather than entities. Value types are always persisted or removed along with their parent entity. So you would use a mapping for the element class instead of a mapping..
------------------------------

Voici le mapping de la liste contenu dans mon objet Truc :

<set name="maListe" table="element" lazy="true" >
<key column="elt_trc_id" not-null="true" />
<composite-element class="org.truc.Element">
<parent name="truc"/>

<property name="identifiant" type="java.lang.Integer">
<column name="elt_id" />
</property>

<property name="commentaire" type="java.lang.String">
<column name="elt_commentaire" length="254" />
</property>

<property name="date" type="timestamp">
<column name="elt_date" />
</property>

</composite-element>
</set>

au lieu de par exemple:

<list name="maListe" lazy="true" cascade="persist, save-update, evict, merge, all-delete-orphan">
<key column="elt_trc_id" not-null="true" />
<index column="elt_numero" />
<one-to-many class="Element" />
</list>

Bon les désavantages sont flagrants, on est obligé de mapper la liste dans le fichier de mapping de l'objet conteneur et dans le cas ou les objets de la liste sont utilisés dans plusieurs objets... on duplique on duplique... pas très pro comme on dit alors mieux vaut éviter.

Mais bon ca fonctionne et les objets sont biens mis à jours et surtout supprimés quand on sauvegarde l'objet conteneur...