[Nhibernate] Inverse

什么是inverse?

 

通过inverse的设置来决定是由哪端来维护对象之间的关系的 

 

双向many-to-one

Employee.hbm.xml

<? xml version="1.0" encoding="utf-8"  ?>
< hibernate-mapping
  
xmlns ="urn:nhibernate-mapping-2.2"
  namespace
="ZiyeNhi.Entities"
  assembly
="ZiyeNhi.Entities" >

   < class  name ="Employee"  table ="Emp" >
     < id  name ="EmpId"  column ="EmpId"  type ="int" >
       < generator  class ="identity" >
       </ generator >
     </ id >

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

     < many-to-one  name ="Department"  class ="Department"  column ="DepId"  lazy ="proxy" ></ many-to-one >
   </ class >

</ hibernate-mapping >

 

 Department.hbm.xml

<? xml version="1.0" encoding="utf-8"  ?>
< hibernate-mapping
  
xmlns ="urn:nhibernate-mapping-2.2"
  namespace
="ZiyeNhi.Entities"
  assembly
="ZiyeNhi.Entities" >

   < class  name ="Department"  table ="Dep" >
     < id  name ="DepId"  column ="DepId"  type ="int" >
       < generator  class ="identity" >
       </ generator >
     </ id >
     < property  name ="DepName"  column ="DepName"  type ="string"  length ="50" ></ property >

     <!-- <one-to-many> -->
     < set  name ="Employees"  table ="Emp"  inverse ="true" lazy ="true"   > <!-- cascade -->
       < key  column ="DepId"   ></ key >
       < one-to-many  class ="Employee" />
     </ set >
   </ class >

</ hibernate-mapping >

 

 

一般来说我们都习惯

one端(department) inverse="true";(对应上面的department.hbm.xml)

many端(Employee) inverse="false";(对应上面的employee.hbm.xml)

这样我们就可以用department属性来维护二者的关系

 

[Test]
         public  void SaveDepartment()
        {
             using (ISession session =  this.sessionFactory.OpenSession())
            {
                var emp1 =  new Employee() { EmpName =  " xr " };
                var emp2 =  new Employee() { EmpName =  " wxr " };
                var department =  new Department() { DepName =  " QQ " };
                emp1.Department = department;
                emp2.Department = department;
                ITransaction transaction = session.BeginTransaction();
                 try
                {
                    session.Save(department);
                    session.Save(emp1);
                    session.Save(emp2); 
                    transaction.Commit(); 
                }
                 catch (Exception ex)
                {
                    transaction.Rollback();
                     throw ex;
                }
            }
        }

 

测试结果 

 

[Nhibernate] Inverse_第1张图片

 

 生成了3条insert语句一条是插入department,另外两条是插入Employee的 他们二者的关系是由Employee对象的department属性来维护的

 为什么我们一般都要这么做呢

 第一:如果我们通过Department.Employee.add(emp对象) 这种关系来维护的话,那么当前部门下有N个employee lazy=“true”的时候 是不是很浪费资源?

 第二:数据模型中 一般情况下 对象关系都是由子表体现出来(看department表中 只有DepId,DepName,而在employee表中可以看到外键depId).那么我们在对象模型中也推荐子对象来维护关系.

 第三:期待补充.

 

 Inverse属性在many-to-one中的应用

 1.经测试。如果两端都设置为inverse="true"那么两端都不会维护关系

 2.两端都为inverse="false"的情况下

 3.one端为false,many端为true

 4.one端为true,many端为false

 

 围绕2 3 4 来说

 两端都为false

 

[Test]
         public  void SaveDepartment()
        {
             using (ISession session =  this.sessionFactory.OpenSession())
            {
                var emp1 =  new Employee() { EmpName =  " xr " };
                var emp2 =  new Employee() { EmpName =  " wxr " };
                var department =  new Department() { DepName =  " QQ " };
                 // 以下两句为A组合 Employee对象来维护关系
                emp1.Department = department;
                emp2.Department = department;
                 // 以下两句为B组合 Department对象来维护关系
                
// department.Employees.Add(emp1);
                
// department.Employees.Add(emp2);
                ITransaction transaction = session.BeginTransaction();
                 try
                {
                    session.Save(department);
                    session.Save(emp1);
                    session.Save(emp2);
                    transaction.Commit(); 
                }
                 catch (Exception ex)
                {
                    transaction.Rollback();
                     throw ex;
                }
            }
        }

 测试结果

 [Nhibernate] Inverse_第2张图片


代码中维持关系的是employee对象 或者说是employee对象下的Department属性

生成了3条语句 (3条insert 先插入department 然后通过插入department返回的ID 来插入2条employee)

 

将A组合切换B组合

[Test]
         public  void SaveDepartment()
        {
             using (ISession session =  this.sessionFactory.OpenSession())
            {
                var emp1 =  new Employee() { EmpName =  " xr " };
                var emp2 =  new Employee() { EmpName =  " wxr " };
                var department =  new Department() { DepName =  " QQ " };
                 // 以下两句为A组合 Employee对象来维护关系
                
// emp1.Department = department;
                
// emp2.Department = department;
                
// 以下两句为B组合 Department对象来维护关系
                department.Employees.Add(emp1);
                department.Employees.Add(emp2);
                ITransaction transaction = session.BeginTransaction();
                 try
                {
                    session.Save(department);
                    session.Save(emp1);
                    session.Save(emp2); 
                    transaction.Commit(); 
                }
                 catch (Exception ex)
                {
                    transaction.Rollback();
                     throw ex;
                }
            }
        }

 

测试结果

[Nhibernate] Inverse_第3张图片
 

 

代码中维持关系的是Department对象 或者说是Department对象下的Employee属性

 

生成了5条语句 (三条insert语句 然后在把后来插入的两条insertEmp给update) [暂且不考虑效率]

 

上面2个测试的例子说明 两端都为false的情况下 随意一端来维持关系都是可以的。

但是用department对象来关联要生成5条语句效率不敢恭维,而且department.employees.add 在lazy="true"的时候也很吓人(上面有提到过)

结论1:尽量不要用department对象 也就是带有集合属性的一端去维持关系。

 

 

 

 

one端为false,many端为true

one端

 

< set  name ="Employees"  table ="Emp"  inverse ="false" lazy ="true"   >
       < key  column ="DepId"   ></ key >
       < one-to-many  class ="Employee" />
     </ set >

 

 many端

 

< many-to-one  name ="Department"  class ="Department"  column ="DepId"  lazy ="proxy" ></ many-to-one >

 大家知道many端默认为inverse="false",怎么才能模拟出inverse="true".

< many-to-one  name ="Department"  class ="Department"  column ="DepId"  insert ="false"  update ="false"  lazy ="proxy" ></ many-to-one >

 

<many-to-one/>和<one-to-one/>没有inverse属性,可以用insert和update属性模拟出inverse="true",

 

加上insert="false" ,update="false" 两个默认都为true。

这样就模拟出了inverse="true"了

就是说 many端 不参与 关系维护,而one端参与关系维护

测试代码利用A组合

[Test]
         public  void SaveDepartment()
        {
             using (ISession session =  this.sessionFactory.OpenSession())
            {
                var emp1 =  new Employee() { EmpName =  " xr " };
                var emp2 =  new Employee() { EmpName =  " wxr " };
                var department =  new Department() { DepName =  " QQ " };
                 // 以下两句为A组合 Employee对象来维护关系
                emp1.Department = department;
                emp2.Department = department;
                 // 以下两句为B组合 Department对象来维护关系
                
// department.Employees.Add(emp1);
                
// department.Employees.Add(emp2);
                ITransaction transaction = session.BeginTransaction();
                 try
                {
                    session.Save(department);
                    session.Save(emp1);
                    session.Save(emp2); 
                    transaction.Commit(); 
                }
                 catch (Exception ex)
                {
                    transaction.Rollback();
                     throw ex;
                }
            }
        }

 测试结果


  [Nhibernate] Inverse_第4张图片

 

 

细心的同学会发现 这里的2 3条insert语句它们的Department外键并没有插入

说明他们的关系没有维护成功。


 

在来A组合换成B组合

 [Test]
         public  void SaveDepartment()
        {
             using (ISession session =  this.sessionFactory.OpenSession())
            {
                var emp1 =  new Employee() { EmpName =  " xr " };
                var emp2 =  new Employee() { EmpName =  " wxr " };
                var department =  new Department() { DepName =  " QQ " };
                 // 以下两句为A组合 Employee对象来维护关系
                
// emp1.Department = department;
                
// emp2.Department = department;
                
// 以下两句为B组合 Department对象来维护关系
                department.Employees.Add(emp1);
                department.Employees.Add(emp2);
                ITransaction transaction = session.BeginTransaction();
                 try
                {
                    session.Save(department);
                    session.Save(emp1);
                    session.Save(emp2); 
                    transaction.Commit(); 
                }
                 catch (Exception ex)
                {
                    transaction.Rollback();
                     throw ex;
                }
            }
        }

测试结果

[Nhibernate] Inverse_第5张图片 同样生成了5条SQL 与上面两端为 false的B组测试情况一样

 

也就验证了 上面的话

就是说 many(Employee)端 不参与 关系维护,而one(Department)端参与关系维护

 

 

one端为true,many端为false

这个不用测了吧?跟开篇的例子一样的。如果有兴趣的同学们可以自己测试一下另一种情况。

 

到底在哪端设置inverse呢?

大部分我们都在没有集合的那一端设置inverse="false",如果用集合那端来维持关系。比如department.employees.add(上面提到过 lazy="true")

怎么去记这个东西。其实我也被这个东西困扰了20多个小时了。

总结一下:

 

<!--  第一种从“对象”的角度去记
    当前xml文件里的class是employee对象,设置当前employee对象里的department属性inverse="false"
    那么当前employee就要参与关系维护
    那么我们save的时候 就需要用Employee对象去维护关系 所有就有了
       var emp1 = new Employee() { EmpName = "xr" };
       var department = new Department() { DepName = "QQ" };
       emp1.Department = department;    ->employee的对象emp1来维护关系.
       ....
       session.Save(department);
       session.Save(emp1);
    
         第二种从“属性”的角度去记
    inverse="false" 这个是在Employee对象里Department的属性中标示的
    那么Department属性就要参与关系维护
    那么我们save的时候就需要用employee对象的Department属性去维护关系 所有就有以下代码
       var emp1 = new Employee() { EmpName = "xr" };
       var department = new Department() { DepName = "QQ" };
       emp1.Department = department;    ->employee的对象emp1中的department属性维护关系.
       ....
       session.Save(department);
       session.Save(emp1);
       
       PS:综上两点所述:如果在employee的配置文件里设置Department属性为false
          那么要么就是employee对象去维护,要么就是employee对象下的Department属性去维护。
       
         第三种也是从第二种延伸的
     inverse="false" 字面理解就是"镜像"为false
     那么这个对象就是真实的对象.那么我们设置它的属性是有效的
     维护关系的时候就设置department属性即可
       emp1.Department = department; 这里就设置了department属性 因为这个属性是没有镜像 那么他就是真实的
        
       PS:因为当前employee 也就是many端的inverse="false" 
          那么department就是one端的inverse="true"
          用第三种来理解就是department端里的employee属性是有镜像的
          镜像是什么意思呢?
          department 下面有N个Employee对象(这里的employee对象是真实的)
          如果设置inverse="true"那么这个employee对象就不是真实的了
          既然不真实 设置它的属性 就是没用的.
          那么就不能用department.employee 来维护关系(当前employee对象是镜像)
          那就得用department属性来维护关系了 
    
-->

 


对于双向one-to-many或者many-to-one
inverse="true"被指定于集合端(one-to-many/many-to-many),很少被指定于引用端(many-to-one/one-to-one/any)(默认为inverse="false")。

如果集合端为<list>或<map>,并且子表中的index或key字段没有被作为普通属性映射,则必须让集合有效而在引用端使用inverse,其余一切情况,均强烈建议在集合上使用inverse

对于两端都是集合的双向many-to-many
有一端的集合可以任意地使用<set>, <bag>, <idbag>, <list>, <map>元素,而另外一段的集合则只能使用<set>或<bag>
如果有一段是<idbag>, <list>或<map>,则inverse属性必须加载另外一端的<set>或<bag>上
如果两端都为<set>或<bag>,则任意。但出于优化目的,尽量加在概率上长度比较大的集合端上

(比如,一个用户的角色可能只有几种,但是一种角色可能被授予很多用户,请在Role对象的Users集合上添加inverse="true")

 

全文完。希望看完的同学能够掌握inverse这个怪物.


 

 

 

 

 

 

 

你可能感兴趣的:(Hibernate)