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


      内容摘要

        单向多对多关联映射

        双向多对多关联映射

      一、单向多对多关联映射

      1.1 多对多关联映射描述

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

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

     

    图1.1.1

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

      让我们看一下代码:

      

        public class Role
        {
            
    public virtual int? ID { getset; }

            
    public virtual string Name { getset; }
        }

        
    public class User
        {
            
    public virtual int? ID { getset; }

            
    public virtual string Name { getset; }

            
    public virtual IList<Role> Roles { getset; }
        }

      映射文件:

    <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 { getset; }

            
    public virtual string Name { getset; }

            
    public virtual IList<User> Users { getset; }
        }

      

    <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   

      代码下载

      出处:http://www.cnblogs.com/GoodHelper/archive/2011/03/04/nhibernate10.html

      欢迎转载,但需保留版权。

  • 相关阅读:
    wap2app(五)-- 微信授权登录以及踩过的坑
    wap2app(六)-- wap2app的原生标题头无法隐藏
    创建对象的几种模式
    通过url动态获取图片大小方法总结
    wap2app(三)-- 添加引导页
    wap2app(二)-- 设置APP系统状态栏
    wap2app(一)-- 网站快速打包成app
    树叶飘落、雪花飘落等同时多个图片飘落
    基于bootstrap的双日历插件 daterangepicker
    FileProvider解决FileUriExposedException
  • 原文地址:https://www.cnblogs.com/GoodHelper/p/nhibernate10.html
Copyright © 2020-2023  润新知