[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這個怪物.

浙公網安備 33010602011771號