[Nhibernate] Inverse
什么是inverse?
双向many-to-one
Employee.hbm.xml
<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
<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属性来维护二者的关系
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
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组合
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条语句 (三条insert语句 然后在把后来插入的两条insertEmp给update) [暂且不考虑效率]
上面2个测试的例子说明 两端都为false的情况下 随意一端来维持关系都是可以的。
但是用department对象来关联要生成5条语句效率不敢恭维,而且department.employees.add 在lazy="true"的时候也很吓人(上面有提到过)
结论1:尽量不要用department对象 也就是带有集合属性的一端去维持关系。
one端
<key column="DepId" ></key>
<one-to-many class="Employee"/>
</set>
many端
大家知道many端默认为inverse="false",怎么才能模拟出inverse="true".
加上insert="false" ,update="false" 两个默认都为true。
这样就模拟出了inverse="true"了
就是说 many端 不参与 关系维护,而one端参与关系维护
测试代码利用A组合
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组合
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组测试情况一样
也就验证了 上面的话
这个不用测了吧?跟开篇的例子一样的。如果有兴趣的同学们可以自己测试一下另一种情况。
到底在哪端设置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属性来维护关系了
-->
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这个怪物.