Question

J'ai un mappage suivant:

<set name="People" lazy="true" table="ProjectPeople">
  <key column="ProjectId" />
  <composite-element class="PersonRole">
    <many-to-one name="Person" column="PersonId" cascade="save-update" not-null="true" />
    <many-to-one name="Role" column="RoleId" cascade="save-update" not-null="true"  />
  </composite-element>
</set>

Maintenant, je ne veux pas vraiment avoir une classe séparée pour le rôle dans le domaine, je n'ai besoin que du nom du rôle. Cependant, dans la base de données, les rôles doivent toujours être normalisés dans une table séparée Role (Id, Name).

Comment mapper le contenu de sorte que People utilise la classe PersonRole suivante?

public class PersonRole {
    public virtual Person Person { get; set; }
    public virtual string Role { get; set; }
}

Mise à jour : l'ajout d'une prime semble être une question utile non seulement pour moi.

Était-ce utile?

La solution

Personnellement, je créerais une classe de rôle comme Yassir

Mais si vous souhaitez utiliser la structure que vous avez actuellement, créez une vue contenant la clé antérieure de votre table de personnes et la description du rôle.

Modifier la table de mappage Définir pour pointer vers votre nouvelle vue Modifiez ensuite votre mappage de rôles afin qu’il s’agisse d’une propriété plutôt que du mappage plusieurs à un.

Cependant, si vous adoptez cette approche, vous ne pourrez pas mettre à jour votre rôle car il est en train de ré-émettre une vue.

Modifier: pour mettre à jour le rôle, vous pouvez ajouter <sql-insert>,<sql-update> and <sql-delete> à votre fichier de mappage afin que le processus en cascade fonctionne

.

Autres conseils

Vous n'obtiendrez pas la réponse que vous espériez, tout simplement parce que ce n'est pas possible. (N) Hibernate est un cadre de mappage relationnel-objet et prend en charge Trois types de stratégies de mappage :

  • table par hiérarchie de classe
  • table par sous-classe
  • table par classe de béton

Cela vous permet également de vous écarter de cette situation en utilisant formula ou sql-insert etc., mais comme vous l'avez découvert, ils ne vous causent que plus de douleur à la fin, ne sont pas encouragés par la communauté d'Hibernate et sont mauvais. pour la maintenabilité de votre code.

Solution?

En fait, c'est très simple. Vous ne voulez pas utiliser une classe pour Role. Je suppose que vous voulez dire que vous ne voulez pas exposer une classe de type Role et que vous ne voulez pas avoir à taper prObject.Role.Name tout le temps. Juste prObject.Role, ce qui devrait renvoyer une chaîne. Vous avez plusieurs options:

  1. Utilisez une classe interne dans, par exemple, PersonRole. Cette classe peut être interne ou privée. Ajouter une propriété Rôle qui définit et met à jour un champ membre;
  2. Utilisez une classe interne. Ajouter une propriété Rôle qui définit et met à jour un champ membre;

Examinons l'option 2:

// mapped to table Role, will not be visible to users of your DAL
// class can't be private, it's on namespace level, it can when it's an inner class
internal class Role 
{
    // typical mapping, need not be internal/protected when class is internal
    // cannot be private, because then virtual is not possible
    internal virtual int Id { get; private set; }
    internal virtual string Name { get; set; }
}

// the composite element
public class PersonRole
{
    // mapped properties public
    public virtual Person Person { get; set; }

    // mapped properties hidden
    internal virtual Role dbRole { get; set; }

    // not mapped, but convenience property in your DAL
    // for clarity, it is actually better to rename to something like RoleName
    public string Role     /* need not be virtual, but can be */
    { 
        get
        {
            return this.dbRole.Name;
        }
        set
        {
            this.dbRole.Name = value;    /* this works and triggers the cascade */
        }
    }
}

Et la cartographie peut ressembler à celle attendue. Résultat: vous n'avez pas enfreint la règle du tableau unique par classe ( EDIT: le demandeur indique qu'il veut explicitement enfreindre cette règle et Hib la prend en charge, ce qui est correct ), mais vous avez masqué les objets de la modification et de l'accès à l'aide de techniques orientées objet typiques. Toutes les fonctionnalités NH (cascade, etc.) fonctionnent toujours comme prévu.

(N) Hibernate concerne tout ce type de décision: comment créer une couche d’abstraction réfléchie et sûre dans votre base de données sans sacrifier la clarté, la brièveté ou la maintenabilité, ni enfreindre les règles OO ou ORM.

