什么是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这个怪物.