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