NHibernate从入门到精通系列(10)——多对多关联映射

 内容摘要

    单向多对多关联映射

    双向多对多关联映射

 

  一、单向多对多关联映射

  1.1 多对多关联映射描述

  众所周知,持久化类的有三种对应关系:“一对一”、“一对多(多对一)”和“多对多”。在项目开发过程中,我们接触过权限系统。而权限系统中,“用户”和“角色”的对应关系往往就是“多对多”。

  让我们看一下“多对多”的数据,如图1.1.1所示:

 

图1.1.1

 

 

  从数据中,我们能够观察到:多个用户(User)对应多个角色(Role)。构造“多对多”的关联关系,我们不仅需要用户(User)表和角色(Role)表,还需要用户和角色的关系表。通过这三张表的关系构造了“多对多”的关联关系。

 

  让我们看一下代码:

public class Role
    {
        public virtual int? ID { get; set; }

        public virtual string Name { get; set; }
    }

    public class User
    {
        public virtual int? ID { get; set; }

        public virtual string Name { get; set; }

        public virtual IList<Role> Roles { get; set; }
    }

 映射文件:

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Domain" namespace="Domain">
  <class name="Role" table="T_Role" lazy="true" >
    <id name="ID" type="int" column="RoleID">
      <generator class="native"/>
    </id>

    <property name="Name" type="string">
      <column name="Name" length="50"/>
    </property>

  </class>
</hibernate-mapping>


<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Domain" namespace="Domain">
  <class name="User" table="T_User" lazy="true" >
    <id name="ID" type="int" column="UserID">
      <generator class="native"/>
    </id>
  
    <property name="Name" type="string">
      <column name="Name" length="50"/>
    </property>

    <bag name="Roles" table="T_User_Role">
      <key column="UserID"/>
      <many-to-many class="Role" column="RoleID"/>
    </bag>
    
  </class>
</hibernate-mapping>

在用户(User)类中有与角色(Role)对应的IList<Role>属性Roles,在映射文件中我们用<bag>和<many-to-many>标签描述“多对多”关联映射。

    其中,<bag>标签中的“table”属性是设置“多对多”映射的第三方的关系表。注意的是“T_User_Role”这个第三方关系表不在持久化类的结构中体现。<key>标签的“column”属性用于指定主键表的主键,<many-to-many>标签的“column”属性用于指定外键表的关联字段。

 

  1.2 单向多对多关联映射的数据插入

  我们编写单元测试类的代码:

[TestFixture]
    public class ManyToManyTest
    {
        private ISessionFactory sessionFactory;

        public ManyToManyTest()
        {
            log4net.Config.XmlConfigurator.Configure();
        }

        [SetUp]
        public void Init()
        {
            var cfg = new NHibernate.Cfg.Configuration().Configure("Config/hibernate.cfg.xml");
            sessionFactory = cfg.BuildSessionFactory();
        }

        [Test]
        public void SaveTest()
        {
            using (ISession session = this.sessionFactory.OpenSession())
            {
                var role1 = new Role { Name = "库管" };
                var role2 = new Role { Name = "出纳" };
                var role3 = new Role { Name = "会计" };

                var liu = new User
                {
                    Name = "刘冬",
                    Roles = new List<Role> { role1, role2 }
                };
                var zhang = new User 
                {
                    Name = "张三" ,
                    Roles = new List<Role> { role2, role3 }
                };

                ITransaction tran = session.BeginTransaction();
                try
                {
                    session.Save(role1);
                    session.Save(role2);
                    session.Save(role3);

                    session.Save(liu);
                    session.Save(zhang);

                    tran.Commit();
                }
                catch(Exception ex) 
                {
                    tran.Rollback();
                    throw ex;
                }
            }
        }
}

运行效果如图1.2.1所示,运行成功。

图1.2.1   

 

  首先生成了主表的“insert into”语句,其次生成关系表的“insert into”语句。

 

  二、双向多对多关联映射

     2.1 双向多对多关联映射的描述

     实现双向多对多关联映射需要修改一下代码和映射文件:


