NHibernate多对多的关联,通过使用域模型中的关系实体,将两端作为父项 [英] NHibernate many-to-many assocations making both ends as a parent by using a relationship entity in the Domain Model

查看:151
本文介绍了NHibernate多对多的关联,通过使用域模型中的关系实体,将两端作为父项的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

实体:
团队< - > TeamEmployee< - >员工< b>

要求:


  • 一个团队和一个员工可以存在,而不存在其对应关系。
  • > [稍后使用TeamRepository]。
  • 在Employee-TeamEmployee关系中,Employee负责(父) [稍后使用EmployeeRepository]。
  • 不允许重复。

  • 如果员工不在其他团队中,则删除团队将删除团队中的所有员工。



映射:

  • 删除员工只会删除一个团队(如果团队不包含更多员工)

      public class TeamMap:ClassMap< Team> 
    公共TeamMap()
    {
    //身份映射
    Id(p => p.Id)
    .Column(TeamID )
    .GeneratedBy.Identity();

    //列映射
    映射(p => p.Name);

    $ association
    HasMany(p => p.TeamEmployees)
    .KeyColumn(TeamID)
    .Inverse()
    .Cascade .SaveUpdate()
    .AsSet()
    .LazyLoad();
    }
    }

    public class EmployeeMap:ClassMap< Employee>
    公共EmployeeMap()
    {
    //标识符映射
    Id(p => p.Id)
    .Column(EmployeeID )
    .GeneratedBy.Identity();

    //列映射
    映射(p => p.EMail);
    Map(p => p.LastName);
    Map(p => p.FirstName);

    $ associations
    HasMany(p => p.TeamEmployees)
    .Inverse()
    .Cascade.SaveUpdate()
    .KeyColumn EmployeeID)
    .AsSet()
    .LazyLoad();

    HasMany(p => p.LoanedItems)
    .Cascade.SaveUpdate()
    .LazyLoad()
    .KeyColumn(EmployeeID);
    }
    }

    public class TeamEmployeeMap:ClassMap< TeamEmployee>
    {
    public TeamEmployeeMap()
    {
    Id(p => p.Id);

    引用(p => p.Employee)
    .Column(EmployeeID)
    .LazyLoad();

    参考资料(p => p.Team)
    .Column(TeamID)
    .LazyLoad();




    $ b

    创建员工和团队:

      var employee1 = new Employee {EMail =Mail,FirstName =Firstname,LastName =Lastname}; 
    var team1 = new Team {Name =Team1};
    var team2 = new Team {Name =Team2};

    employee1.AddTeam(team1);
    employee1.AddTeam(team2);

    $ b var employee2 = new Employee {EMail =Mail2,FirstName =Firstname2,LastName =Lastname2};
    var team3 = new Team {Name =Team3};

    employee2.AddTeam(team3);
    employee2.AddTeam(team1);

    team1.AddEmployee(employee1);
    team1.AddEmployee(employee2);
    team2.AddEmployee(employee1);
    team3.AddEmployee(employee2);

    session.SaveOrUpdate(team1);
    session.SaveOrUpdate(team2);
    session.SaveOrUpdate(team3);

    session.SaveOrUpdate(employee1);
    session.SaveOrUpdate(employee2);

    在此之后,我使用transaction.Commit()提交更改。
    第一个奇怪的是,我必须保存团队和员工,而不是其中的一个(为什么?!)。如果我只保存所有团队或(XOR)所有员工,那么我得到一个 TransientObjectException
    $ b


    对象引用未保存的
    暂时实例 - 在刷新之前保存
    暂存实例
    类型:Core.Domain.Model.Employee,
    实体:Core.Domain.Model.Employee p>

    当我保存所有创建的团队和员工时,一切都可以节省,但是关系表TeamEmployee有重复关系

      ID EID TID 
    1 1 1
    2 2 1
    3 1 2
    4 2 3
    5 1 1
    6 1 2
    7 2 3
    8 2 1

    所以不是4个关系就有8个关系。左边4个关系,右边4个关系。 :

    我错了什么?



    更多问题:当我删除团队或员工时,我必须从对象模型中的TeamEmployee列表中删除团队或员工,或者NHibernate是否为我(使用session.delete(..))做这个工作?

    解决方案

    您正在谈论业务逻辑。这不是NHibernate实现业务逻辑的目的。



    您的代码在做什么:

    您映射了 TeamEmployee 的两个不同集合,一个位于 Team 中,一个位于雇员。在您的代码中,您将项目添加到两个集合,每次创建 TeamEmployee 的新实例。那么你为什么期望NHibernate不应该存储所有这些不同的实例?

    你可以做些什么来解决它:



    您使 TeamEmployee 一个实体(与一个值类型相反)。为了只创建一次实例,你必须在内存中只实例化一次,并在两个集合中重用它。只有在你的领域模型中真正需要这个类的时候才能做到这一点。 (例如,因为它包含关于关系的附加信息,实际上是它自己的一个实体)。

    如果你不需要这个类,将其映射为多对多关系(如已经由 Chris Conway )。因为在内存中有两个包含相同数据的集合,所以当存储时,告诉NHibernate忽略其中的一个,使用 Inverse



    父母双方问题



    父母两端都没有 。我认为很明显,无论是团队还是员工都不是对方的父母,他们是独立的。您可能表示他们都是中间 TeamEmployee 的父项。他们不能是同一个实例的父母(也就是拥有者)。它们中的任何一个都是父级,或者是另一个独立的实例,这使得管理变得复杂得多(现在就是这样实现的)。如果你把它映射成多对多的关系,它将由NHibernate进行管理。



    完成你的业务逻辑:存储新团队和新员工




    • 管理关系并保持同步
    • >
    • 删除团队和员工,当他们不再使用。 (在NHibernate中没有明确的持久性垃圾回收实现,原因如下)。

      Entities: Team <-> TeamEmployee <-> Employee

      Requirements:

      • A Team and an Employee can exist without its counterpart.
      • In the Team-TeamEmployee relation the Team is responsible (parent) [using later a TeamRepository].
      • In the Employee-TeamEmployee relation the Employee is responsible (parent) [using later an EmployeeRepository].
      • Duplicates are not allowed.
      • Deleting a Team deletes all Employees in the Team, if the Employee is not in another Team.
      • Deleting an Employee deletes only a Team, if the Team does not contain no more Employees.

      Mapping:

      public class TeamMap : ClassMap<Team>
      {
          public TeamMap()
          {
              // identity mapping
              Id(p => p.Id)
                  .Column("TeamID")
                  .GeneratedBy.Identity();
      
              // column mapping
              Map(p => p.Name);
      
              // associations
              HasMany(p => p.TeamEmployees)
                  .KeyColumn("TeamID")
                  .Inverse()
                  .Cascade.SaveUpdate()
                  .AsSet()
                  .LazyLoad();
          }
      }
      
      public class EmployeeMap : ClassMap<Employee>
      {
          public EmployeeMap()
          {
              // identifier mapping
              Id(p => p.Id)
                  .Column("EmployeeID")
                  .GeneratedBy.Identity();
      
              // column mapping
              Map(p => p.EMail);
              Map(p => p.LastName);
              Map(p => p.FirstName);
      
              // associations
              HasMany(p => p.TeamEmployees)
                  .Inverse()
                  .Cascade.SaveUpdate()
                  .KeyColumn("EmployeeID")
                  .AsSet()
                  .LazyLoad();
      
              HasMany(p => p.LoanedItems)
                  .Cascade.SaveUpdate()
                  .LazyLoad()
                  .KeyColumn("EmployeeID");
          }
      }
      
      public class TeamEmployeeMap : ClassMap<TeamEmployee>
      {
          public TeamEmployeeMap()
          {
              Id(p => p.Id);
      
              References(p => p.Employee)
                  .Column("EmployeeID")
                  .LazyLoad();
      
              References(p => p.Team)
                  .Column("TeamID")
                  .LazyLoad();
          }
      }
      

      Creating Employees and Teams:

          var employee1 = new Employee { EMail = "Mail", FirstName = "Firstname", LastName = "Lastname" };
          var team1 = new Team { Name = "Team1" };
          var team2 = new Team { Name = "Team2" };
      
          employee1.AddTeam(team1);
          employee1.AddTeam(team2);
      
      
          var employee2 = new Employee { EMail = "Mail2", FirstName = "Firstname2", LastName = "Lastname2" };
          var team3 = new Team { Name = "Team3" };
      
          employee2.AddTeam(team3);
          employee2.AddTeam(team1);
      
          team1.AddEmployee(employee1);
          team1.AddEmployee(employee2);
          team2.AddEmployee(employee1);
          team3.AddEmployee(employee2);
      
          session.SaveOrUpdate(team1);
          session.SaveOrUpdate(team2);
          session.SaveOrUpdate(team3);
      
          session.SaveOrUpdate(employee1);
          session.SaveOrUpdate(employee2);
      

      After this I commit the changes by using transaction.Commit(). The first strange thing is that I have to save Teams and Employees instead only one of them (why?!). If I only save all teams or (Xor) all employees then I get a TransientObjectException:

      "object references an unsaved transient instance - save the transient instance before flushing. Type: Core.Domain.Model.Employee, Entity: Core.Domain.Model.Employee"

      When I save all created Teams and Employees everything saves fine, BUT the relation table TeamEmployee has duplicate assoications.

      ID EID TID
      1  1   1
      2  2   1
      3  1   2
      4  2   3
      5  1   1
      6  1   2
      7  2   3
      8  2   1
      

      So instead of 4 relations there are 8 relations. 4 relations for the left side and 4 relations for the right side. :[

      What do I wrong?

      Further questions: When I delete a Team or an Employee, do I have to remove the team or the Employee from the TeamEmployee list in the object model or does NHibernate make the job for me (using session.delete(..))?

      解决方案

      You are talking about business logic. It's not the purpose of NHibernate to implement the business logic.

      What your code is doing:

      You mapped two different collections of TeamEmployees, one in Team, one in Employee. In your code, you add items to both collections, creating new instances of TeamEmployee each time. So why do you expect that NHibernate should not store all these distinct instances?

      What you could do to fix it:

      You made TeamEmployee an entity (in contrast to a value type). To create an instance only once, you would have to instantiate it only once in memory and reuse it in both collections. Only do this when you really need this class in your domain model. (eg. because it contains additional information about the relations and is actually an entity of its own.)

      If you don't need the class, it is much easier to map it as a many-to-many relation (as already proposed by Chris Conway). Because there are two collections in memory which are expected to contain the same data, you tell NHibernate to ignore one of them when storing, using Inverse.

      The parent on both ends problem

      There is no parent on both ends. I think it's clear that neither the Team nor the Employee is a parent of the other, they are independent. You probably mean that they are both parents of the intermediate TeamEmployee. They can't be parent (and therefore owner) of the same instance. Either one of them is the parent, or it is another independent instance, which makes managing it much more complicated (this is how you implemented it now). If you map it as a many-to-many relation, it will be managed by NHibernate.

      To be done by your business logic:

      • storing new Teams and new Employees
      • managing the relations and keeping them in sync
      • deleting Teams and Employees when they are not used anymore. (There is explicitly no persistent garbage collection implementation in NHibernate, for several reasons.)

      这篇关于NHibernate多对多的关联,通过使用域模型中的关系实体,将两端作为父项的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