[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;
                }
            }
        }

 

测试结果 

 

 

 生成了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;
                }
            }
        }

 测试结果

 


代码中维持关系的是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;
                }
            }
        }

 

测试结果


 

 

代码中维持关系的是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;
                }
            }
        }

 测试结果


 

 

 

细心的同学会发现 这里的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;
                }
            }
        }

测试结果

同样生成了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这个怪物.


 

 

 

 

 

 

 

posted @ 2012-02-15 00:18  子夜.  Views(2782)  Comments(2Edit  收藏  举报