public class Role
    {
        public virtual int? ID { get; set; }

        public virtual string Name { get; set; }

        public virtual IList<User> Users { get; set; }
    }

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Domain" namespace="Domain">
  <class name="Role" table="T_Role" lazy="true" >
    <id name="ID" type="int" column="RoleID">
      <generator class="native"/>
    </id>

    <property name="Name" type="string">
      <column name="Name" length="50"/>
    </property>

    <bag name="Users" table="T_User_Role">
      <key column="RoleID"/>
      <many-to-many class="User" column="UserID"/>
    </bag>

  </class>
</hibernate-mapping>

 在角色(Role)类中建立用户(User)的IList<User>属性“Users”。这样,多个角色(Role)对应多个用户(User),多个用户(User)也对应多个角色(Role)。

 

  2.2 双向多对多关联映射的数据插入

  编写单元测试代码:


[Test]
        public void SaveRoleTest()
        {
            using (ISession session = this.sessionFactory.OpenSession())
            {
                var user = new User
                {
                    Name = "刘冬"
                };

                var role = new Role
                {
                    Name = "系统管理员",
                    Users = new List<User> { user }
                };
 
                ITransaction tran = session.BeginTransaction();
                try
                {
                    session.Save(user);
                    session.Save(role);

                    tran.Commit();
                }
                catch (Exception ex)
                {
                    tran.Rollback();
                    throw ex;
                }
            }
        }

运行效果如图2.2.1所示,运行成功

图2.2.1  

   

   设置cascade属性为all,用于从一端保存数据。


 <bag name="Roles" table="T_User_Role" cascade="all">
      <key column="UserID"/>
      <many-to-many class="Role" column="RoleID"/>
    </bag>


[Test]
        public void SaveUserTest()
        {
            using (ISession session = this.sessionFactory.OpenSession())
            {
                var role = new Role
                {
                    Name = "系统管理员",
                };

                var user = new User
                {
                    Name = "刘冬",
                    Roles = new List<Role> { role }
                };

                ITransaction tran = session.BeginTransaction();
                try
                {
                    session.Save(user);

                    tran.Commit();
                }
                catch (Exception ex)
                {
                    tran.Rollback();
                    throw ex;
                }
            }
        }

运行效果如图2.2.2所示,运行成功。

图2.2.2  

 

 

  我们设置<bag>标签的inverse为true:

 <bag name="Roles" table="T_User_Role" inverse="true" cascade="all">
      <key column="UserID"/>
      <many-to-many class="Role" column="RoleID"/>
    </bag>

运行效果如图2.2.3所示,虽然运行成功,但没有将数据插入关系表“T_User_Role”。

图2.2.3

 

  这是为什么呢?因为“inverse”属性的意思是反转,就是将操作权交给关联关系的另一点,“多对多”关联映射的另一端始终是它们的“关系表”。正因为“关系表”不在持久化类中体现,所以在“多对多”关联映射中设置“inverse”属性是没有意义的。

 

  2.3 双向多对多关联映射的数据更新

  我们编写单元测试的代码:


[Test]
        public void UpdateUserTest()
        {
            using (ISession session = this.sessionFactory.OpenSession())
            {

                var role = new Role
                {
                    Name = "系统管理员",
                };

                var user = session.Get<User>(1);
                user.Roles.Clear();
                user.Roles.Add(role);

                ITransaction tran = session.BeginTransaction();
                try
                {
                    session.Update(user);

                    tran.Commit();
                }
                catch (Exception ex)
                {
                    tran.Rollback();
                    throw ex;
                }
            }
        }

运行效果如图2.3.1所示,运行成功。生成了“delete”语句,删除了原有集合的数据,然后插入了新的集合数据。

图2.3.1

 

 

  2.4 双向多对多关联映射的数据查询

  编写单元测试类代码:

[Test]
        public void SelectTest()
        {
            using (ISession session = this.sessionFactory.OpenSession())
            {
                var user = session.Get<User>(1);

                foreach (var item in user.Roles)
                {
                    Console.WriteLine("角色名称:{0}", item.Name);
                    foreach (var us in item.Users)
                    {
                        Console.WriteLine("用户名称:{0}", us.Name);
                    }
                }
            }
        }

 运行效果如图2.4.1所示,运行成功。

图2.4.1   


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值