NHibernate经验之谈:Inverse与Cascade


inverse 与Cascade分别表示NHibernate中类之间关系、级联操作。他们之间不同值以及不一样的组合,很多时候都影响着业务逻辑的执行。其实inverse与Cascade又是不一样的。理解它们的作用对使用NHibernate进行开发也是比较重要的。
inverse:意思是反转,它指明类之间的关系由谁来进行维护。例如:班级(Class类)与学生(Student),班级-学生就是one-to-many,学生-班级就是many-to-one。它只能在one-

to-many中many的一方进行设置(当然,在many一方,可能是在<set>也可能是在<bag>也可能是在<array>中进行设置),而对于one方,是不进行Inverse设置的。在Hibernate社区,inverse默认值为false。

Cascade:则表示一种级联操作,它的值有none\save-update\delete\all\all-delete-ophan,它主要作用是在父类子类之间进行操作时,哪些操作会对父类(如班级Class类)、子类(Student)有影响。在Hibernate社区,Cascade默认值为none。
下面通过一个示例说说他们在实际应用中应该如何进行设置。
 

 

目录:
  • inverse
    • 测试1
    • 测试2
    • 测试3
    • 测试4
    • 测试5
  • cascade
    • 测试6: cascade="all"
    • 测试7:cascade="all-delete-orphan"

 

本节班级(Class类)与学生(Student)、学生(Student)与课程(Course)来进行说明。

Student实体类定义:
 
 
       public  class  Student 

    {
        public virtual int Id { getset; }

        public virtual string Name { getset; }

        public virtual int Gender { getset; }

        public virtual Class Class { getset; }

        //public virtual int ClassId { get; set; }

        public virtual IList<Course> Courses { getset; }
    }

 

配置(基本部分,不含many-to-one配置。因为各种测试下inverse\cascade值可能不同,所以具体将在各种测试中给出):

<?xml version= " 1.0 " encoding= " utf-8 " ?>
<hibernate-mapping xmlns= " urn:nhibernate-mapping-2.2 ">
    < class name= " NHibernateCascadeInverse.Entities.Student,NHibernateCascadeInverse " lazy= " true " table= " Student ">
        <id column= " Id " name= " Id " type= " Int32 ">
            <generator  class= " native "></generator>
        </id>
        <property name= " Name " column= " Name "></property>
        <property name= " Gender " column= " Gender "></property>
        <!--<property name= " ClassId " column= " ClassId "></property>-->
    </ class>
</hibernate-mapping>
 
Class实体类定义
 
      public  class  Class 

    {
        public virtual int Id { getset; }

        public virtual string SerialNO { getset; }

        public virtual int DepartmentId { getset; }

        public virtual ISet<Student> Students { getset; }
    }

 
配置:
<?xml version= " 1.0 " encoding= " utf-8 " ?>
<hibernate-mapping xmlns= " urn:nhibernate-mapping-2.2 ">
    < class name= " NHibernateCascadeInverse.Entities.Class,NHibernateCascadeInverse ">
        <id name= " Id " >
            <generator  class= " native "></generator>            
        </id>
        <property column= " SerialNO " name= " SerialNO " type= " string "></property>
        <property column= " DepartmentId " name= " DepartmentId " type= " Int32 "></property>    
    </ class>
</hibernate-mapping>
 

测试1:希望在添加班级时,将若干新学生到该班级中。(注意:在此次测试中,删除了Class表与Student表之间的主外键关系。正常为保证数据的完整性还是应建立主外键关系)

 
Student中many-to-one 配置:
 
<many-to-one   name= " Class "            

           class="NHibernateCascadeInverse.Entities.Class,NHibernateCascadeInverse" >
            <column name="ClassId" not-null="false" sql-type="int"></column>            
        </many-to-one>

 
Class 中one-to-many配置
 
< set  name= " Students "  lazy= " false "   cascade= " all "   inverse="false" >             

<key column="ClassId" ></key>
            <one-to-many class="NHibernateCascadeInverse.Entities.Student,NHibernateCascadeInverse"/>
</set>

此时,维护Class与Student直接关系的是Class
测试代码:
   
          [Test] 

publicvoid ClassAddTest()
        {
            Class classEntity = new Class
                                    {
                                        SerialNO = "C0302",
                                        DepartmentId = 36,
                                        Students =
                                            new HashedSet<Student> {new Student {Name = "zhangsan"}, new Student {Name = "zhangsan1"}}
                                    };
            var repository = new ClassRepository();
            repository.Add(classEntity);
        }

测试结果:
由测试结果可知:Class与Student对象都保存至数据库,而Student对象是先将外键字段设置为null,然后根

据插入的Class对象的ClassId将刚插入的Student进行批量更新。

测试2、将Class中many中inverse设置为:true
 
< set  name= " Students "  lazy= " false "   cascade= " all "   inverse= "true" >             

<key column="ClassId" ></key>
            <one-to-many class="NHibernateCascadeInverse.Entities.Student,NHibernateCascadeInverse"/>
</set>

此时,维护Class与Student直接关系的是Student
Student中Class设置为:
 
          <many-to-one   name= " Class "  
             class= " NHibernateCascadeInverse.Entities.Class,NHibernateCascadeInverse " >
            <column name= " ClassId " not- null= " false " sql-type= " int "></column>            
        </many-to-one>

 

测试2 结果: 

 

通过以上信息可知:数据被正常保存了,插入数据时,已经为待插入的Student对象构造

好了外键。所以在one-to-many关联中一般通过将many一方(如上例中的Student)作为维护双方关系

的主控方,也就是在one(如上例中的Class)设置inverse="true"

添加一个many-to-many的映射,来测试一下 Inverse对此关系的影响。新增一个Course类(课程)。一

个学生对应多门课程,一门课程也对应许多学生。

    public  class Course
    {
         public  virtual  int Id {  getset; }

         public  virtual  string Name {  getset; }

         public  virtual  int TeacherId {  getset; }

         public  virtual IList<Student> Students {  getset; }
    }
 

Course的映射:(many-to-many部分没有给出,在各种测试中进行不同配置)

 

<hibernate-mapping xmlns= " urn:nhibernate-mapping-2.2 ">
    < class name= " NHibernateCascadeInverse.Entities.Course,NHibernateCascadeInverse " lazy= " true " >
        <id column= " Id " name= " Id " type= " Int32 ">
            <generator  class= " native "></generator>
        </id>
        <property name= " Name " column= " Name "></property>
        <property name= " TeacherId " column= " teacherID "></property>
    </ class>
</hibernate-mapping>
 
Student的映射:

 

<hibernate-mapping xmlns= " urn:nhibernate-mapping-2.2 " >
    < class name= " NHibernateCascadeInverse.Entities.Student,NHibernateCascadeInverse " lazy= " true " table= " Student ">
        <id column= " Id " name= " Id " type= " Int32 ">
            <generator  class= " native "></generator>
        </id>
        <property name= " Name " column= " Name "></property>
        <property name= " Gender " column= " Gender "></property>
        <many-to-one   name= " Class " 
                      class= " NHibernateCascadeInverse.Entities.Class,NHibernateCascadeInverse " >
            <column name= " ClassId " not- null= " false " sql-type= " int "></column>            
        </many-to-one>
        <property name= " ClassId " column= " ClassId "></property>
    </ class>
</hibernate-mapping>
 
测试3:添加学生和课程信息(学生与课程之间有个中间表CourseInfo保存学生选课信息):
 

设置Student为主控方,即Course的Inverse设置为true.Student many-to-many映射

 
         <bag name= " Courses "  inverse= " false "   table= " CourseInfo "  cascade= " all " > 
<key foreign-key= " FK_CourseInfo_Student " column= " StudentId "></key>
            <many-to-many  class= " NHibernateCascadeInverse.Entities.Course,NHibernateCascadeInverse " column= " CourseId "></many-to-many>
        </bag>
 

Course many-to-many映射

    
  <bag inverse= " true "  name= " Students "  table= " CourseInfo "  cascade= " all " > 
   <key foreign-key= " FK_CourseInfo_Course " column= " CourseId ">                
            </key>
            <many-to-many column= " StudentID "  class= " NHibernateCascadeInverse.Entities.Student,NHibernateCascadeInverse "></many-to-many>
 </bag>
 
测试代码:
 
[Test]          

publicvoid ManyToMany_Test()
        {
            var student = new Student { Gender = 1, Name = "testAccount", ClassId = 10 };
            var course = new Course { Name = "Computer", TeacherId = 1 };
            student.Courses = new List<Course> { course };
            course.Students = new List<Student> { student };
            StudentRepository studentRepository = new StudentRepository();
            CourseRepository courseRepository = new CourseRepository();
            //courseRepository.Add(course);
            studentRepository.Add(student);            
        }

 

测试3 结果:

 
 

测试三中,以Student为主控方,保存信息的时候也以Student进行保存。倘若以Course一方进行保存,

数据也能正常写入吗?

 

测试4: 以受控方(Course )测试保存many-to-many的数据

 
[Test]          

publicvoid ManyToMany_Test()
        {
            var student = new Student { Gender = 1, Name = "testAccount", ClassId = 10 };
            var course = new Course { Name = "Computer", TeacherId = 1 };
            student.Courses = new List<Course> { course };
            course.Students = new List<Student> { student };
            CourseRepository courseRepository = new CourseRepository();
            courseRepository.Add(course);            
        }

 

测试4结果:

 

 

由测试三、测试四可以看出:两种情况下就Student与Course表的写入顺序不一样,但都能保存正常。所以在many-to-many中,以哪方作为主控方,没有影响。

 
Cascade:

设置对象关联关系中的级联操作。以上用了cascade="all"。其实它还有其他值:

cascade 可选值:none、save-update、delete、all-delete-orphan。
 

先通过一个测试简单了解cascade 的作用。

测试5: 在测试1 中去掉cascade,将many-to-one的配置改为如下:

 

< set name= " Students " lazy= " false "   inverse= " true ">
            <key column= " ClassId " ></key>
            <one-to-many  class= " NHibernateCascadeInverse.Entities.Student,NHibernateCascadeInverse "/>
</ set>
 

Student的配置还如测试一,保持不变。

测试5 结果如下:

由此可见,不设置cascade情况下,只保存了Class数据。
 
save-update、delete这几个都好理解。主要说说all与all-delete-orphan的区别
 
测试6: 级联关系在Class中设置Cascade为all.
 

还以Class与Student为例进行删除测试。

 

测试准备:通过测试1,添加一个班级、两条学生信息。

 

测试:将删除班级中一条学生信息,然后删除班级

 

测试代码:

        [Test]
         public   void DeleteClassTest()
        {
             var repository =  new ClassRepository();
            Class @class = repository.Get( 287);
            IEnumerator<Student> students = @class.Students.GetEnumerator();
            Student student =  null;
             if (students.MoveNext())
            {
                student = students.Current;
            }
             if ( null!=student)
            {
                @class.Students.Remove(student);
            }
            repository.Delete(@class);  }

 

测试6 结果: 

 
测试7: 级联关系在Class中设置Cascade为all-delete-orphan.
 

测试代码同测试六。测试7 结果如下:

 

由测试六、测试七可知:对于删除与父类之间的关系时,all-delete-orphan会将孤儿删除

(orphan意为:孤儿),而all不会删除,而是将与父关联的外键字段设置为null。


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM