删除对象图时出现NHibernate异常:not null属性引用null或瞬态值

删除对象图时出现NHibernate异常:not null属性引用null或瞬态值,nhibernate,cascade,Nhibernate,Cascade,我有一个方案(不需要字段): 我有一些映射: 实体 <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="prope

我有一个方案(不需要字段):

我有一些映射:

实体

<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.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>
SQL分析器:

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 check字段Attribute(在db中不是null约束,可以)之后(请参阅探查器日志)

这很有趣,因为NH必须同时删除属性和KeyAttributes,NH读取有关KeyAttribute类中属性字段的信息,在DB中找到它,NH会话中找不到它(!!!)(因为属性以前加载过),然后抛出这个愚蠢的错误

我已经尝试过的是: 1.make not null=“false”。在这种情况下,NH进行额外更新-尝试设置Attribute=NULL-导致DB中的约束冲突。 2.在KeyAttribute属性的多对一关联上设置lazy=“false”,lazy=“no proxy”—无

现在我不喜欢拦截器的想法,因为有很多场景我都遇到了相同的情况,我需要共同的解决方案


伙计们,有什么建议吗

在我看来,这可能是由于您对模型的所有实体的延迟加载造成的。 删除实体时,它会加载并删除引用的属性列表、加载引用的键列表、加载引用的键属性列表(以具有删除键),然后它会落入NOTNULL属性中,因为引用的属性在会话中之前已被删除,所以会引用null或暂时值

您可以通过删除映射文件中的所有延迟加载来检查这一点


一个快速的解决方案可能是保持延迟加载,但在删除时强制满载模型(使用hibernate initialize()),例如在实体工厂中的Delete(Entity)静态方法中。

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();
objectentityid;
//获取要删除的键
List keyIds=会话
.CreateQuery(“从键中选择id,其中实体=:实体”)
.SetEntity(“实体”,实体)
.List();
//删除引用键的键属性
CreateQuery(“删除(:keyIds)中Key.id所在的KeyAttribute”)
.SetParameterList(“keyIds”,keyIds)
.ExecuteUpdate();
//删除密钥
CreateQuery(“删除id所在的键(:keyIds)”)
.SetParameterList(“keyIds”,keyIds)
.ExecuteUpdate();
//获取要删除的属性
列表属性ID=会话
.CreateQuery(“从实体=:实体的属性中选择id”)
.SetEntity(“实体”,实体)
.List();
//删除引用属性的KeyAttributes
CreateQuery(“删除(:attributeIds)中Attribute.id所在的KeyAttribute”)
.SetParameterList(“AttributeId”,AttributeId)
.ExecuteUpdate();
//删除属性
CreateQuery(“删除属性,其中id位于(:attributeId)”)
.SetParameterList(“AttributeId”,AttributeId)
.ExecuteUpdate();
CreateQuery(“删除实体,其中id=:entityId”)
.SetParameter(“entityId”,Entity.Id)
.ExecuteUpdate();
注:

  • 如果参数列表的大小超过2000左右(在SQL Server中),则可以将其拆分为多个部分
  • 在数据库中直接删除时,会话不同步。这不会导致任何问题,当删除是你所做的一切。当您在同一会话中执行其他人员操作时,请在删除后清除该会话

您是否尝试在中设置delete=“cascade”


... 


因为在概要文件中,您将看到nh试图将不可为null的内容更新为null

谢谢您的回答。我认为这是相当困难的编码,尤其是在HQL中!现在我试图在自己的DeleteEventListener中覆盖CascadeBeeforeDelete,我想我可以通过它设置正确的删除顺序way@EvgeniyK:等一下,我说允许空值应该可以解决问题。“硬编码”部分只是给你更多的控制,可能是一个很好的性能优化;在俄罗斯,它被称为:“非体育行为”=)@EvgeniyK:我认为这是使用ORM的一种权衡。您不需要用普通SQL编写所有内容,但也不能影响SQL查询的确切语句和顺序。因此,可能存在一些优化空间(比如:理论上,这些约束可能有效),但NH不支持。我不会太担心这个问题。@EvgeniyK:正如您所看到的:这是唯一有效的解决方案,除了切换回本机SQL。谢谢您的回答。我试图加载完整图形并关闭属性kill lazy on使其非暂时性(我只是通过迭代来读取所有集合和链接:),但这对我没有帮助((“.referenced Attribute在会话中之前已被删除…”这可能是!@Vince:我不认为这与延迟加载有任何关系。@Stefan Steinegger,@Vince我去了reflector,似乎NH需要外部删除顺序控制,因为在属性persistContext中没有关于与KeyAttribute关联的信息。嗯..我现在已经尝试过了,没有帮助,因为NH仍然不知道该属性必须与KeyAttribute一起删除,但必须先删除KeyAttributes,而不检查null。最后NH尝试更新指向属性的链接,该链接不是null
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
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'
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();
<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" />