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 :

Mapping : Tables génériques


Sujet :

JPA Java

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Nouveau membre du Club
    Inscrit en
    Décembre 2011
    Messages
    8
    Détails du profil
    Informations forums :
    Inscription : Décembre 2011
    Messages : 8
    Par défaut Mapping : Tables génériques
    Bonjour,

    J'ai un gros souci pour mapper une relation avec des tables génériques.

    Ce que j'appelle une table générique, est une table qui contient plusieurs types de données :

    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
    +-----------------------------------------------------------+
    | GENERIC                                                   |
    +-----+-----------------+-----------------+-----------------+
    | ID  | TYPE            | CODE            | LIBELLE         |
    +-----+-----------------+-----------------+-----------------+
    | 1   | STA             | ACT             | ACTIF           |
    | 2   | STA             | INA             | INACTIF         |
    | 3   | MAT             | MAR             | MARIE           |
    | 4   | MAT             | CEL             | CELIBATAITRE    |
    | 5   | CIV             | MR              | MONSIEUR        |
    | 6   | CIV             | MME             | MADAME          |
    | 7   | CIV             | MLE             | MADEMOISELLE    |
    | 8   | TRA             | MAR             | MARITIME        |
    | 9   | TRA             | AER             | AERIEN          |
    | 10  | TRA             | FER             | FERROVIAIRE     |
    +-----+-----------------+-----------------+-----------------+
    Dans les autres tables, j'ai des références vers cette table "GENERIC", mais qui se fait avec le CODE et non l'ID...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    +-----------------------------------------------------------------------+
    | CLIENT                                                                |
    +-----+-----------------+-----------------+-----------+-----------------+
    | ID  | NOM             | PRENOM          | CIVILITE  | SITU_MATRI      |
    +-----+-----------------+-----------------+-----------+-----------------+
    | 1   | DUPONT          | STHEPANIE       | MME       | MAR             |
    | 2   | DURAND          | CHRISTINE       | MLE       | CEL             |
    +-----+-----------------+-----------------+-----------+-----------------+
    Du coup, j'ai un gros problème : si je fais la relation entre un CLIENT et un GENERIC uniquement avec le CODE, je risque d'avoir plusieurs lignes !
    Ex: MAR -> MARIE, MARITIME, ...

    Dans le cas de la situation matrimoniale, je veux faire la relation suivante :
    CLIENT.SITU_MATRI=GENERIC.CODE et GENERIC.TYPE='MAT'

    Et pour les civilités, je veux faire la relation :
    CLIENT.CIVILITE=GENERIC.CODE et GENERIC.TYPE='CIV'.


    Bien sûr, je n'ai pas le droit de toucher au modèle de données

    Comment faire pour mapper cette relation svp ?


    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
    @Entity
    @Table(name="GENERIC")
    public class Generic implements Serializable {
     
    	@Id
    	@Column(name="ID", nullable=false)
    	@GeneratedValue(strategy=GenerationType.AUTO)
    	private Long id;
     
    	@Column(name="TYPE", nullable=false)
    	private String type;
     
    	@Column(name="CODE", nullable=false)
    	private String code;
     
    	@Column(name="LIBELLE", nullable=false)
    	private String libelle;
    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="CLIENT")
    public class Client {
     
    	@Id
    	@Column(name="ID", nullable=false)
    	@GeneratedValue(strategy=GenerationType.AUTO)
    	private Long id;
     
    	@Column(name="NOM", length=50, nullable=false)
    	private String nom;
     
    	@Column(name="PRENOM", length=50, nullable=false)
    	private String prenom;
     
    	@ManyToOne
    	@JoinColumns({@JoinColumn(name="???", referencedColumnName="TYPE"),
            		@JoinColumn(name="CIVILITE", referencedColumnName="CODE")})
    	private Generic civilite;
     
    	@ManyToOne
    	@JoinColumns({@JoinColumn(name="???", referencedColumnName="TYPE"),
            		@JoinColumn(name="SITU_MATRI", referencedColumnName="CODE")})
    	private Generic situationMatrimoniale;

  2. #2
    Membre Expert
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    2 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 2 938
    Par défaut
    Bonjour,
    Tu ne peux pas effectuer ce type de joinutre automatiquement, en l'état une jointure ne peut se faire qu'entre les colonnes clés primaires. Tu seras obligé de faire ta jointure uniquement sur la colonne clé primaire, et procéder par NamedQuery (avec le bon filtre sur la colonne TYPE) pour extraire tes données.

  3. #3
    Nouveau membre du Club
    Inscrit en
    Décembre 2011
    Messages
    8
    Détails du profil
    Informations forums :
    Inscription : Décembre 2011
    Messages : 8
    Par défaut
    Ta remarque sur le join est très intéressante ! J'ai testé pas mal de possibilités, et effectivement, j'avais toujours l'ID côté CLIENT... au lieu du code. Tout prend son sens.
    J'abandonne donc cette piste. Merci !!

    Entre temps, j'ai pensé à autre chose :
    - intégrer la donnée "CODE" côté client en tant qu'attribut persisté de type String
    - ajouter un attribut transient de type Generic pour contenir le code/libellé
    - ajouter une EventListener sur l'entité Client, avec des méthodes @PostLoad et @PrePersist.

    Je serais très preneur de critiques/conseils/remarques par rapport à ça.

    Ca donne à peu près ça :
    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
    @Entity
    @Table(name="CLIENT")
    @EntityListeners(ClientListener.class)
    public class Client {
     
    	@Id
    	@Column(name="ID", nullable=false)
    	@GeneratedValue(strategy=GenerationType.AUTO)
    	private Long id;
     
    	@Version
    	@Column(name="VERSION", nullable=false)
    	private int version;
     
    	@Column(name="NOM", unique=true, length=50, nullable=false)
    	private String nom;
     
    	@Column(name="PRENOM", length=50, nullable=false)
    	private String prenom;
     
    	@Column(name="CIVILITE")
    	private String codeCivilite;
     
    	@Transient
    	private Civilite civilite;
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    @Entity
    @MappedSuperclass
    @DiscriminatorValue(value="CIV")
    public class Civilite extends Generic {
     
    	public Civilite() {
    		super();
    	}
     
    	public Civilite(String type, String code, String libelle) {
    		super(type, code, libelle);
    	}
     
    }
    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
    @Entity
    @Table(name="GENERIC")
    @Inheritance(strategy=InheritanceType.SINGLE_TABLE)
    @DiscriminatorColumn(name="TYPE")
    public class Generic implements Serializable {
     
    	private static final long serialVersionUID = 2059698217750789116L;
     
    	@Id
    	@Column(name="ID", nullable=false)
    	@GeneratedValue(strategy=GenerationType.AUTO)
    	private Long id;
     
    	@Version
    	@Column(name="VERSION", nullable=false)
    	private int version;
     
    	@Column(name="TYPE", nullable=false, updatable=false, insertable=false)
    	private String type;
     
    	@ForeignKey(name="CODE")
    	private String code;
     
    	@Column(name="LIBELLE", nullable=false)
    	private String libelle;
    Je ne suis pas encore allé au bout de cette piste, et donc pour le moment, voici mon Listener :

    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
    public class ClientListener {
     
    	@PersistenceContext 
    	EntityManager em;
     
    	@PostLoad
    	public void populateTransientFields(Client c) {
    		String hql = "select c from Civilite c where c.code = :code";
    		Query query = em.createQuery(hql);
    		query.setParameter("code", c.getCodeCivilite());
    		c.setCivilite((Civilite) query.getSingleResult());
        }
     
    	@PrePersist
    	public void populatePersistentFields(Client c) {
    		c.setCodeCivilite(c.getCivilite().getCode());
    	}

  4. #4
    Membre Expert
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    2 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 2 938
    Par défaut
    Bonjour,
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    @Column(name="ID", nullable=false)
    	@GeneratedValue(strategy=GenerationType.AUTO)
    	private Long id;
    	...
    	@Column(name="NOM", unique=true, length=50, nullable=false)
    ...
    J'aurai juste 2 petites remarques :
    Une colonne clé primaire est par défaut obligatoire, donc pas la peine de remettre une couche de non nullité.
    Et également mettre la clause "unique=true" alourdit les traitements surtout si cette contrainte est déjà définie en base.

  5. #5
    Nouveau membre du Club
    Inscrit en
    Décembre 2011
    Messages
    8
    Détails du profil
    Informations forums :
    Inscription : Décembre 2011
    Messages : 8
    Par défaut
    Merci pour ces remarques, je vais en prendre bonne note.


    Sinon, concernant la piste que j'explore, c'est pas gagné.
    Je suis parti la fleur au fusil en pensant qu'il serait facile d'injecter des beans managés par Spring dans un Listener qui lui sera instancé par JPA...
    En fait, c'est pas si simple.

    Au stade où j'en suis, il semble que je doive passer par aspectj... Je continue de creuser. Si j'obtiens un résultat, j'en ferai profiter tout le monde, mais peut-être qu'on m'expliquera que c'est une hérésie aussi.

  6. #6
    Nouveau membre du Club
    Inscrit en
    Décembre 2011
    Messages
    8
    Détails du profil
    Informations forums :
    Inscription : Décembre 2011
    Messages : 8
    Par défaut
    Bonsoir,

    Oubliez l'usine à gaz que j'allais mettre en place...
    Avec une annotation Hibernate, on s'en sort très bien : @WhereJoinTable.
    Je ne suis donc plus full JPA, un peu dépendant de l'implémentation, mais c'est un moindre mal.

    J'espère que JPA 2 adressera cette problématique.


    ps: le temps de nettoyer le code, je posterai l'exemple

  7. #7
    Membre Expert
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    2 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 2 938
    Par défaut
    Bonjour,
    Hum hum ça m'intéresse ça, elle sert à quoi cette clause ??

  8. #8
    Nouveau membre du Club
    Inscrit en
    Décembre 2011
    Messages
    8
    Détails du profil
    Informations forums :
    Inscription : Décembre 2011
    Messages : 8
    Par défaut
    Pour répondre à ta qustion : si l'entité Client possède une référence vers une entité Reference, alors on peut rajouter une clause dans le WHERE sur la relation.

    @Where -> la clause concernerait une colonne de l'entité (ici Client)
    @WhereJoinTable -> la clause concerne une colonne de l'entité avec laquelle on fait une jointure (ici Reference)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    	@ManyToOne
    	@WhereJoinTable(clause="TYPE='CIV'")
    	@JoinColumns({@JoinColumn(name="CIVILITE", referencedColumnName="CODE"))
    	private Reference civilite;
    Dans mon cas, j'ai besoin d'indiquer que sur la jointure entre un Client et une Reference qui me permet de récupérer la civilité, la colonne TYPE de la table REFERENCE doit avoir la valeur 'CIV'.

  9. #9
    Nouveau membre du Club
    Inscrit en
    Décembre 2011
    Messages
    8
    Détails du profil
    Informations forums :
    Inscription : Décembre 2011
    Messages : 8
    Par défaut
    Sinon, je me suis un peu emballé.


    En effet, cette écriture pose un problème :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    	@ManyToOne
    	@WhereJoinTable(clause="TYPE='CIV'")
    	@JoinColumns({@JoinColumn(name="CIVILITE", referencedColumnName="CODE"))
    	private Reference civilite;
    JPA en déduit qu'il faut rajouter une contrainte d'unicité sur la colonne CODE de la table REFERENCE.
    -> Gros problème pour moi, car je veux pouvoir utiliser éventuellement plusieurs fois le même code pour différents type.
    Exemple :
    - le code MAR (Maritime) pour le type TRA (Transport)
    - le code MAR (Marié) pour le type CIV (Civilité)...


    Une solution, c'est de rajouter la colonne REFERENCE.TYPE dans le Join sans préciser de colonne dans la table CLIENT :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    	@ManyToOne
    	@WhereJoinTable(clause="TYPE='" + Reference.TYPE_CIVILITE + "'")
    	@JoinColumns({@JoinColumn(name="CIVILITE", referencedColumnName="CODE"),
    				@JoinColumn(referencedColumnName="TYPE")})
    	private Reference civilite;
    Nouveau problème : JPA ajoute une colonne dans la table CLIENT et lui donne la valeur du type précisé dans la @WhereJoinTable.
    Comme dans mon entité Client, j'ai 3 relation avec l'entité Reference, JPA ajoute 3 colonnes dans la table CLIENT :
    - une colonne civilite_TYPE qui vaut toujours 'CIV'
    - une colonne sexe_TYPE qui vaut toujours 'SEX'
    - une colonne situationMatrimoniale_TYPE qui vaut toujours 'MAT'...


    Donc pour le moment, je peux utiliser ceci uniquement si je ne demande pas à JPA de créer le schéma...

  10. #10
    Membre Expert
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    2 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 2 938
    Par défaut
    Citation Envoyé par aimka Voir le message
    Pour répondre à ta qustion : si l'entité Client possède une référence vers une entité Reference, alors on peut rajouter une clause dans le WHERE sur la relation.

    @Where -> la clause concernerait une colonne de l'entité (ici Client)
    @WhereJoinTable -> la clause concerne une colonne de l'entité avec laquelle on fait une jointure (ici Reference)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    	@ManyToOne
    	@WhereJoinTable(clause="TYPE='CIV'")
    	@JoinColumns({@JoinColumn(name="CIVILITE", referencedColumnName="CODE"))
    	private Reference civilite;
    Dans mon cas, j'ai besoin d'indiquer que sur la jointure entre un Client et une Reference qui me permet de récupérer la civilité, la colonne TYPE de la table REFERENCE doit avoir la valeur 'CIV'.
    Très interessant à savoir ça, chapeau pour l'idée, j'ai appris là

  11. #11
    Nouveau membre du Club
    Inscrit en
    Décembre 2011
    Messages
    8
    Détails du profil
    Informations forums :
    Inscription : Décembre 2011
    Messages : 8
    Par défaut
    Pour résumer, cette solutionne fonctionne bien (testée sur MySQL).

    Il faut alors distinguer 2 cas :

    1. JPA créée le schéma (via hibernate.hbm2ddl.auto)

    Dans ce cas, JPA ajoute des colonnes inutiles car possédant pour chaque ligne la même valeur (celle précisée en dur dans la clause du @Where/@WhereJoinTable). A part ce détail, ça fonctionne bien.

    2. JPA ne touche pas au shéma

    Dans ce cas, ça fonctionne nickel !

    Remarque
    Si on a des contraintes si lourdes, c'est parce qu'on hérite d'un modèle existant qu'on aimerait bien modifier pour éviter les problèmes mais on en n'a pas le droit/possibilité. Donc si on doit faire avec ces obstacles, on est dans le cas 2. Et là, aucun problème

    Dans le cas 1 (on créé le schéma), on se débrouillera pour avoir un modèle plus amical et on fuira ces gros problèmes !!!



    Si j'ai le temps, je teste cette solution sur Oracle demain.
    Mais maintenant que j'ai mis ma petite synthèse, je considère que c'est résolu.

    Merci pour ton aide DevServlet !

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

Discussions similaires

  1. [Mapping] Table de liaison avec attributs
    Par JulCh dans le forum Hibernate
    Réponses: 0
    Dernier message: 06/07/2010, 09h10
  2. [AC-2003] Tables génériques et spécifiques
    Par jax54000 dans le forum Modélisation
    Réponses: 7
    Dernier message: 30/03/2010, 19h38
  3. stockage de données dans une table générique.
    Par Romers dans le forum MS SQL Server
    Réponses: 2
    Dernier message: 22/09/2009, 09h11
  4. Mapping table externe
    Par vlo59 dans le forum SQL
    Réponses: 0
    Dernier message: 15/04/2008, 10h32
  5. tables génériques prêtes à l'emploi ?
    Par patbeautifulday1 dans le forum Access
    Réponses: 3
    Dernier message: 20/07/2006, 16h28

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