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

JPA Java Discussion :

OneToMany et Héritage


Sujet :

JPA Java

  1. #1
    Membre averti
    Profil pro
    Inscrit en
    Septembre 2007
    Messages
    17
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Septembre 2007
    Messages : 17
    Par défaut OneToMany et Héritage
    Bonjour,

    Voici ma config:
    Serveur d'application: JBoss 4.2
    DB : MySql 5.0.22

    Voici maintenant l'organisation de mes classes ( la DB correspondante est parfaitement générée ):

    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
     
    @Entity
    @Inheritance(strategy = InheritanceType.JOINED)
    public class A implements Serializable
    {
    	...
    }
     
     
    @Entity
    @Inheritance(strategy = InheritanceType.JOINED)
    public class B extends A
    {
    	...
    }
     
     
    @Entity
    @NamedQuery(name = "findMyObjects", query = "SELECT mo FROM MyObject mo")
    public class MyObject implements Serializable
    {
    	@OneToMany
    	protected Collection<A> collectionOfA;
     
    	...
     
    	Collection<A> getCollectionOfA()
    	{
    		return collectionOfA;
    	}
    }
    --> Dans l'EJB session, j'essaye de recupere la liste de mes objets

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    // The query used to find MyObjects
    Query findMyObjects = entityManager.createNamedQuery( "findMyObjects" );
     
    // Get the list of MyObject
    Collection<MyObject> myObjects = findMyObjects.getResultList();

    Mon problème est que myObjects.getCollectionOfA() retourne toujours une liste vide. Pourtant il y a des entrées dans la base.

    Mon intuition c'est qu'il y a un problème dans la combinaison héritage + OneToMany relation, mais comment le résoudre ?

    Merci d'avance a ceux qui pourront m'aider.

    Cédric Templie

  2. #2
    Expert confirmé
    Avatar de djo.mos
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    4 666
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2004
    Messages : 4 666
    Par défaut
    Bonjour.
    Tout d'abord, votre post semble manquer d'infos: Qu'est ce que retourne ta requête nommé ? (c'est un select, ok, mais de quelle entité ?), où et comment est défini ta relation OneToMany ?

    Sinon, tu es bien sûr de vouloir avoir 2 entités dans une même hiérarchie ?
    Perso, je m'y prendrais autrement:
    Je déclares une classe C comme étant MappedSuperClass (pas une Entity) et je ferais hériter A et B de C.

  3. #3
    Membre averti
    Profil pro
    Inscrit en
    Septembre 2007
    Messages
    17
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Septembre 2007
    Messages : 17
    Par défaut
    Je vais essayer de clarifier.

    La requête nommée retourne une collection de MyObject, j'ai juste utilisé un alias dans la requête.

    J'ai du mal a définir la relation OneToMany différemment de comment je l'ai fait pour plusieurs raison, et ca répond aussi a pourquoi j'ai fait un heritage d'entité.

    En fait j'ai une class Document ( 3 champs: type, fileName, filePath ), et ensuite j'ai un héritage pour les différents types de documents.
    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
     
    class CarteIdentite extends Document
    {
         // Données de ma carte d'identite
    }
     
    class Passeport extends Document
    {
         // Données de mon passeport
    }
     
    class DocumentLambda extend Document
    {
         // Données du document lambda
    }
    Maintenant il faut savoir que j'ai plein d'objets qui peuvent avoir des documents liés, et de n'importe quel type.

    Exemple:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    class Person
    {
         // Une personne a une liste de documents associés ( n'importe quel type possible )
         Collection<Document> documents;
    }
    J'espere que j'ai reussi a clarifier le contexte dans lequel je suis.

    Encore merci

    Cédric Templie

  4. #4
    Membre averti
    Profil pro
    Inscrit en
    Septembre 2007
    Messages
    17
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Septembre 2007
    Messages : 17
    Par défaut
    Ah oui, j'ai oublié de préciser que j'ai des documents dont je ne connais pas le type, c'est pour ca que je fais exister Document en tant qu'entite.

    Je vais quand meme essayer d'approfondir la solution avec MappedSuperClass, en utilisant une classe UnknownDocument.

    Je met à jour le post dès que j'ai tester la solution avec MappedSuperClass.

    Merci pour l'aide.

    Cédric Templie

  5. #5
    Membre averti
    Profil pro
    Inscrit en
    Septembre 2007
    Messages
    17
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Septembre 2007
    Messages : 17
    Par défaut
    Aiie...

    Ca n'est pas possible d'avoir une relation OneToMany sur une classe qui n'est pas une entité.

    Me voila donc revenu au point de départ.

    Si quelqu'un a une idée je suis preneur

    Merci encore

    Cédric Templie

  6. #6
    Membre Expert
    Homme Profil pro
    Inscrit en
    Septembre 2006
    Messages
    2 966
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Septembre 2006
    Messages : 2 966
    Par défaut
    Citation Envoyé par _Cedrix_ Voir le message
    Aiie...

    Ca n'est pas possible d'avoir une relation OneToMany sur une classe qui n'est pas une entité.
    par définition...

    mais OneToMany sur la classe racine (éventuellement abstract) d'une hiérarchie fonctionne très bien…

    si vous n'avez pas activé l'auto-détection des annotations, vous avez peut-être oublié une classe dans le persistence.xml (ou une typo…)

  7. #7
    Membre averti
    Profil pro
    Inscrit en
    Septembre 2007
    Messages
    17
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Septembre 2007
    Messages : 17
    Par défaut
    Bonjour,

    Mon persistence.xml est minimaliste:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <?xml version="1.0"?>
    <persistence version="1.0">
    	<persistence-unit name="EchoParcel">
    		<jta-data-source>java:/EchoParcelDS</jta-data-source>
    		<properties>
    			<property name="hibernate.hbm2ddl.auto" value="create-drop" />
    			<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />
    			<property name="hibernate.show-sql" value="true" />
    		</properties>
    	</persistence-unit>
    </persistence>
    D'après les logs de JBoss et la base MySQL, les entités sont bien trouvées et bindées.
    Je suis assez d'accord pour dire que ça fonctionne, puisque l'ajout de Person avec des Documents fonctionne parfaitement ( la base est bien populée ).

    Mon problème est au niveau de la récupération des documents associés à une personne. Il y a certainement qqe chose qui ne va pas avec l'execution de ma requête, mais je n'arrive pas à voir quoi.

    Je rappelle la manière dont je procède pour la requête:

    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
    @Entity
    @NamedQuery(name = "findMyObjects", query = "SELECT mo FROM MyObject mo")
    public class MyObject implements Serializable
    {
    	@OneToMany
    	protected Collection<A> collectionOfA;
     
    	...
     
    	Collection<A> getCollectionOfA()
    	{
    		return collectionOfA;
    	}
    }
     
     
     
    // Dans l'EJB Session
     
    // The query used to find MyObjects
    Query findMyObjects = entityManager.createNamedQuery( "findMyObjects" );
     
    // Get the list of MyObject
    Collection<MyObject> myObjects = findMyObjects.getResultList();
    Encore merci

    Cédric Templie

  8. #8
    Membre Expert
    Homme Profil pro
    Inscrit en
    Septembre 2006
    Messages
    2 966
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Septembre 2006
    Messages : 2 966
    Par défaut
    Citation Envoyé par _Cedrix_ Voir le message
    Bonjour,

    Mon persistence.xml est minimaliste:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <?xml version="1.0"?>
    <persistence version="1.0">
    	<persistence-unit name="EchoParcel">
    		<jta-data-source>java:/EchoParcelDS</jta-data-source>
    		<properties>
    			<property name="hibernate.hbm2ddl.auto" value="create-drop" />
    			<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />
    			<property name="hibernate.show-sql" value="true" />
    		</properties>
    	</persistence-unit>
    </persistence>
    D'après les logs de JBoss et la base MySQL, les entités sont bien trouvées et bindées.
    Je suis assez d'accord pour dire que ça fonctionne, puisque l'ajout de Person avec des Documents fonctionne parfaitement ( la base est bien populée ).

    Mon problème est au niveau de la récupération des documents associés à une personne. Il y a certainement qqe chose qui ne va pas avec l'execution de ma requête, mais je n'arrive pas à voir quoi.

    Je rappelle la manière dont je procède pour la requête:

    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
    @Entity
    @NamedQuery(name = "findMyObjects", query = "SELECT mo FROM MyObject mo")
    public class MyObject implements Serializable
    {
    	@OneToMany
    	protected Collection<A> collectionOfA;
     
    	...
     
    	Collection<A> getCollectionOfA()
    	{
    		return collectionOfA;
    	}
    }
     
     
     
    // Dans l'EJB Session
     
    // The query used to find MyObjects
    Query findMyObjects = entityManager.createNamedQuery( "findMyObjects" );
     
    // Get the list of MyObject
    Collection<MyObject> myObjects = findMyObjects.getResultList();
    Encore merci

    Cédric Templie
    êtes-vous certain que la foreign key de A vers MyObject est correcte ?
    si vous faites un
    "select o.*, a.* from MyObject o
    join A as a on a.REF_MY_OBJECT = o.ID"
    directement en console DB, est-ce que cela donne le résultat espéré ?

  9. #9
    Membre averti
    Profil pro
    Inscrit en
    Septembre 2007
    Messages
    17
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Septembre 2007
    Messages : 17
    Par défaut
    La relation que j'ai entre MyObject et A est unidirectionnelle, donc, je n'ai pas de reference a MyObject dans A.
    Pour reprendre avec des examples plus concrets, un document n'a pas de reference sur une personne qui le possède.
    La requête que vous proposez ne peux donc pas être exécutée. Cependant j'ai quand même une table de liaison pour faire la correspondance entre personnes et documents:

    la Table PersonDocuments avec 2 colonnes Person_ID et Document_ID

    Grâce à cette table je peux faire des requêtes SQL pour récupérer les documents d'une personne.
    L'ajout d'une personne dans la base avec des documents associés fonctionne correctement, la table PersonDocuments est rempli correctement.
    Je procède comme suit pour ajouter une personne avec des documents liés:
    1: Merge/Persist des documents dans la base --> Rempli les tables correctement en fonction du type du document
    2: Association des documents mergés avec la personne: person.setDocuments( mergedDocuments )
    3: Persist de la personne. La table d'association PersonDocuments est remplie corretement.

    Par contre dans ma class Person, si au lieu d'avoir une collection de Document, j'utilise une collection de IdentityCard, le résultat de la recherche fonctionne parfaitement, je récupère bien mon objet Person avec les IdentityCards.
    Bon evidemment j'ai juste fais ca pour tester, parce qu'il doit etre possible dans mon systeème d'associer n'importe quel type de document à une personne.

    Je ne comprend vraiment pas pourquoi ça ne marche pas avec une collection de Document....

    Toujours merci pour l'aide

  10. #10
    Membre Expert
    Homme Profil pro
    Inscrit en
    Septembre 2006
    Messages
    2 966
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Septembre 2006
    Messages : 2 966
    Par défaut
    Citation Envoyé par _Cedrix_ Voir le message
    La relation que j'ai entre MyObject et A est unidirectionnelle, donc, je n'ai pas de reference a MyObject dans A.
    Pour reprendre avec des examples plus concrets, un document n'a pas de reference sur une personne qui le possède.
    La requête que vous proposez ne peux donc pas être exécutée. Cependant j'ai quand même une table de liaison pour faire la correspondance entre personnes et documents:

    la Table PersonDocuments avec 2 colonnes Person_ID et Document_ID

    Grâce à cette table je peux faire des requêtes SQL pour récupérer les documents d'une personne.
    L'ajout d'une personne dans la base avec des documents associés fonctionne correctement, la table PersonDocuments est rempli correctement.
    Je procède comme suit pour ajouter une personne avec des documents liés:
    1: Merge/Persist des documents dans la base --> Rempli les tables correctement en fonction du type du document
    2: Association des documents mergés avec la personne: person.setDocuments( mergedDocuments )
    3: Persist de la personne. La table d'association PersonDocuments est remplie corretement.

    Par contre dans ma class Person, si au lieu d'avoir une collection de Document, j'utilise une collection de IdentityCard, le résultat de la recherche fonctionne parfaitement, je récupère bien mon objet Person avec les IdentityCards.
    Bon evidemment j'ai juste fais ca pour tester, parce qu'il doit etre possible dans mon systeème d'associer n'importe quel type de document à une personne.

    Je ne comprend vraiment pas pourquoi ça ne marche pas avec une collection de Document....

    Toujours merci pour l'aide

    vous implémentez une relation ManyToMany et vous la déclarez OneToMany :
    c'est normal que cela ne fonctionne pas...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    @ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinTable(
        name="PersonDocuments",
        joinColumns=@JoinColumn(name="person_ID"),
        inverseJoinColumns=@JoinColumn(name="document_ID")
    )
    protected List<Document> documents = new ArrayList<Document>() ;
    Si vous voulez absolument utiliser le @OneToMany dans Person alors la collection doit être de type PersonDocument et non Document...
    et vous devez aller mettre des @ManyToOne dans PersonDocument...
    et un @OneToMany sur PersonDocument dans Document...

  11. #11
    Membre averti
    Profil pro
    Inscrit en
    Septembre 2007
    Messages
    17
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Septembre 2007
    Messages : 17
    Par défaut
    Ma base de données est générée depuis mes classes.

    La relation OneToMany sur des Documents dans la classe Person génère une table person_documents dans la base de données. Je ne pense pas qu'il est possible d'éviter ça.
    Normalement il est possible de définir la relation OneToMany que d'un seul coté ?
    Dans la base je comprend aisément que cela nécéssite une table d'association, mais au niveau du java il n'y a pas besoin de gérer l'association des 2 cotés....

    Qu'entendez-vous par PersonDocument ? une nouvelle classe qui hérite de Document ?

    J'essaye donc de passer par une relation ManyToMany. Voici mes classes mises a jour:

    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
    @Entity
    @Table(name = "document")
    @Inheritance(strategy = InheritanceType.JOINED)
    public class Document implements Serializable
    {
    	@ManyToMany(mappedBy = "documents", fetch = FetchType.LAZY)
    	Collection<Person> persons = new ArrayList<Person>();
            ...
    }
     
    @Entity
    @Table(name = "person")
    @NamedQueries(
    {
    	@NamedQuery(name = "findPersons", query = "SELECT p FROM Person p") })
    public class Person implements Serializable
    {
    	/**
             * 
             */
    	@ManyToMany(fetch=FetchType.EAGER, cascade=CascadeType.ALL)
    	protected Collection<Document> documents = new ArrayList<Document>();
            ...
    }
    Je ne peux pas utiliser FetchType.LAZY dans Person sinon j'ai une exception:

    Exception in thread "AWT-EventQueue-0" org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: echo.parcel.be.Person.documents, no session or session was closed


    Même avec cette solution, la liste de documents associée à une personne après avoir utilisé la requête "findPersons" est toujours vide...

    Merci

  12. #12
    Membre Expert
    Homme Profil pro
    Inscrit en
    Septembre 2006
    Messages
    2 966
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Septembre 2006
    Messages : 2 966
    Par défaut
    Citation Envoyé par _Cedrix_ Voir le message
    Ma base de données est générée depuis mes classes.

    La relation OneToMany sur des Documents dans la classe Person génère une table person_documents dans la base de données. Je ne pense pas qu'il est possible d'éviter ça.
    Normalement il est possible de définir la relation OneToMany que d'un seul coté ?
    Dans la base je comprend aisément que cela nécéssite une table d'association, mais au niveau du java il n'y a pas besoin de gérer l'association des 2 cotés....

    Qu'entendez-vous par PersonDocument ? une nouvelle classe qui hérite de Document ?

    J'essaye donc de passer par une relation ManyToMany. Voici mes classes mises a jour:

    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
    @Entity
    @Table(name = "document")
    @Inheritance(strategy = InheritanceType.JOINED)
    public class Document implements Serializable
    {
    	@ManyToMany(mappedBy = "documents", fetch = FetchType.LAZY)
    	Collection<Person> persons = new ArrayList<Person>();
            ...
    }
     
    @Entity
    @Table(name = "person")
    @NamedQueries(
    {
    	@NamedQuery(name = "findPersons", query = "SELECT p FROM Person p") })
    public class Person implements Serializable
    {
    	/**
             * 
             */
    	@ManyToMany(fetch=FetchType.EAGER, cascade=CascadeType.ALL)
    	protected Collection<Document> documents = new ArrayList<Document>();
            ...
    }
    Je ne peux pas utiliser FetchType.LAZY dans Person sinon j'ai une exception:

    Exception in thread "AWT-EventQueue-0" org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: echo.parcel.be.Person.documents, no session or session was closed


    Même avec cette solution, la liste de documents associée à une personne après avoir utilisé la requête "findPersons" est toujours vide...

    Merci
    la relation OneToMany génère une table parce que vous ne donnez pas de JoinColumn et donc Hibernate n'a pas d'autre choix pour résoudre la situation que vous lui soumettez …

    quant à l'exception due au LAZY, c'est que vous avez des problèmes ailleurs dans la gestion de vos transactions… cela n'a rien à voir avec la définition des relations… mais vous avez plus qu'intérêt à le résoudre : vous ne pouvez avoir qu'un seule relation EAGER par entité…
    donc le jour où vous devez ajouter une autre relation à partir de Person, vous allez être plus qu'embêté…

    maintenant pour débuguer votre problème : vérifier les relations dans la DB avec des select … join…
    et activer le log du sql généré par Hibernate…


    (et PersonDocument est évidemment une typo pour PersonDocuments, c'est l'entité intersection entre Person et Document dans le cas où vous décidez d'implémenter le many-to-many "à la main" en définissant 2 ManyToOne vous-mêmes à partir de cette classe intersection, ce qui peut-être nécessaire si vous devez ajouter des attributs à l'intersection…)

  13. #13
    Membre averti
    Profil pro
    Inscrit en
    Septembre 2007
    Messages
    17
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Septembre 2007
    Messages : 17
    Par défaut
    Merci pour les infos

    Je teste et verifie tout ça, et je vous tiens au courant, encore merci.

  14. #14
    Membre averti
    Profil pro
    Inscrit en
    Septembre 2007
    Messages
    17
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Septembre 2007
    Messages : 17
    Par défaut
    Ca marche !!!

    J'ai enfin réussi a faire tout fonctionner comme je le voulais.

    Merci beaucoup pour votre aide.

    J'ai verifier les requêtes exécutées par hibernate. Et effectivement en l'executant je n'optenait pas le resultat escompté.

    Ca ne marchait pas parce que certains de mes documents contiennent un objet embedded qui a lui même une référence non null sur un autre objet.
    Même si je ne me sers pas de ce type de document, la requête les prend deja en compte, du coup il y a un "inner join" dans la requête au lieu d'utiliser un "left outer join".

    J'ai résolu le problème en retirant le fait que la référence doit être non null.

    Par contre j'ai toujours mon problème d'exception avec le lazy fetch (certainement un problème avec le scope d'utilisation de l'objet retourné par la requête). Je vais jeter un coup d'oeil sur les forums, et je pense trouver la réponse.

    En tout cas encore merci pour votre aide.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Héritage JPA et OneToMany
    Par ray_fab dans le forum JPA
    Réponses: 8
    Dernier message: 10/12/2011, 21h36
  2. Héritage et relation OneToMany
    Par wsp_ape dans le forum JPA
    Réponses: 0
    Dernier message: 18/05/2011, 20h29
  3. [Postgresql]Héritage
    Par lheureuxaurelie dans le forum PostgreSQL
    Réponses: 13
    Dernier message: 02/10/2008, 10h18
  4. [Postgres] Héritage + Clés
    Par k-reen dans le forum PostgreSQL
    Réponses: 6
    Dernier message: 21/05/2003, 17h37
  5. Héritage entre Forms
    Par BarBal dans le forum Composants VCL
    Réponses: 7
    Dernier message: 29/08/2002, 18h44

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