2011-07-08 90 views
1

我有一個計劃(字段不是必要的):NHibernate的異常而刪除的對象圖:非空屬性引用null或瞬時值

a busy cat http://picsearch.ru/share/image-BCE8_4E168F3B.jpg

我有映射:

實體

<class name="LogicalModel.Entity" table="`Entity`" lazy="true"> 
    <id name="Id" ..> ... </id> 
    <bag name="Attributes" lazy="true" cascade="all-delete-orphan" fetch="select" batch-size="1" access="property" inverse="true"> 
    <key column="`Entity`" /> 
    <one-to-many class="LogicalModel.Attribute" /> 
    </bag> 
    <bag name="Keys" lazy="true" cascade="all-delete-orphan" fetch="select" batch-size="1" access="property" inverse="true"> 
    <key column="`Entity`" /> 
    <one-to-many class="LogicalModel.Key" /> 
    </bag> 
</class> 

屬性

<class name="LogicalModel.Attribute" table="`Attribute`" lazy="true"> 
    <id name="Id" ..> ... </id> 
    <many-to-one name="Type" class="LogicalModel.Entity" column="`Type`" cascade="save-update" fetch="select" not-null="true" foreign-key="fk_TypeAttribute" /> 
    <many-to-one name="Entity" class="LogicalModel.Entity" column="`Entity`" cascade="none" fetch="select" not-null="true" foreign-key="fk_EntityAttributes" /> 
</class> 

重點

<class name="LogicalModel.Key" table="`Key`" lazy="true"> 
    <id name="Id" ..> ... </id> 
    <bag name="KeyAttributes" lazy="true" cascade="all-delete-orphan" fetch="select" access="property" inverse="true"> 
    <key column="`Key`" /> 
    <one-to-many class="LogicalModel.KeyAttribute" /> 
    </bag> 
    <many-to-one name="Entity" class="LogicalModel.Entity" column="`Entity`" cascade="none" fetch="select" not-null="true" foreign-key="fk_EntityKeys" /> 
</class> 

KeyAttribute:

<class name="LogicalModel.KeyAttribute" table="`KeyAttribute`" lazy="false"> 
    <id name="Id" ..> ... </id> 
    <many-to-one name="Attribute" class="LogicalModel.Attribute" column="`Attribute`" cascade="save-update" fetch="select" not-null="true" foreign-key="fk_AttributeKeyAttribute" /> 
    <many-to-one name="Key" class="LogicalModel.Key" column="`Key`" cascade="none" fetch="select" not-null="true" foreign-key="fk_KeyKeyAttributes" /> 
</class> 

現在請看看...... 正如你看到的,我們已經得到了單向主協會KeyAttribu te - 屬性,所以它只是多對一,我根本不需要返回關聯。

現在的問題是,當我試圖刪除整個圖形 - 刪除實體對象(注意:實體實際上不會加載所有,它只是設置代理的,這就是爲什麼NHibernate的做額外的SELECT查詢檢查引用刪除前) 這樣

Session.Delete(Entity); // here PropertyValueException: 
// not-null property references a null or transient value: LogicalModel.KeyAttribute.Attribute 

Session.Flush(); // Actually I use transactions in my code, but don't mind 

SQL事件探查器:

exec sp_executesql N'SELECT entities0_.[Id] as Id1_1_, entities0_.[Id] as Id1_45_0_, 
FROM [Entity] entities0_ WHERE entities0_.[LogicalModel][email protected]',N'@p0 uniqueidentifier',@p0='DC8F8460-9C41-438A-8334-97D0A94E2528' 

exec sp_executesql N'SELECT attributes0_.[Entity] as Entity12_1_, attributes0_.[Id] as Id1_1_, attributes0_.[Id] as Id1_16_0_, attributes0_.[Type] as Type11_16_0_, attributes0_.[Entity] as Entity12_16_0_ 
FROM [Attribute] attributes0_ WHERE attributes0_.[Entity][email protected]',N'@p0 uniqueidentifier',@p0='63E4D568-EAB2-4DF2-8FED-014C8CB2DE22' 

exec sp_executesql N'SELECT keys0_.[Entity] as Entity4_1_, keys0_.[Id] as Id1_1_, keys0_.[Id] as Id1_43_0_, keys0_.[Entity] as Entity4_43_0_ 
FROM [Key] keys0_ WHERE keys0_.[Entity][email protected]',N'@p0 uniqueidentifier',@p0='63E4D568-EAB2-4DF2-8FED-014C8CB2DE22' 

exec sp_executesql N'SELECT keyattribu0_.[Key] as Key4_1_, keyattribu0_.[Id] as Id1_1_, keyattribu0_.[Id] as Id1_0_0_, keyattribu0_.[Attribute] as Attribute3_0_0_, keyattribu0_.[Key] as Key4_0_0_ 
FROM [KeyAttribute] keyattribu0_ WHERE keyattribu0_.[Key][email protected]',N'@p0 uniqueidentifier',@p0='103D8FB3-0B17-4F51-8AEF-9623616AE282' 

所以,我們可以看到:

非空屬性引用null或瞬時值:LogicalModel.KeyAttribute.Attribute 發生剛過NH校驗字段類KeyAttribute屬性(非空約束以dB爲單位,它的確定)(見探查日誌)。

這的確很有趣,使NH有權刪除屬性和KeyAttributes兩個,NH在KeyAttribute類閱讀屬性字段的信息,FOUND它在DB,不是在NH會議 FOUND它(!) (導致屬性之前加載),並只是拋出這個愚蠢的錯誤。

