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。

posted @ 2012-06-18 21:33  tyb1222  阅读(6308)  评论(8编辑  收藏  举报