Mappages NHibernate lorsque des relations de jointure automatique ont des propriétés supplémentaires

StackOverflow https://stackoverflow.com/questions/606330

Question

Comment mappez-vous une classe à d'autres instances de la même classe lorsque cette relation a elle-même des propriétés ?

J'ai une classe appelée Person qui est mappée sur une table Person

PersonID   PersonName    PersonAge 
----------------------------------
       1   Dave Dee             55
       2   Dozy                 52
       3   Beaky                45
       4   Mick                 55
       5   Tich                 58

Je souhaite une relation plusieurs à plusieurs entre Personne à l'aide d'une table de jointure appelée PersonPerson:

 PersonPersonID  PersonID  RelatedPersonID RelationshipID 
 --------------------------------------------------------
              1         1                5              1
              2         3                4              2
              3         2                1              3

Je souhaite les attributs suivants dans la table PersonPerson:

RelationshipID  RelationshipName
--------------------------------
             1  Colleague
             2  Manager
             3  Tutor

Cette question et les liens associés -to de Billy McCafferty explique que la relation PersonPerson doit être promue d'une JOIN normale à une entité à part entière en raison des colonnes supplémentaires de la table PersonPerson. Cependant, cela n'explique pas ce qu'il faut faire quand il s'agit d'une auto-jointure. La différence est que si je demande toutes les personnes liées à Dave Dee (ID = 1), non seulement je devrais obtenir Tich (ID = 5), mais aussi get obtient également Dozy (ID = 2), car Dave Dee figure également dans la colonne RelatedPersonID.

Ce que ma solution est jusqu'ici, est d'avoir deux propriétés dans ma classe Person.

public virtual IList<PersonPerson> PersonPersonForward {get;set;}
public virtual IList<PersonPerson> PersonPersonBack {get;set;}

private List<PersonPerson> personPersonAll;
public virtual List<PersonPerson> PersonPersonAll 
{
   get
   {
       personPersonAll = new List<PersonPerson>(PersonPersonForward);
       personPersonAll.AddRange(PersonPersonBack);
       return personPersonAll;
   }
}

Et avoir les éléments suivants dans le hbm:

 <bag name="PersonPersonForward" table="PersonPerson" cascade="all">
      <key column="PersonID"/>
      <one-to-many class="PersonPerson" />
 </bag>

 <bag name="PersonPersonBack" table="PersonPerson" cascade="all">
      <key column="RelatedPersonID"/>
      <one-to-many class="PersonPerson" />
 </bag>

Cela semble un peu maladroit et inélégant. NHibernate propose généralement des solutions élégantes à la plupart des problèmes quotidiens. Ce qui précède est-il la manière sensée de le faire ou existe-t-il un meilleur moyen?

Était-ce utile?

La solution

Je pense que je le ferais aussi comme ça, mais je pense que c'est un peu "maladroit" de le modéliser comme ça. Je veux dire: vous avez une collection de personnes avec laquelle une certaine personne est liée, mais vous avez aussi une «relation de dos».
Est-ce vraiment nécessaire? N'est-ce pas une option de supprimer cette collection d'arrière-plan et de spécifier plutôt une méthode sur le référentiel PersonRepository qui peut vous rendre toutes les personnes en relation avec une sorte de relation avec une personne donnée?

Hmm, cela peut sembler un peu obscur, alors voici un code (notez que par souci de brièveté, j’ai oublié les modificateurs 'virtuels', etc ... (je préfère aussi ne pas avoir ces modificateurs, Ainsi, dans 99% des cas, je spécifie "lazy = false" lors de la cartographie de mes classes).

public class Person
{
    public int Id {get; set;}
    public string Name {get; set;}

    public IList<PersonPerson> _relatedPersons;

    public ReadOnlyCollection<PersonPerson> RelatedPersons
    {
        get
        {
           // The RelatedPersons property is mapped with NHibernate, but
           // using its backed field _relatedPersons (can be done using the 
           // access attrib in the HBM.
           // I prefer to expose the collection itself as a readonlycollection
           // to the client, so that RelatedPersons have to be added through
           // the AddRelatedPerson method (and removed via a RemoveRelatedPerson method).

           return new List<PersonPerson) (_relatedPersons).AsReadOnly();
        }
    }

    public void AddRelatedPerson( Person p, RelationType relatesAs )
    {
       ...
    }

}

Comme vous pouvez le constater, il ne reste plus qu’une collection dans la classe Personne, à savoir une collection d’objets PersonPerson qui représente les relations que cette personne a. Pour obtenir les personnes qui ont des relations avec une personne donnée, vous pouvez créer une méthode spécifique sur votre compte PersonRepository qui renvoie ces personnes au lieu de les avoir dans une collection de la classe Personne. Je pense que cela améliorera également les performances.

public class NHPersonRepository : IPersonRepository
{
    ...

    public IList<Person> FindPersonsThatHaveARelationShipWithPerson( Person p )
    {
        ICriteria crit = _session.CreateCriteria <Person>();

        crit.AddAlias ("RelatedPersons", "r");

        crit.Add (Expression.Eq ("r.RelatedWithPerson", p));

        return crit.List();

    }
}

La référence arrière n'est pas un membre de la classe Person; il doit être accessible via le référentiel. C’est aussi ce que dit Eric Evans dans son livre DDD: dans certains cas, il est préférable d’avoir une méthode spécialisée sur le référentiel qui puisse vous donner accès à des objets liés, au lieu de les avoir (= les objets liés) à transporter. avec l'objet lui-même.

Je n’ai pas testé le code, je l’ai simplement saisi ici, aussi je n’ai pas vérifié les erreurs de syntaxe, etc., mais je pense que cela devrait clarifier un peu ma façon de voir les choses.

Autres conseils

Il me semble que vous avez essentiellement construit un modèle de graphe dirigé , et les deux mappages PersonPersonForward et PersonPersonBack représentent respectivement les fronts sortant et entrant.

Cette direction est renforcée par la sémantique de vos types de relation: tandis que est un / une collègue est très probablement un relation symétrique , est un gestionnaire de et est un tuteur de sont presque définitivement asymétriques .

Je pense que dans ce cas, le modèle de données essaie de vous dire que les deux ensembles de liens, bien que de type compatible, ne sont pas la même chose dans le contexte.

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