IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Wicket Java Discussion :

Intégration entre Wicket et Hibernate : une solution pour travailler en mode attaché [Trucs & Astuces]


Sujet :

Wicket Java

  1. #1
    Membre expérimenté

    Inscrit en
    Décembre 2004
    Messages
    584
    Détails du profil
    Informations forums :
    Inscription : Décembre 2004
    Messages : 584
    Points : 1 374
    Points
    1 374
    Par défaut Intégration entre Wicket et Hibernate : une solution pour travailler en mode attaché
    Bonjour

    L'idée de ce post est de présenter une des solutions que nous mettons en oeuvre, à mon boulot, afin de résoudre le problème des entités hibernate détachées.

    L'idée en est toute simple : lorsque nous travaillons avec des entités, nous voulons qu'elles soient attachées afin d'éviter les soucis.

    Pour réaliser ceci, la mise en oeuvre est toute simple : nous avons créé un IModel spécifique, nommé EntityModel<E extends BaseEntity>. Ce dernier s'occupe de s'assurer que l'entité qu'il contient est bien attachée lors de son premier accès au cours de la RequestCycle courant (ou lorsque que créé ou affecté une entité via set(E e)). Lors du détachement, la seule donnée persistée est l'identifiant de l'entité contenue ainsi que sa classe. D'où d'ailleurs l'interface BaseEntity, qui définit (notamment) l'identifiant de chaque entité et une méthode public boolean isPersistent().

    En fait, nous poussons l'EntityModel un peu plus loin : utilisant une approche "Open Session In View", nous nous assurons en fait que l'entité est attachée à l'Entity Manager affecté à la session courante.

    Au delà, et pour finir sur ce topo, l'EntityModel est à même également de gérer des entités non attachées. Ansi, lorsqu'une entité non persistantes lui est communiqué, il la stocke dans un champ ad hoc qui sera ensuite sérialisé entre les RequestCycle.

    A noter que ce modèle n'implique pas que nous travaillons toujours en mode attaché. Il est parfois nécessaire de fonctionner via des value object, lors de wizards ou dialogues par exemple. Simplement, lorsqu'il est possible de travailler avec des entités attachées, l'EntityModel nous évite bien des problèmes et questionnements!

    J'espère que cela peut apporter des éléments de réponse, à votre dispo si besoin

    ++
    joseph

    nb : je travaille en anglais, du coup parler de tout cela en français ne m'arrive pas trop souvent, veuillez m'excuser d'éventuels anglicismes...
    Merci d'utiliser le bouton [Résolu] pour les sujets qui le sont.
    [pub]mon blog franco anglais, article du moment: Wicket: fournir des données JSON via Ajax[/pub]

  2. #2
    Membre du Club
    Profil pro
    Inscrit en
    Décembre 2005
    Messages
    60
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2005
    Messages : 60
    Points : 67
    Points
    67
    Par défaut
    Bonjour, merci pour ton explication, j'aurais besoin de quelques précisions supplémentaires (j'utilise JPA et pas hibernate directement, j'espere que les problèmes que je rencontre ne sont pas liés à JPA) :

    - Que fait le code de la méthode isPersistent() dans les entités? En d'autre termes comment sais tu si une entité est attachée ou non à l'entity manager ou à la session hibernate?

    -Si tu as un objet partiellement modifié dans la couche UI, et que tu veux le rattacher après une action utilisateur, pour afficher une liste en lazy loading par exemple, comment gère tu le fait qu'en rattachant l'entité tu ne veux ni perdre les modifications qui ont été faites sur l'objet, ni les envoyer tout de suite dans la base (l'utilisateur n'a pas encore cliqué sur save) ?

    En JPA le si je fais un merge, j'envoie je rattache l'objet mais j'envoie également les modifications dans la base, si je recharge depuis l'id, par contre là je perd les modifications qui ont commencé à être rentrées par l'utilisateur. C'est peut être dans ces cas là qu'il faut passer par un value object?

    Merci beaucoup pour tes éclaircissements!

    Loic

  3. #3
    Membre expérimenté

    Inscrit en
    Décembre 2004
    Messages
    584
    Détails du profil
    Informations forums :
    Inscription : Décembre 2004
    Messages : 584
    Points : 1 374
    Points
    1 374
    Par défaut
    Citation Envoyé par loic38_01 Voir le message
    j'utilise JPA et pas hibernate directement, j'espere que les problèmes que je rencontre ne sont pas liés à JPA
    je ne pense pas, cela devrait être jouable en JPA sans souci (de notre côté que nous tentons de coller à JPA autant que ce faire se peut).

    Citation Envoyé par loic38_01 Voir le message
    - Que fait le code de la méthode isPersistent() dans les entités? En d'autre termes comment sais tu si une entité est attachée ou non à l'entity manager ou à la session hibernate?
    Déterminer si une entité est persistante est différent du fait qu'elle soit attachée, ou non, à un Entity Manager (EM).

    En effet, persistante veut dire qu'elle a déjà été sauvée et donc que son identifiant est présent en base de données. Dans notre cas, par convention, cela se traduit par le fait que l'entité en question à un identifiant supérieur à 0.

    Pour qu'une entité soit attachée à l'EM, il faut que ce dernier la contienne, cad :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
            EntityManagerProvider emp = getEntityManagerProvider();
            return emp.get().contains(e);
    Cela n'implique nullement qu'elle soit déjà persistante. Par contre elle le deviendra si persist lui est appliquée (avec une transaction ouverte puis commitée, bien sûr).

    Citation Envoyé par loic38_01 Voir le message
    -Si tu as un objet partiellement modifié dans la couche UI, et que tu veux le rattacher après une action utilisateur, pour afficher une liste en lazy loading par exemple, comment gère tu le fait qu'en rattachant l'entité tu ne veux ni perdre les modifications qui ont été faites sur l'objet, ni les envoyer tout de suite dans la base (l'utilisateur n'a pas encore cliqué sur save) ?
    hum, l'EntityModel n'est pas une solution miracle hein, juste un outil pratique

    Plus concrètement, cela veut dire, au choix :
    Dans le cas d'une entité persistante accédée via EntityModel :
    - si l'entité subit des modifications hors transactions (le cas par défaut), alors les changements sont perdus à la fin de la Request Cycle
    - si l'entité participe à une transaction, alors ses modifications sont prises en compte lors du commit

    En termes d'utilisation, cela veut dire plusieurs usages.
    En lecture seule : aucun souci d'attachement (c'est toujours vrai), pas de Lazy Loading Exception, des entités toujours à jour. Que du bonheur

    En édition, je vois 3 situations bien distinctes.

    1. Changement de l'entité en dehors d'un formulaire : il suffit alors (dans un Link par exemple), d'appeler ou persist/merge pendant une transaction. C'est tout simple. Le changement est alors persisté/mergé et pris en compte dans la suite.

    Dans le cas d'un formulaire, il y a grosso modo 2 situations, soit le formulaire ne met à jour son modèle qu'en une fois, de façon atomique, soit pas.

    2. Un formulaire qui met à jours "atomiquement" son modèle le fait via la méthode updateModel, appelée sur tous les composants répondant à IFormModelUpdateListener et membres du formualire. Cet appel est préalable à l'appel du onSubmit du formulaire. Ainsi, dans le onSubmit, l'EntityModel contient les modification et on peut appeler persist/merge. Le tour est joué !

    Pour procéder de la sorte, les composants que l'on développe soit même doivent adhérer à l'interface IFormModelUpdateListener, afin de fonctionner eux aussi de façon atomique.

    Il faut aussi faire attention à ce qui pourrait réaliser des mises à jour partielles prématurée du modèle derrière le formulaire, comme l'AjaxFormComponentUpdatingBehavior. De même, toutes les validations doivent avoir lieu avant le onSubmit, il faut donc écrire les (form)validators qui vont bien et ne pas lever d'erreur dans le onSubmit (ce qui est une bonne pratique de toute façon).

    3. Il arrive cependant que, parfois, cela ne soit pas possible, notamment du fait de wizards, dialogues ou formulaires plus modulaires avec possibilité d'annulation partielle. Dès lors, pas de secret, il faut procéder via des Value Object.

    Pour cela, nous basons toujours nos FormComponent perso sur le IFormModelUpdateListener, qui oblige initialement à copier l'objet qui lui est donné (à la manière d'un TextField au final, qui prend une image au début de l'édition puis ne contribue son résultat au modèle global lors du updateModel).

    Dès lors, ces composants sont aisément réutilisables dans des formulaires travaillent soit en approche Value Object soit en approche "persistence onSubmit". On peut manipuler ses objets détachés selon nos désirs, les rattachements le cas échéant. Que du bonheur donc Bien sûr, cela implique une gestion des versions et un risque de ConcurrentModificationException. There's no free lunch

    Ultime bafouille pour éviter tout malentendu : en règle général, il faut éviter de passer des modèles entre différentes pages (ou des composants in fine).

    La raison est liée à la szrialization, qui se base sur Page et tout ce qui est attaché. Autrement dit, les membres d'une Page sont garantis de rester cohérent au cours des serialization/deserialization.

    Ce n'est pas vrai pour des références partagées par deux pages.

    Du coup, le même objet passé à 2 pages peut se retrouver avec deux références différentes, vu que désérialisé à des moments différents. Cela a des conséquences très néfastes : les changements réalisés sur une référence ne le seront pas sur l'autre référence. On "perd" alors des modifications de façon surprenante, sans aucune alerte ou raison apparente.

    l'EntityModel présenté ici permet de s'affranchir de cette problématique. En effet, nous vérifions à chaque getObject() que l'object est attaché à l'EM courant, le rechargeant le cas échéant. On a donc toujours une version à jour. De même, les changements qui lui sont apportés doivent être répercutés en base de données, et donc, dès lors, visibles via tous les EntityModel référençant la même entité (dans le sens d'avoir un même identifiant), et cela que les EntityModel partagaient ou non une référence commune initialement.

    cela illustre bien la problématique des données passées entre pages : il faut qu'elles s'appuient sur un moyen de stockage indépendant de la page, la couche de persistance dans notre cas.

    A noter, pour finir, que cette problématique liée à la sérialization dépend aussi de la stratégie retenue pour la PageMap, qui stocke les anciennes pages.

    En effet, par défaut, la PageMap garde en mémoire l'instance de page courante et la précédente. Cela implique que, par défaut, les problèmes n'apparaissent que lorsque 3 instances de pages sont impliquées, ce qui n'arrive pas tout le temps.

    désolé pour le pavé, j'ai essayé d'être clair... ne pas hésiter à me poser des questions si besoin !

    ++
    Merci d'utiliser le bouton [Résolu] pour les sujets qui le sont.
    [pub]mon blog franco anglais, article du moment: Wicket: fournir des données JSON via Ajax[/pub]

  4. #4
    Membre du Club
    Profil pro
    Inscrit en
    Décembre 2005
    Messages
    60
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2005
    Messages : 60
    Points : 67
    Points
    67
    Par défaut
    Bonjour, merci beaucoup pour toutes ces précisions et pour le temps passé à me répondre.

    Citation Envoyé par joseph_p Voir le message
    Déterminer si une entité est persistante est différent du fait qu'elle soit attachée, ou non, à un Entity Manager (EM).
    En effet je n'ai pas utilsé le bon terme, en fait je voulais savoir comment tu déterminais si l'entitée persistente, était attachée à l'EL ou non pour savoir si un merge (ou un find?) est nécessaire.

    Citation Envoyé par joseph_p Voir le message
    l'EntityModel présenté ici permet de s'affranchir de cette problématique. En effet, nous vérifions à chaque getObject() que l'object est attaché à l'EM courant, le rechargeant le cas échéant. On a donc toujours une version à jour.
    Comment procède tu pour faire ça ?Pour ma part pour être sur d'avoir un objet attaché, j'effectue un simple em.find(MyEntity.class, myEntity.getId());
    Mais c'est un peu bourrin comme méthode (jévite le merge pour ne pas envoyer de données vers la base avant le submit)....

    Citation Envoyé par joseph_p Voir le message
    Pour cela, nous basons toujours nos FormComponent perso sur le IFormModelUpdateListener
    Merci pour cette astuce, je n'utilise pas ce type de Listener, je vais y jeter un oeil.

    Citation Envoyé par joseph_p Voir le message
    De même, toutes les validations doivent avoir lieu avant le onSubmit.
    Je ne suis pas sur d'avoir bien compris mais ceci n'est possible qu'en mode ajax non?
    Si par exemple une valeur est entrée dans un TextField, en mode non ajax il me semble que je ne peux vérifier sa valeur que quand l'utilisateur aura cliqué sur submit. A moins que ce soit différent avec l'utilisation de IFormModelUpdateListener?


    Loic

  5. #5
    Membre expérimenté

    Inscrit en
    Décembre 2004
    Messages
    584
    Détails du profil
    Informations forums :
    Inscription : Décembre 2004
    Messages : 584
    Points : 1 374
    Points
    1 374
    Par défaut
    Citation Envoyé par loic38_01 Voir le message
    Bonjour, merci beaucoup pour toutes ces précisions et pour le temps passé à me répondre.

    Citation Envoyé par loic38_01 Voir le message
    En effet je n'ai pas utilsé le bon terme, en fait je voulais savoir comment tu déterminais si l'entitée persistente, était attachée à l'EL ou non pour savoir si un merge (ou un find?) est nécessaire.
    pour savoir si l'entité est attachée, on passe par :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    EntityManagerProvider emp = getEntityManagerProvider();
    return emp.get().contains(e);
    pour savoir si un merge est nécessaire, on fait pas

    Je m'explique. Comme dit plus haut, on a une interface "BaseEntity" qui comporte une méthode isPersistent(). Du coup, on a fait une méthode save() générique qui appelle isPersistent() puis persist/merge en fonction.

    Mais tout cela ne marche que "par déclenchement", on ne fait pas de détection automatique de "dirtyness".

    Par conséquent, lors du réattachement qui a lieu dans l'EntityModel, il se peut que des données non sauvées soient perdues. Charge au programmeur de faire ça correctement.

    Citation Envoyé par loic38_01 Voir le message
    Comment procède tu pour faire ça ?Pour ma part pour être sur d'avoir un objet attaché, j'effectue un simple em.find(MyEntity.class, myEntity.getId());
    Mais c'est un peu bourrin comme méthode (jévite le merge pour ne pas envoyer de données vers la base avant le submit)....
    on a encapsulé un peu le find histoire de ne pas avoir à donner le type de classe, mais ça revient à ça. Au final, cela ne devrait pas couter bien cher normalement : le cache de second niveau a en général déjà l'entité en question.

    Quand au merge, il est à mon sens spécifique aux opérations de modifications des données. L'appeler à tout bout de champ par craindre de perdre des données me parait très dangereux (et couteux pour la peine).

    Citation Envoyé par loic38_01 Voir le message

    (..)
    Je ne suis pas sur d'avoir bien compris mais ceci n'est possible qu'en mode ajax non?
    Si par exemple une valeur est entrée dans un TextField, en mode non ajax il me semble que je ne peux vérifier sa valeur que quand l'utilisateur aura cliqué sur submit. A moins que ce soit différent avec l'utilisation de IFormModelUpdateListener?
    hum, oui et non

    Le cycle est grosso modo le suivant :
    - submit par l'utilisateur
    - conversion des données saisies
    - pour chaque composant, application des validators correspondant
    - si tous les composants sont valides, appels des FormValidators
    - si tout est ok, appel du onValidate
    - si tout est ok, mis à jour des modèles (via updateModel comme dit plus haut)
    - appel du onSubmit
    - en cas de pépin, appel du onError

    Concernant la validation, elle ne se fait pas via l'objet contenu dans le modèle mais via l'interface IValidatable, qui permet de récupérer la valeur saisie convertie. On a donc bien se découplage entre soumission du formulaire et mises à jour des modèles.

    Pour plus de détail, voir du côté de l'AbstractValidator et de l'AbstractFormValidator, de même que la Javadoc de la classe Form.

    Concernant l'ajax ou le javascript, il permet juste de faire des soumissions du formulaire sur d'autres actions que le clic d'un bouton (via un SubmitLink par exemple). L'ajax permette en plus de limiter la bande passante. Les principes restent toutefois les mêmes

    Pour l'ajax, comme dit plus haut, il faut "juste" se méfier de l'AjaxFormComponentUpdatingBehavior, qui va directement tenter de valider et mettre à jour le modèle derrière le composant concerné. Cela casse l'unicité de la mise à jour du modèle du formulaire, ce qui peut devenir problématique.

    ne pas hésiter si tu as besoin de plus détails

    ++
    joseph

    ps : je suis en congé la semaine prochaine, ne pas s'inquiéter si je ne réponds pas
    Merci d'utiliser le bouton [Résolu] pour les sujets qui le sont.
    [pub]mon blog franco anglais, article du moment: Wicket: fournir des données JSON via Ajax[/pub]

  6. #6
    Membre du Club
    Profil pro
    Inscrit en
    Décembre 2005
    Messages
    60
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2005
    Messages : 60
    Points : 67
    Points
    67
    Par défaut
    Je crois que tout est clair pour moi maintenant
    Merci beaucoup d'avoir partagé toutes ces astuces!

Discussions similaires

  1. Réponses: 1
    Dernier message: 21/05/2007, 16h14
  2. Liaison entre plusieurs projet d'une solution
    Par jeremycs dans le forum Windows Forms
    Réponses: 1
    Dernier message: 16/02/2007, 13h38
  3. Une solution pour faire des enums dynamique ?
    Par n!co dans le forum Langage
    Réponses: 7
    Dernier message: 16/12/2006, 14h44
  4. Une solution pour migrer de .NET vers MFC
    Par torNAdE dans le forum MFC
    Réponses: 1
    Dernier message: 03/05/2006, 22h23

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo