-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 8 posts ] 
Author Message
 Post subject: heritage, discriminator et classes polymorphes
PostPosted: Tue Dec 06, 2005 6:37 am 
Newbie

Joined: Fri Mar 05, 2004 8:55 am
Posts: 19
Bonjour,

j'ai des objets de types réference (ce que l'on appelle parfois tables parametres ou code/libellé) qur j'aimerai mettre dans une meme table.

j'ai donc :

Code:
<class name="Reference" table="REFERENCE" mutable="false"    abstract="true" polymorphism="explicit" discriminator-value="null" >
       
   <id name="code" column="CODE"  type="string"  />
   <discriminator type="string"  column="DISC"/>
   <property name="libelle" column="LIBELLE" type="string" />       
      
   <subclass name="ModePaiement"  discriminator-value="MDP"  />
   <subclass name="Pays"  discriminator-value="PA1"  />
   .....
        et plein d'autre


Le clé primaire de ma table est donc composite (id, disc).
Par contre mon identifiant d'objet (sous classe) est seulement l'id (le discriminant correspond au type de classe).

j'ai l'erreur suivante lorsque j'essaie de lister les modes de paiement par exemple :
Code:
getCurrentSession().createCriteria(ModePaiement.class).list()


Code:
org.hibernate.WrongClassException: Object with id: 1 was not of the specified subclass: eg.ModePaiement (loaded object was of wrong class)


je m'appercoie que en base, j'ai plusieurs tuple avec le meme id (mais pas le meme discriminant). j'ai l'impression qu'il se mélange les pinceaux.....

Je constate également que ce probleme est recurrent au vu du nombre de post qui y ressemble....

Et je n'arrive pas a voir si c'est un probleme de design ou un bug ou une limite d'hibernate qui finalement n'est peut-etre pas si "intelligent" que je le croyais...

Pouvez vous m'aider?
merci
Cordialement, David


Top
 Profile  
 
 Post subject:
PostPosted: Wed Dec 07, 2005 5:09 am 
Hibernate Team
Hibernate Team

Joined: Thu Dec 18, 2003 9:55 am
Posts: 1977
Location: France
Quote:
Et je n'arrive pas a voir si c'est un probleme de design ou un bug ou une limite d'hibernate qui finalement n'est peut-etre pas si "intelligent" que je le croyais...


je pense que c'est le concepteur qui n'est pas si "intelligent" que tu ne le crois, désolé si c'est toi... sourire, ce n'est qu'une boutade.

Imagine le scénario suivant ultra simplifié: une Personne possède un Vehicule. Un Vehicule est soit une Moto, soit une Voiture.

Donc Personne--1Vehicule et Moto et Voiture héritent de Vehicule.

Maintenant, si ton id est la marque de la voiture, disons Honda, qu'il existe dans ta base aussi bien une moto Honda et une voiture Honda... problème, comment savoir si le véhicule de la personne est la moto ou la voiture...

Donc effectivement un gros problème de conception.

De manière générale, peu importe la stratégie de mapping de l'héritage, la notion de discriminant ne doit avoir aucun impact sur ta facon de raisonner, c'est une colonne technique et ne peut donc aucunement servir à identifier un objet. Elle sert uniquement à savoir quelle classe instancier, ce qui est totalement différent.

_________________
Anthony,
Get value thanks to your skills: http://www.redhat.com/certification


Top
 Profile  
 
 Post subject:
PostPosted: Wed Dec 07, 2005 9:35 am 
Newbie

Joined: Fri Mar 05, 2004 8:55 am
Posts: 19
Je suis daccord pour dire que le discriminant est technique.
j'admet donc que la clé primaire doit etre composite (puisque mon vrai probleme est la...), j'ai donc :

Code:
<class   name="Reference"  table="REF" polymorphism="explicit" >

    <composite-id>
   <key-property  name="disc" column="DISC" type="string" />
   <key-property  name="code"  column="CODE" type="string"/>
     </composite-id>

    <discriminator column="DISC" type="string"  insert="false"  />
    <property  name="libelle"    column="LIBELLE" type="string"/>          
     <subclass name="Ref1"  discriminator-value="REF1"  />
    <subclass name="Ref2"  discriminator-value="REF2" />
   
</class>



ce qui impose de declarer une propriété 'discriminant' dans la classe Reference, ce qui fais double emploi avec le type de la classe concrete...

Si je requete des objets de type Ref1, il est evident que le discriminant est 'REF1'. Il devrai etre possible de dire que le discriminant est composante de la PK.
il en resulte que les associations entre un autre objet et une reference m'obligera a declarer 2 colonnes dans ma table, ce qui est inutile car je connais le type (ici Ref1):

Code:
     
<class name="Autre"  table="AUTRE">
      <id name="id" column="ID" type="string"/>
      <many-to-one name="ref1" class="Ref1"  >
         <column name="REF1_DISC"  ></column>
         <column  name="REF1_CODE" ></column> 
      </many-to-one> 

   </class>


voila, je sais pas si c'est clair....


(question au passage: je ne comprend pas pourquoi avec une clé composite je ne paux pas declarer ma classe de base abstraite...alors qu'avec un id simple, ca passe...
Code:
org.hibernate.InstantiationException: Cannot instantiate abstract class or interface: Reference
)


merci de ta réponse,@+ David


Top
 Profile  
 
 Post subject:
PostPosted: Wed Dec 07, 2005 5:30 pm 
Newbie

Joined: Fri Mar 05, 2004 8:55 am
Posts: 19
Je suis persuadé qu'il y a un probleme de conception (moi qui voulais laisser hibernate etre intelligent a ma place)...
mais il ya autre chose qui me gène dans cette histoire.

je reprends depuis le début :


--------------------- premiere approche ---------------------------
la base :

Code:
drop table ref;
create table REF (
   DISC varchar(255) not null,
   CODE varchar(255),
   LIBELLE varchar(255),
   primary key (DISC,CODE)
)

insert into REF values('COULEUR','1','bleu');
insert into REF values('COULEUR','2','rouge');
insert into REF values('MARQUE','1','HONDA');
insert into REF values('MARQUE','2','PIGEOT');

drop table VOITURE;
create table VOITURE (
   ID varchar(255) not null,
   COULEUR_CODE varchar(255),
   MARQUE_CODE varchar(255),
   primary key (ID)
)
insert into VOITURE values( '1','2','2');
insert into VOITURE values( '2','1','1');


le HBM:

Code:
<class abstract="true" name="Reference" table="REF" polymorphism="explicit" discriminator-value="null">
      <id name="code" column="CODE" type="string"  />    
      <discriminator column="DISC" type="string" />
      <property  name="libelle"    column="LIBELLE" type="string"/>          
      <subclass name="Couleur"  discriminator-value="COULEUR"  />
      <subclass name="Marque"  discriminator-value="MARQUE" />
   </class>
   <class name="Voiture"  table="VOITURE">
      <id name="id" column="ID" type="string"/>
      <many-to-one name="couleur"  column="COULEUR_CODE" class="Couleur"/>
      <many-to-one name="marque" column="MARQUE_CODE" class="Marque" /> 
   </class>


evidement les contraintes SGBD ne sont pas respectées, mais peu importe, ceci me parait logique...

le problème:

Code:
System.out.println("1>>les couleurs :"+sf.getCurrentSession().createCriteria(Couleur.class).list());
       System.out.println("2>>les marques :"+sf.getCurrentSession().createCriteria(Marque.class).list());


donne


Code:
       org.hibernate.WrongClassException: Object with id: 1 was not of the specified subclass: Marque (loaded object was of wrong class)
         at org.hibernate.loader.Loader.instanceAlreadyLoaded(Loader.java:1235)
         at org.hibernate.loader.Loader.getRow(Loader.java:1186)
         at org.hibernate.loader.Loader.getRowFromResultSet(Loader.java:568)
         at org.hibernate.loader.Loader.doQuery(Loader.java:689)
         at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:223)
         at org.hibernate.loader.Loader.doList(Loader.java:2147)
         at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2026)
         at org.hibernate.loader.Loader.list(Loader.java:2021)
         at org.hibernate.loader.criteria.CriteriaLoader.list(CriteriaLoader.java:94)   
       


si CODE de la table reference est unique, tout est ok.
Mais CODE est seulement unique pour un discriminant donné (composite).
Je m'etais imaginé que hibernate prennait en compte ceci...que le discriminant permettait de travailler
avec un sous ensemble d'une table de maniere transparente.

--------------------- une solution avec pk composite ---------------------------
la base :

Code:
drop table ref;
create table REF (
   DISC varchar(255) not null,
   CODE varchar(255),
   LIBELLE varchar(255),
   primary key (DISC,CODE)
)

insert into REF values('COULEUR','1','bleu');
insert into REF values('COULEUR','2','rouge');
insert into REF values('MARQUE','1','HONDA');
insert into REF values('MARQUE','2','PIGEOT');

drop table VOITURE;
create table VOITURE (
   ID varchar(255) not null,
   COULEUR_DISC varchar(255),
   COULEUR_CODE varchar(255),
   MARQUE_DISC varchar(255),
   MARQUE_CODE varchar(255),
   primary key (ID)
)
insert into VOITURE values( '1','COULEUR','2','MARQUE','2');
insert into VOITURE values( '2','COULEUR','1','MARQUE','1');


le HBM:

Code:
   <class name="Reference" table="REF" polymorphism="explicit" discriminator-value="null">
      <composite-id>
         <key-property  name="disc" column="DISC" type="string" />
         <key-property  name="code"  column="CODE" type="string" />
      </composite-id>      
      <discriminator column="DISC" type="string"  insert="false"  />
      <property  name="libelle"    column="LIBELLE" type="string"/>          
      <subclass name="Couleur"  discriminator-value="COULEUR"  />
      <subclass name="Marque"  discriminator-value="MARQUE" />
   </class>
   <class name="Voiture"  table="VOITURE">
      <id name="id" column="ID" type="string"/>
      <many-to-one name="couleur" class="Couleur"  >
         <column name="COULEUR_DISC"  ></column>
         <column  name="COULEUR_CODE" ></column> 
      </many-to-one>
      <many-to-one name="marque" class="Marque"  >
         <column name="MARQUE_DISC"  ></column>
         <column  name="MARQUE_CODE" ></column> 
      </many-to-one>   
   </class>


le problème:

ceci est ok
Code:
System.out.println("1>>les couleurs :"+sf.getCurrentSession().createCriteria(Couleur.class).list());
       System.out.println("2>>les marques :"+sf.getCurrentSession().createCriteria(Marque.class).list());
       System.out.println(">>la voiture 1 :"+sf.getCurrentSession().get(Voiture.class,"1"));


mais session.get() et session.load() sont inutilisables sans créer une classe interne id(disc, code), deplus il est
hors de question de gerer les discriminant a ce niveau...
il reste quand meme la solution :

Code:
System.out.println(">>la marque 1 :"+sf.getCurrentSession().createCriteria(Marque.class).add(Restrictions.eq("code","1")).list());
       System.out.println(">>la couleur 1 :"+sf.getCurrentSession().createCriteria(Couleur.class).add(Restrictions.eq("code","1")).list());


la création d'une colonne suplementaire pour chaque association me parait ici superflu, car le type est connu...


--------------------- solution sans heritage ---------------------------
la base :

Code:
drop table ref;
create table REF (
   DISC varchar(255) not null,
   CODE varchar(255),
   LIBELLE varchar(255),
   primary key (DISC,CODE)
)

insert into REF values('COULEUR','1','bleu');
insert into REF values('COULEUR','2','rouge');
insert into REF values('MARQUE','1','HONDA');
insert into REF values('MARQUE','2','PIGEOT');
insert into REF values('OPTION','1','CLIM');
insert into REF values('OPTION','2','ABS');
insert into REF values('OPTION','3','VOLANTMOUMOUT');