Mise à jour (après la fermeture de q.)

Voici d’autres excellentes approches que j’utilise beaucoup pour traiter ce type de problème:

  • Créez vos mappages normalement (c'est-à-dire, une classe par table, je sais que vous ne l'aimez pas, mais c'est pour le mieux) et utilisez des méthodes d'extension:

     // trivial general example
     public static string GetFullName(this Person p)
     {
         return String.Format("{0} {1}", p.FirstName, p.LastName);
     }
    
     // gettor / settor for role.name
     public static string GetRoleName(this PersonRole pr)
     {
         return pr.Role == null ? "" : pr.Role.Name;
     }
     public static SetRoleName(this PersonRole pr, string name)
     {
         pr.Role = (pr.Role ?? new Role());
         pr.Role.Name = name;
     }
    
  • Créez vos correspondances normalement, mais utilisez partial class es, qui vous permettent de & "Décorer &"; votre classe comme vous le souhaitez. L'avantage: si vous utilisez le mappage généré de vos tables, vous pouvez le régénérer aussi souvent que vous le souhaitez. Bien sûr, les classes partielles doivent être placées dans des fichiers séparés, de sorte que votre souhait de diminuer & "; Bloat &"; ce n'est probablement pas un bon scénario pour le moment.

     public partial class PersonRole
     {
         public string Role {...}
     }
    
  • Peut-être le plus simple: surcharger simplement ToString() pour String.Format, ce qui le rend approprié pour une utilisation avec Role.Name et ses amis, mais ne le rend bien sûr pas assignable. Par défaut, chaque classe d’entités ou chaque POCO devrait avoir une <=> surcharge de toute façon.

Bien qu’il soit possible de le faire directement avec NHibernate, le q. a été fermé avant que j’ai le temps de le regarder (personne n’a la faute, je n’avais tout simplement pas le temps). Je mettrai à jour si je trouve le temps de le faire grâce à la cartographie Hibernate HBM, même si je ne suis pas d'accord avec l'approche. Il n’est pas bon de se débattre avec les concepts avancés de Hib lorsque le résultat final est moins clair pour les autres programmeurs et moins clair (où est ce tableau? Pourquoi n’y at-il pas une abstraction IDao pour ce tableau? Voir aussi Meilleures pratiques de NHibernate et S # arp ). Cependant, l'exercice est quand même intéressant.

Considérant les commentaires sur les & "; meilleures pratiques &"; ": dans des situations typiques, il ne devrait pas s'agir de &"; une classe par table & ";, mais aussi d'un IDaoXXX, un DaoConcreteXXX et un GetDaoXXX pour chaque table, où vous utilisez la hiérarchie classe / interface pour différencier les tables en lecture seule et en lecture / écriture. C'estun minimum de quatre classes / lignes de code par table. Ceci est généralement généré automatiquement mais donne une couche d'accès très claire (dao) à votre couche de données (dal). La couche de données doit être gardée aussi spartiate que possible. Rien de ces & "Meilleures pratiques &"; vous empêche d'utiliser des méthodes d'extension ou des méthodes partielles pour déplacer <=> vers <=>.

Ce sont les meilleures pratiques générales . Ce n'est pas toujours possible, faisable ni même nécessaire dans certaines situations spéciales ou typiques.

Je ne pense pas qu'il soit possible de mapper plusieurs-à-un à un type primitif si je vous ajoutais une classe de rôle au modèle

C’est la plus grande déception de l’ensemble du puriste OO. Le but est sûrement d'avoir une application qui marche. Pas la version de quelqu'un d'une hiérarchie de classe parfaite. Et si vous deviez coder & Quot; prObject.Role.Name & Quot; au lieu de & "PrObject.Role &"; Comment cela vous aide-t-il à améliorer la fiabilité du programme?

Du point de vue puriste de la conception d'applications, ce que vous voulez est tout simplement faux. Une personne peut avoir plusieurs rôles, un rôle pouvant généralement être attribué à plusieurs personnes. Pourquoi faire tout ce qui est en son pouvoir pour imposer une hiérarchie irréaliste à un rôle par personne lorsque le modèle de données non retardé comprend plusieurs rôles par personne?

Si vous avez vraiment un " seulement un rôle par personne " règle alors il devrait être reflété dans le modèle de données sous-jacent.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top