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

Hibernate Java Discussion :

[lazy] Annotation et NullPointer


Sujet :

Hibernate Java

  1. #21
    Membre à l'essai
    Profil pro
    Inscrit en
    Septembre 2006
    Messages
    36
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2006
    Messages : 36
    Points : 13
    Points
    13
    Par défaut
    manblaizo, j'ai testé ton exemple:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    public boolean isUtilisateurCourantGestionnaire() {
        getUtilisateurCourant().getProfil().getLibelle();
        Long uid = getUtilisateurCourant().getProfil().getUid();
        if (uid.equals(ConstantesDroits.PROFIL_GESTIONNAIRE)) {
            return true;
        }
        return false;
    }
    Ca ne change rien, j'ai toujours mon erreur...

  2. #22
    Membre confirmé
    Profil pro
    Inscrit en
    Janvier 2006
    Messages
    365
    Détails du profil
    Informations personnelles :
    Localisation : Maroc

    Informations forums :
    Inscription : Janvier 2006
    Messages : 365
    Points : 495
    Points
    495
    Par défaut
    Bizarre ! Et quelle est exactement la ligne de code incriminée ? Tu as vérifié si uid n'est pas NULL dans la base données pour l'Utiilisateur concerné ? Essaie de faire des tests pour voir ce qui reste à null pour provoquer cette exception. Mais pour ce qui est du lazy loading en tout cas, le proxy est normalement initialisé quand on accède à un champ non id. Je le fais souvent et ça ne me pose pas de problème.
    SCJP 5 / SCBCD 1.3 Certified

  3. #23
    Membre à l'essai
    Profil pro
    Inscrit en
    Septembre 2006
    Messages
    36
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2006
    Messages : 36
    Points : 13
    Points
    13
    Par défaut
    L'erreur apparait ligne 4 (la ligne du if, uid vaut null). L'UID n'est pas null et ne peut pas etre null en base (contrainte NOT NULL). J'ai testé tout ca, et c'est vraiment les propriete du proxy qui sont null (TOUTES les proprietes).

    Mais tu pointes exactement mon probleme: le proxy n'est pas initialisé, et je n'ai pas de LazyInitializeException, juste les propriete du proxy qui valent toute null.

  4. #24
    Modérateur
    Avatar de OButterlin
    Homme Profil pro
    Inscrit en
    Novembre 2006
    Messages
    7 313
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Novembre 2006
    Messages : 7 313
    Points : 9 529
    Points
    9 529
    Billets dans le blog
    1
    Par défaut
    Peux-tu faire un petit diagramme de séquence depuis la page ?
    (on verra où tu alloues la session par exemple, etc...)

    A+
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  5. #25
    Membre confirmé
    Profil pro
    Inscrit en
    Janvier 2006
    Messages
    365
    Détails du profil
    Informations personnelles :
    Localisation : Maroc

    Informations forums :
    Inscription : Janvier 2006
    Messages : 365
    Points : 495
    Points
    495
    Par défaut
    Citation Envoyé par gcube Voir le message
    L'erreur apparait ligne 4 (la ligne du if, uid vaut null). L'UID n'est pas null et ne peut pas etre null en base (contrainte NOT NULL). J'ai testé tout ca, et c'est vraiment les propriete du proxy qui sont null (TOUTES les proprietes).

    Mais tu pointes exactement mon probleme: le proxy n'est pas initialisé, et je n'ai pas de LazyInitializeException, juste les propriete du proxy qui valent toute null.
    C'est vraiment curieux que le proxy ne soit toujours pas initialisé. Est-ce que le problème ne viendrait pas du fait que l'objet "utilisateur" lui-même n'est pas initialisé ? Parce qu'il faut savoir que si dans ton DAO tu fais un session.load() avec l'id de l'utilisateur, hibernate ne fera pas d'accès base de données tant qu'un champ autre que id ne sera pas demandé pour l'objet chargé ! donc même à ce niveau déjà, hibernate crée un proxy pour ton objet utilisateur, proxy dont il va falloir forcer l'initialisation également (utilisateur.getNom() par exemple), avant de forcer ensuite l'initialisation des objets associés.
    En tout cas, vérifie si les autres champs de "utilisateur" ne sont pas null aussi.
    SCJP 5 / SCBCD 1.3 Certified

  6. #26
    Membre à l'essai
    Profil pro
    Inscrit en
    Septembre 2006
    Messages
    36
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2006
    Messages : 36
    Points : 13
    Points
    13
    Par défaut
    Oui, les autres champs de mon utilisateur sont bien remplis (ils sont tous en FetchType.EAGER).
    Ensuite, j'ai bien verifié, je fais un session.get(), pas un session.load(). De plus, je ne fais pas un get par id, mais par son identifiant, qui n'est pas @Id. De plus, l'objet utilisateur ne semble pas etre un proxy (et tout cas, le debugger eclipse ne me le montre pas comme proxy, alors que je vois bien que profil est proxy).

    Je precise quand meme que j'utilise la classe spring HibernateDaoSupport (qui est en gros un wrapper autour de la session hibernate).


    Pour le digramme de sequence, je vais essayer, mais je ne m'occupe pas de la session!! C'est hibernateDaoSupport qui s'en occupe pour moi. A priori, cette classe tente de recuperer une session stocké en ThreadLocal. Si il n'en trouve pas, il en créé une.

  7. #27
    Membre confirmé
    Profil pro
    Inscrit en
    Janvier 2006
    Messages
    365
    Détails du profil
    Informations personnelles :
    Localisation : Maroc

    Informations forums :
    Inscription : Janvier 2006
    Messages : 365
    Points : 495
    Points
    495
    Par défaut
    Citation Envoyé par gcube Voir le message
    Oui, les autres champs de mon utilisateur sont bien remplis (ils sont tous en FetchType.EAGER).
    Ensuite, j'ai bien verifié, je fais un session.get(), pas un session.load(). De plus, je ne fais pas un get par id, mais par son identifiant, qui n'est pas @Id. De plus, l'objet utilisateur ne semble pas etre un proxy (et tout cas, le debugger eclipse ne me le montre pas comme proxy, alors que je vois bien que profil est proxy).

    Je precise quand meme que j'utilise la classe spring HibernateDaoSupport (qui est en gros un wrapper autour de la session hibernate).
    Je ne comprend pas bien cette phrase que j'ai mise en gras. Que veut dire "Identifiant" dans cette phrase ? Est-ce que tu fais une requête hql pour charger l'objet ? Tu pourrais nous montrer alors ce code du DAO ?
    SCJP 5 / SCBCD 1.3 Certified

  8. #28
    Membre à l'essai
    Profil pro
    Inscrit en
    Septembre 2006
    Messages
    36
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2006
    Messages : 36
    Points : 13
    Points
    13
    Par défaut
    J'ai plusieurs propriete sur mon objet:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
        @Id
        @Column(name = "uid", unique = true, nullable = false, updatable = true, insertable = true)
        @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "uid_generator")
        private Long uid;
    et
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
        @Basic(fetch = FetchType.EAGER)
        @Column(name = "uti_t_identifiant", length = Constants.HIBERNATE3_STRING_SIZE, nullable = false, unique = true)
        private String identifiant;
    l'uid est herité depuis une superclasse, il y a donc un attributeoverride:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    @AttributeOverride(name = "uid", column = @Column(name = "uti_np_id"))
    Voici le code du dao que j'appel:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
        @SuppressWarnings("unchecked")
        public final IUtilisateur findUtilisateurByIdentifiant(final String _identifiant)
                throws DaoException {
            try {
                final List<IUtilisateur> users = this.getHibernateTemplate().find(
                        "from Utilisateur where identifiant=?", _identifiant);
                if (users.isEmpty()) {
                    return null;
                }
                return users.get(0);
            } catch (final Exception excp) {
                this.error("an error occurs while finding user by identifiant", excp);
                throw new DaoException("an error occurs while finding user by identifiant", excp);
            }
        }
    getHibernateTemplate() retourne un http://static.springframework.org/sp...eTemplate.html

  9. #29
    Membre confirmé
    Profil pro
    Inscrit en
    Janvier 2006
    Messages
    365
    Détails du profil
    Informations personnelles :
    Localisation : Maroc

    Informations forums :
    Inscription : Janvier 2006
    Messages : 365
    Points : 495
    Points
    495
    Par défaut
    Eh bien, là les choses sont plus claires, tu fais donc une requête hql pour charger ton objet "utilisateur". Mais pour une requête hql, les entités associées ne sont pas initialisées par défaut, il te faut préciser cela dans la requête.
    Quelque chose comme ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    "from Utilisateur u join fetch u.profil where u.identifiant=?"
    C'est bien le but de mettre du mode FetchType.LAZY, c'est qu'en fonctionnement normal (avec session.load ou session.get()) l'objet est chargé sans ses associations et collections dont on n'a pas besoin. Mais en fonction du cas d'utilisation, on utilise une requête hql pour monter également ses dépendances lorsqu'on en a besoin.
    J'espère que ceci va résoudre ton problème.
    SCJP 5 / SCBCD 1.3 Certified

  10. #30
    Membre à l'essai
    Profil pro
    Inscrit en
    Septembre 2006
    Messages
    36
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2006
    Messages : 36
    Points : 13
    Points
    13
    Par défaut
    Je n'ai jamais utilisé de fetch join sur mes autres projets, et j'avais un comportement correct...

    Pour moi le fonctionnement du lazy est: L'objet est chargé sans ses associations, et c'est sur un getXXX que l'association va etre chargée (a condition que l'association soit "liée" a une session hibernate, si il n'y a pas de session, une LazyException est lancée). Le chargement tardif est transparent, je ne dois pas m'en occuper (a part reattacher l'objet a une session si besoin).

    Ou alors je n'ai rien compris au lazy loading hibernate...

  11. #31
    Modérateur
    Avatar de OButterlin
    Homme Profil pro
    Inscrit en
    Novembre 2006
    Messages
    7 313
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Novembre 2006
    Messages : 7 313
    Points : 9 529
    Points
    9 529
    Billets dans le blog
    1
    Par défaut
    Tu as très bien compris

    Je pense que ton problème est lié à la session justement, au moment où tu exploite les infos liées, la session ne doit plus être active.
    Tu disais que tu utilisais Spring pour ça avec un context ThreadLocal, dans ce cas de retour à la couche JSF, ta session est fermée.
    Tu pourrais peut-être charger la session à un autre niveau (quelles autres possibilités ?)
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  12. #32
    Membre confirmé
    Profil pro
    Inscrit en
    Janvier 2006
    Messages
    365
    Détails du profil
    Informations personnelles :
    Localisation : Maroc

    Informations forums :
    Inscription : Janvier 2006
    Messages : 365
    Points : 495
    Points
    495
    Par défaut
    Oui, c'est bien ça le fonctionnement du lazy loading. En ce qui me concerne, j'utilise plein de fetch join, car je ne garde pas de session ouverte longtemps. Etant donné que mes transactions sont gérées sur la couche service et que la session hibernate est liée à la transaction, je ne renvoie que des objets détachés à la couche présentation.
    Mais dans ton cas, tu utilises OpenSessionInViewFilter, et donc la session reste ouverte, ce qui devrait permettre au lazy loading de fonctionner comme on le comprend. J'avoue que je n'ai pas encore d'explication à ce comportement que tu rencontres, sauf si le problème devrait venir d'ailleurs, d'autant plus que tu dis n'avoir jamais eu de problème de ce type avant sans utiliser des fetch join.
    SCJP 5 / SCBCD 1.3 Certified

  13. #33
    Membre à l'essai
    Profil pro
    Inscrit en
    Septembre 2006
    Messages
    36
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2006
    Messages : 36
    Points : 13
    Points
    13
    Par défaut
    Bon je continue mes tests, et si j'instrumentalise mon code, ca marche correctement (j'ai meme des LazyInitializeException de temps en temps!!). Ce qui m'embete le plus dans cette histoire, c'est que je sois obligé d'instrumentaliser mon code pour avoir un comportement coherent finalement. Je ne comprends vraiment pas pourquoi.

    A moins que hibernate s'attendent a trouver un EntityManager (qui n'existe meme pas). mais dans ce cas j'aurais des erreurs quand meme...

    En ce qui concerne la session, je ne sais pas ou je pourrais ouvrir la session. A priori, je ne m'occupe pas de l'ouverture/fermeture de la session, c'est HibernateTemplate qui s'en charge. D'apres ce que j'ai compris, lui, il ouvre la session des qu'il en a besoin et qu'il n'en a pas (il les stocke en threadLocal).
    C'est la qu'intervient OpenSessionInViewFilter: effectivement, normallement, quand tu reviens vers la couche JSF, ta session est fermée. Le filtre vas donc reattacher une session en ThreadLocal, HibernateTemplate vas donc utiliser cette session plutot que d'en creer une nouvelle.

  14. #34
    Membre confirmé
    Profil pro
    Inscrit en
    Janvier 2006
    Messages
    365
    Détails du profil
    Informations personnelles :
    Localisation : Maroc

    Informations forums :
    Inscription : Janvier 2006
    Messages : 365
    Points : 495
    Points
    495
    Par défaut
    Je crois que ce n'est pas normal que tu ais des LazyInitializeException de temps en temps. En principe quand tu utilises OpenSessionInViewFilter, la session hibernate est ouverte au moment où la requête est reçue et n'est refermée qu'une fois la vue rendue. S'il y a enchevêtrement de sessions hibernate, ça veut dire qu'il y a un problème dans l'utilisation même du pattern OpenSessionInViewFilter. C'est peut-être une piste à explorer.
    SCJP 5 / SCBCD 1.3 Certified

  15. #35
    Membre à l'essai
    Profil pro
    Inscrit en
    Septembre 2006
    Messages
    36
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2006
    Messages : 36
    Points : 13
    Points
    13
    Par défaut
    En fait, mes lazy exception arrivent dans des cas particulier: c'est quand j'utilise un objet "intermediaire" (par exemple, un BOUtilisateur qui contient un objet IUtilisateur, celui qui est "persisté").

    Bon, donc je m'en suis sorti, en combinant plusieurs choses. D'une part j'instrumentalise mes classes persistantes.
    Et ensuite, dans le cas d'une lazy, je "rattache" l'objet a la session (dans l'exemple precedent, je rattache IUtilisateur, pas BOUtilisateur).

    Pour ceux que ca interresse
    Le code maven pour instrumentaliser les classes:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
                <plugin>
                    <artifactId>maven-antrun-plugin</artifactId>
                    <version>1.1</version>
                    <executions>                  
                        <execution>
                            <id>Instrument domain classes</id>
                            <configuration>
                                <tasks>
                                    <taskdef name="instrument" classname="org.hibernate.tool.instrument.cglib.InstrumentTask">
                                        <classpath>
                                            <path refid="maven.dependency.classpath" />
                                            <path refid="maven.plugin.classpath" />
                                        </classpath>
                                    </taskdef>
                                    <instrument verbose="true">
                                        <fileset dir="${project.build.outputDirectory}">
                                            <include name="**/impl/model/**/*.class" />
                                            <exclude name="**/impl/model/Configuration.class" />
                                            <exclude name="**/impl/model/Constants.class" />
                                        </fileset>
                                    </instrument>
                                </tasks>
                            </configuration>
                            <phase>process-classes</phase>
                            <goals>
                                <goal>run</goal>
                            </goals>
                        </execution>
                    </executions>
                    <dependencies>
                        <dependency>
                            <groupId>org.hibernate</groupId>
                            <artifactId>hibernate</artifactId>
                            <version>${hibernate.version}</version>
                        </dependency>
                    </dependencies>
                </plugin>
    Et le code de mes fonctions de "rattachement":
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
     
        public Session getHibernateSession() {
            final Session session = this.getSession(false);
            return session;
        }
     
        public void evict(final Object _obj) {
            this.getHibernateTemplate().evict(_obj);
        }
     
        public void lock(final Object obj) {
            this.getHibernateTemplate().lock(obj, LockMode.NONE);
        }
     
        @SuppressWarnings("unchecked")
        public Object lockAndEvict(final Object _obj) {
            if (_obj == null) {
                return null;
            }
            if (_obj instanceof Collection) {
                final Collection collection = (Collection) _obj;
                final List newCollection = new ArrayList();
     
                final Iterator iter = collection.iterator();
                while (iter.hasNext()) {
                    newCollection.add(this.lockAndEvict(iter.next()));
                    iter.remove();
                }
                collection.addAll(newCollection);
                return collection;
            }
     
            return this.lockAndEvictInternal(_obj);
        }
     
        protected Object lockAndEvictInternal(final Object _obj) {
            try {
                final Object obj2 = this.getObjectInHibernateSession(_obj);
                if ((obj2 != null) && (obj2 != _obj)) {
                    this.debug("evicting " + obj2 + " for " + _obj);
                    this.evict(obj2);
                }
                this.lock(_obj);
            } catch (final Exception excp) {
                this.debug("cannot lock " + _obj);
            }
            return _obj;
        }
     
        public Object getObjectInHibernateSession(final Object _obj) {
            Object result = null;
            try {
                final SessionImpl session = (SessionImpl) this.getHibernateSession();
                Serializable identifier = null;
                try {
                    identifier = session.getIdentifier(_obj);
                } catch (final TransientObjectException excp) {
                    try {
                        identifier = session.getEntityPersister(null, _obj).getIdentifier(_obj,
                                session.getEntityMode());
                    } catch (final MappingException excp1) {
                        identifier = null;
                    }
                }
                if (identifier != null) {
                    final EntityKey key = new EntityKey(identifier, session.getEntityPersister(null,
                            _obj), session.getEntityMode());
                    result = session.getPersistenceContext().getEntity(key);
                }
            } catch (final Exception excp) {
                this.debug("cannot recuparate object " + _obj + " from hibernate session");
            }
            return result;
        }
    C'est la méthode lockAndEvict qu'il faut appeler, en lui passant soit l'objet persistant, soit une collection d'objet persistant. A noter qu'utiliser lock et evict permet de rester "transactionAware", ce que merge ne permet pas par exemple.

  16. #36
    Membre à l'essai
    Profil pro
    Inscrit en
    Septembre 2006
    Messages
    36
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2006
    Messages : 36
    Points : 13
    Points
    13
    Par défaut
    N'empeche que je ne comprends toujours pas pourquoi je dois instrumentaliser mon code...

Discussions similaires

  1. @EJB annotation et lazy relation
    Par Sugus dans le forum Java EE
    Réponses: 1
    Dernier message: 14/09/2010, 13h33
  2. [EJB3] Annotations non reconnues
    Par ericw78 dans le forum Eclipse Java
    Réponses: 2
    Dernier message: 01/03/2006, 21h35
  3. [HIBERNATE 3]Lazy loading
    Par SEMPERE Benjamin dans le forum Hibernate
    Réponses: 11
    Dernier message: 08/02/2006, 22h40
  4. [FPDF] Comment mettre une annotation sur un Pdf déjà existant en PHP
    Par shequet dans le forum Bibliothèques et frameworks
    Réponses: 2
    Dernier message: 13/09/2005, 11h23

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