drop table VOITURE;
create table VOITURE (
   ID varchar(255) not null, 
   COULEUR_CODE varchar(255),
   MARQUE_CODE varchar(255),
   primary key (ID)
)
insert into VOITURE values( '1', '2', '2');
insert into VOITURE values( '2', '1', '1');

drop table VOITURE_OPTIONS;
create table VOITURE_OPTIONS (
   VOITURE_ID varchar(255), 
   OPTION_CODE varchar(255),
   primary key (VOITURE_ID, OPTION_CODE)
)
insert into VOITURE_OPTIONS values( '1', '1');
insert into VOITURE_OPTIONS values( '1', '2');
insert into VOITURE_OPTIONS values( '2', '3');
insert into VOITURE_OPTIONS values( '2', '2');   


le HBM:

Code:
<class name="Couleur"  table="REF"  where="DISC='COULEUR'" >
      <id name="code"  column="CODE" type="string" />
      <property  name="libelle"    column="LIBELLE" type="string"/>    
   </class>
   <class name="Marque"  table="REF"  where="DISC='MARQUE'" >
      <id name="code"  column="CODE" type="string" />
      <property  name="libelle"    column="LIBELLE" type="string"/>    
   </class>
   <class name="Option"  table="REF"  where="DISC='OPTION'" >
      <id name="code"  column="CODE" type="string" />
      <property  name="libelle"    column="LIBELLE" type="string"/>    
   </class>
            
   <class name="Voiture"  table="VOITURE">
      <id name="id" column="ID" type="string"/>
      <many-to-one name="couleur" class="Couleur"  column="COULEUR_CODE" />
      <many-to-one name="marque" class="Marque" column="MARQUE_CODE" />   
      <set name="options" table="VOITURE_OPTIONS"  >
         <key column="VOITURE_ID"/>
         <many-to-many  unique="true" class="Option" column="OPTION_CODE" where="DISC='OPTION'"/>
      </set>
   </class>



pas de problème:

les classes heritent de reference et tout me parait ok avec cette solution.
Il y a t-il une contre indication avec cette approche?



des conseils ?

merci, David


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jan 30, 2006 10:05 am 
Beginner
Beginner

Joined: Mon Nov 24, 2003 5:39 am
Posts: 26
Location: France
Est-ce qu'il y a des recommendations pour ce problème. Il me paraît évident que Hibernate devrait inclure le dicriminator-value.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jan 30, 2006 12:33 pm 
Newbie

Joined: Fri Mar 05, 2004 8:55 am
Posts: 19
thomasvdv wrote:
Est-ce qu'il y a des recommendations pour ce problème. Il me paraît évident que Hibernate devrait inclure le dicriminator-value.



j'ai finalement utilisé la solution 3 sans heritage (dans les mappings hibernate), ce qui permet d'avoir un espace de nom dans le cache pour chaque sous classe (hibernate ne mélange donc pas les instances en mémoire).
j'ai quand meme garder l'heritage dans mes classe JAVA :

Code:
public  abstract class Reference  {

   private String libelle;
   private String code;
...
}


comme classe de base et
Code:
public class UneRef extends Reference{
//rien a declarer

}


ca marche pour mon besoin d'avoir plein de classes reference mappées dans une meme table...


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jan 30, 2006 12:36 pm 
Beginner
Beginner

Joined: Mon Nov 24, 2003 5:39 am
Posts: 26
Location: France
Est-ce que vous avez trouvé un moyen de limiter la duplication de configuration?


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jan 30, 2006 12:53 pm 
Newbie

Joined: Fri Mar 05, 2004 8:55 am
Posts: 19
thomasvdv wrote:
Est-ce que vous avez trouvé un moyen de limiter la duplication de configuration?


non, yen a pas.j'utilise le where a la place du discriminator.
chaque classe doit donc etre déclarée avec les colonnes du parent:


Code:
<class name="Marque"  table="REF"  where="DISC='MARQUE'" >
      <id name="code"  column="CODE" type="string" />
      <property  name="libelle"    column="LIBELLE" type="string"/>   
   </class>


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 8 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.