我已經試過的: 1. make not-null =「false」。在這種情況下,NH進行額外的更新 - 嘗試設置Attribute = NULL - 導致DB中的約束違規。 2.在KeyAttribute-Attribute的多對一​​關聯上設置lazy =「false」,lazy =「no-proxy」 - nothing;

現在我不喜歡攔截器的想法,因爲有許多場景中我已經得到了同樣的情況,我需要共同的解決辦法

拜託,夥計們,有什麼建議?

回答

1

在我看來,這可以由你懶的負荷模型的所有實體造成的。 當刪除實體,它加載和刪除引用的屬性列表中,引用密鑰列表負載,負載引用KeyAttribute名單(有刪除鍵),然後將其落在非空屬性,因爲引用屬性已被刪除引用null或瞬時值在會議之前。

您可以通過在映射文件中刪除所有延遲加載檢查。

快速解決方案可能是保持延遲加載,但在刪除時強制模型的完整加載(使用hibernate initialize()),例如在Entity工廠的Delete(Entity)靜態方法中。

+0

謝謝設置你回答。 我試圖加載全圖表及殺死屬性關閉,以使其成爲非暫時的(我剛纔讀通過遍歷他們:)所有集合和鏈接),它並沒有幫助我懶(( 」 ..referenced屬性已被刪除之前在會話中..「 這可能是! – EvgeniyK

+0

@Vince:我不認爲它與延遲加載有什麼關係 –

+0

@Stefan Steinegger,@Vince 我去反射器,看來NH需要外部刪除順序控制,事業屬性persistContext有關於協會的KeyAttribute的任何信息。 – EvgeniyK

0

NH有時需要將引用設置爲null。通常這是爲了避免存在循環引用的模型中的問題。但是,找到避免它的方法並不總是很聰明,即使它是一個。

因此,它可能需要爲允許空值在國外的一些重點領域,當然不僅是在映射文件,也可在數據庫。它實際上應該解決問題。


或者,你也可以通過使用表中刪除HQL的數據表。這適用於所有情況得很好,你沒有繼承,如果你知道所有的實體和順序將其刪除:

object entityId; 

// gets keys to delete 
List<object> keyIds = Session 
    .CreateQuery("select id from Key where Entity = :entity") 
    .SetEntity("entity", Entity) 
    .List<object>(); 

// delete KeyAttribute which reference the key 
Session.CreateQuery("delete KeyAttribute where Key.id in (:keyIds)") 
    .SetParameterList("keyIds", keyIds) 
    .ExecuteUpdate(); 

// delete the keys 
Session.CreateQuery("delete Key where id in (:keyIds)") 
    .SetParameterList("keyIds", keyIds) 
    .ExecuteUpdate(); 

// get attributes to delete 
List<object> attributeIds = Session 
    .CreateQuery("select id from Attribute where Entity = :entity") 
    .SetEntity("entity", Entity) 
    .List<object>(); 

// delete KeyAttributes which reference the attributes 
Session.CreateQuery("delete KeyAttribute where Attribute.id in (:attributeIds)") 
    .SetParameterList("attributeIds", attributeIds) 
    .ExecuteUpdate(); 

// delete the attributes 
Session.CreateQuery("delete Attribute where id in (:attributeIds)") 
    .SetParameterList("attributeIds", attributeIds) 
    .ExecuteUpdate(); 

Session.CreateQuery("delete Entity where id = :entityId") 
    .SetParameter("entityId", Entity.Id) 
    .ExecuteUpdate(); 

注:

  • 您可能會破壞參數列表到一塊,如果它們超過了2000年左右的大小(在SQL Server中)。
  • 在數據庫中直接刪除會話時會話不同步。這不會導致任何問題時,刪除是你所做的一切。當您在同一會話中進行其他工作人員時,請在刪除後清除會話。
+0

謝謝你的答案。 我認爲這是相當困難的編碼,特別是在HQL!現在我想要重寫CascadeBeforeDelete在自己的DeleteEventListener ,我想我可以通過該wa設置正確的刪除順序y – EvgeniyK

+0

@EvgeniyK:等一下,我說允許空值*應該*解決問題。 「硬編碼」部分只是給你更多的控制,可能是一個很好的性能優化。 –

+0

Yeap,如果我放棄NH&MS SQL中的所有限制,當然一切都會好的,毫無疑問;在俄羅斯它稱:「非運動員行爲」=)) – EvgeniyK

1

您是否嘗試過上刪除=「級聯」在

<class name="LogicalModel.Key" table="`Key`" lazy="true"> 
<id name="Id" ..> ... </id> 
<bag name="KeyAttributes" lazy="true" cascade="all-delete-orphan" fetch="select" access="property" inverse="true"> 
    <key column="`Key`" on-delete="cascade" /> 
    <one-to-many class="LogicalModel.KeyAttribute" /> 
</bag> 
<many-to-one name="Entity" class="LogicalModel.Entity" column="`Entity`" cascade="none" fetch="select" not-null="true" foreign-key="fk_EntityKeys" /> 

因爲在配置文件,你會看到NH試圖更新的東西null,其非可空

+0

嗯..我現在試過了,它並沒有幫助,因爲NH仍然不知道該屬性必須與KeyAttribute一起刪除,但KeyAttributes首先,沒有檢查Nullability。最後NH試圖更新鏈接到屬性,它不是null。 如果我們將通過反射看到的,問題是在方法DeleteEntity - >呼叫的方法NHibernate.Engine.Nullability.CheckNullability: 公共無效CheckNullability(對象[]值,IEntityPersister持留,布爾isUpdate) 可能是需要檢查一個 – EvgeniyK

相關問題