删除对象图时出现 NHibernate 异常:not-null 属性引用 null 或瞬态值
我有一个方案(字段不是必需的):
一只忙碌的猫 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>
现在请看一下... 如您所见,我们有单向主关联KeyAttribute - Attribute,所以它只是多对一,我根本不需要反向关联。
现在的问题是,当我尝试删除整个图时 - 删除实体对象(注意:实体实际上根本没有加载,它只是一组代理,这就是 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 Profiler:
exec sp_executesql N'SELECT entities0_.[Id] as Id1_1_, entities0_.[Id] as Id1_45_0_,
FROM [Entity] entities0_ WHERE entities0_.[LogicalModel]=@p0',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]=@p0',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]=@p0',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]=@p0',N'@p0 uniqueidentifier',@p0='103D8FB3-0B17-4F51-8AEF-9623616AE282'
所以我们可以看到:
not-null 属性引用 null 或瞬态值:LogicalModel.KeyAttribute.Attribute 发生在KeyAttribute类中的NH检查字段Attribute(数据库中的非空约束,没关系)之后(请参阅探查器日志)。
这很有趣,因为NH必须同时删除Attributes和KeyAttributes,NH读取KeyAttribute类中有关Attribute字段的信息,FOUND在DB中,NOT FOUND在中NH 会话(!!!)(因为属性之前已加载),然后抛出这个愚蠢的错误。
我已经尝试过做的事情: 1. 使 not-null="false"。在这种情况下,NH 进行额外更新 - 尝试设置 Attribute=NULL - 导致 DB 中的约束冲突。 2. 在KeyAttribute-Attribute 的多对一关联上设置lazy="false"、lazy="no-proxy" - 无;
现在我不喜欢拦截器的想法,因为在很多情况下我都会遇到相同的情况,我需要通用的解决方案
请问,伙计们,有什么建议吗?
I've got a scheme (fields aren't necessary):
a busy cat http://picsearch.ru/share/image-BCE8_4E168F3B.jpg
I've got mappings:
Entity
<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>
Attribute
<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>
Key
<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>
Now please take a look...
As you see, We've got one-way master association KeyAttribute - Attribute, so it's just many-to-one and I don't need back association at all.
Now the problem is when I'm trying to delete whole graph - delete Entity object (notice: Entity actually aren't loaded at all, it's just set of proxies, that's why NHibernate make additional SELECT queries to check references before delete)
like this
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 Profiler:
exec sp_executesql N'SELECT entities0_.[Id] as Id1_1_, entities0_.[Id] as Id1_45_0_,
FROM [Entity] entities0_ WHERE entities0_.[LogicalModel]=@p0',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]=@p0',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]=@p0',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]=@p0',N'@p0 uniqueidentifier',@p0='103D8FB3-0B17-4F51-8AEF-9623616AE282'
So what we can see:
not-null property references a null or transient value: LogicalModel.KeyAttribute.Attribute
happened just after NH check field Attribute (not-null constraint in db, it's ok) in class KeyAttribute (see profiler log).
It's pretty fun, cause NH have to delete Attributes and KeyAttributes both, NH read information about Attribute field in KeyAttribute class, FOUND it in DB, NOT FOUND it in NH session (!!!) (cause Attributes was loaded before), and just throw this stupid error.
What I've already tried to do:
1. make not-null="false". In this case NH makes additional update - try to set Attribute=NULL - cause constraint violation in DB.
2. set lazy="false", lazy="no-proxy" on many-to-one association for KeyAttribute-Attribute - nothing;
Now I don't like the idea of interceptors because there are to many scenarios where I've got the same situation, I need common solution
Please, guys, any suggestions?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
在我看来,这可能是由于您对模型的所有实体的延迟加载造成的。
删除实体时,它会加载并删除引用的属性列表,加载引用的键列表,加载引用的KeyAttribute列表(以具有删除的键),然后它陷入非空属性引用空值或瞬态值,因为引用的属性之前已被删除会议中。
您可以通过删除映射文件中的所有延迟加载来检查这一点。
一个快速的解决方案可能是保持延迟加载,但在删除时强制完全加载模型(使用 hibernate初始化()),例如在实体工厂中的Delete(Entity)静态方法中。
In my opinion it may be caused by your lazy load on all entities of model.
When deleting entity, it loads and delete referenced Attribute list, loads referenced Key list, loads referenced KeyAttribute list (to have key of deletion) and then it falls in not-null property references a null or transient value because referenced Attribute has been deleted before in session.
You can check that by removing all lazy load in your mapping files.
A quick solution may be to keep lazy load but to force a full load of model (with hibernate initialize()) when deleting, for example in a Delete(Entity) static method in Entity factory.
您是否尝试过在配置文件中设置 on-delete="cascade"
因为在配置文件中您会看到 nh 尝试将某些不可为空的内容更新为 null
Have you tried setting on-delete="cascade" in
Because in profile you will see nh trying to update something to null which is non nullable
NH 有时需要将引用设置为 null。通常这是为了避免存在循环引用的模型中出现问题。但它并不总是足够聪明,能够找到一种方法来避免它,即使是一种方法。
因此可能需要在某些外键字段中允许空值,当然不仅在映射文件中,也在数据库中。它实际上应该解决问题。
或者,您也可以使用 HQL 逐表删除数据。如果您没有继承并且您知道所有实体以及删除它们的顺序,那么这在所有情况下都可以正常工作:
注意:
NH sometimes requires to set references to null. Usually this is to avoid problems in models where circular references exist. But it is not always clever enough to find a way to avoid it, even if the is one.
So it may require to allow nulls in some foreign key fields, of course not only in the mapping file, also in the database. It actually should solve the problem.
Alternatively, you could also delete the data table by table using HQL. This works fine in all cases where you don't have inheritance and if you know all entities and the order to delete them:
Note: