NHibernate exception while deleting object graph: not-null property references a null or transient value - nhibernate

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?

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.

Have you tried setting on-delete="cascade" in
<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" />
Because in profile you will see nh trying to update something to null which is non nullable

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:
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();
Note:
You may break the parameter lists into piece if they exceed the size of around 2000 (in SQL Server).
The session gets out of synch when deleting directly in the database. This doesn't cause any problems when deleting is all you do. When you are doing other staff in the same session, clear the session after deleting.

Related

One-to-one or many-to-one mapping using primary key of each table

I've got a legacy schema with a main table and secondary table, where the secondary table connects to the main table by having the same primary key (a Secondary doesn't not necessarily exist for a given Main). I've been searching up-and-down for XML mappings that will make this work but haven't found anything that works for me.
<class name="Secondary" table="Secondary" lazy="true" dynamic-insert="true" dynamic-update="true">
<id name="mainId" type="Int32">
<column name="MAIN_ID" not-null="true" />
<generator class="foreign">
<param name="property">Main</param>
</generator>
</id>
<one-to-one class="Main" name="Main" constrained="true" />
</class>
<class name="Main" table="Main" lazy="true" dynamic-insert="true" dynamic-update="true">
<one-to-one name="Secondary" cascade="all-delete-orphan" class="Secondary" />
Also tried on the Main side, still no go. It doesn't necessarily break, but it certainly doesn't do what I expect. For example:
session.Query<Main>().Count(m => m.Secondary != null) generates
select
cast(count(*) as INT) as col_0_0_
from
MAIN main0_
where
main0_.MAIN_ID is not null
Note that it's using the MAIN_ID from the MAIN table and is ignoring Secondary altogether.
This issue is fixed in NHibernate 5.3
It's a known issue (PR with suggested fix is here).
For now as a workaround in LINQ you can call Count on some non-ID and not-nullable property:
session.Query<Main>().Count(m => m.Secondary.NotNullableProperty != null)
If such property doesn't exist (or you just want to easily find all such hacky usages in future) you can additionally map your Id column as read-only property and use it instead:
<property name="ForceJoinId" not-null="true" column="MAIN_ID" insert="false" update="false" />
session.Query<Main>().Count(m => m.Secondary.ForceJoinId != null)

NHibernate constraint violated issue

There are two entities in my code which are participated in a many-to-one relationship. The problem is when i try to delete the parent it says :
ORA-02292: integrity constraint violated - child record found
As you can see below there is a User Security Parameter entity in my project which can have related children called Exceptional User Security parameters.
I expect the ORM to delete the found child records when it wants to eliminate their parent
<bag name="ExeptionalUserSecurityParameters" inverse="true" lazy="false" access="property" cascade="none" batch-size="256">
<key>
<column name="Key" />
</key>
<one-to-many class="ExeptionalUserSecurityParameter"/>
</bag>
<many-to-one name="UserSecurityParameter" cascade="all-delete-orphan" fetch="join"
class="UserSecurityParameters" >
<column name="Key" />
</many-to-one>
How can I avoid this issue?
Almost always (well, always) I do use cascading like this:
//<bag name="ExeptionalUserSecurityParameters" cascade="none" ...
<bag name="ExeptionalUserSecurityParameters" cascade="all-delete-orphan" ...
//<many-to-one name="UserSecurityParameter" cascade="all-delete-orphan"
<many-to-one name="UserSecurityParameter" cascade="none"
that should solve the issue. If the collection owner is deleted.. who ever reference it - is deleted as well. But not vice versa

Polymorfic many-to-one NHibernate Mapping

I'm trying to map the following classes:
PessoaFisica and PessoaJuridica inherits Pessoa.
Cliente has an association with Pessoa, it may be PessoaJuridica or PessoaFisica.
When I save a Cliente object with PessoaFisica, for example, thats ok. But when I try to update and I set the property Pessoa from Cliente to PessoaJuridica and try to update, it updates, but it generates a new row in table TB_PESSOA and the old row, in PessoaFisica is not deleted. It creates a new row to PessoaJuridica, but the old row remains. What's wrong with my mapping XMLs ? Why NHibernate does not delete the old row before insert the new polymorphic object ?
Those are the mapping files I am using
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
namespace="SALClassLib.Masterdata.Model" assembly="SALClassLib">
<class name="Pessoa" table="TB_PESSOA">
<id name="Id">
<column name="ID_PESSOA" not-null="true"/>
<generator class="increment" />
</id>
(other properties...)
<joined-subclass name="PessoaFisica" table="TB_PESSOA_FISICA">
<key column="ID_PESSOA" />
(other properties...)
</joined-subclass>
<joined-subclass name="PessoaJuridica" table="TB_PESSOA_JURIDICA">
<key column="ID_PESSOA" />
(other properties...)
</joined-subclass>
</class>
<class name="Cliente" table="TB_CLIENTE">
<id name="Id">
<column name="ID_CLIENTE" not-null="true"/>
<generator class="increment" />
</id>
<many-to-one name="Pessoa" class="Pessoa" cascade="all" column="ID_PESSOA" not-null="true" unique="true" />
Thank you
NHibernate cascading is nicely explained here: NHibernate Cascades: the different between all, all-delete-orphans and save-update
One of the option, is cascade="all-delete-orphan" which could be seen as what you are asking for.
BUT
Cascading deletion of the orphans is correct only in parent-child scenario (no parent ==> no children) or one-to-one mapping. (i.e not vice versa child-parent)
In your case, you do ask for deletion of the referenced object. But NHibernate (well no-one) can know, if it is not referenced by some other "child".
If you need to delete previous Person assigned, you can always do it in code - but explicitly

One-to-Many List Not Being Saved with NHibernate

I'm very new to NHibernate, and I'm running into a problem while saving a list of child objects.
NOTE
<class name="Note" table="NOTE">
<id name="NoteID" column="NOTE_ID">
<generator class="identity" />
</id>
...
<list name="Sections" table="NOTE_SECTIONS" cascade="all" lazy="false">
<key column="NOTE_ID"/>
<index column="SORT_ORDER"/>
<one-to-many class="Section"/>
</list>
</class>
NOTE SECTION
<class name="Section" table="NOTE_SECTIONS">
<id name="SectionID" column="Section_ID">
<generator class="identity" />
</id>
<property name="NoteID" column="NOTE_ID"/>
...
</class>
The mappings work perfectly for reading the data. However, when I make a change to the Note Section, The queries it generates appears to be going through the proper steps, but then I get the following error:
NHibernate.Exceptions.GenericADOException: could not delete collection: [Domain.Note.Sections#1][SQL: UPDATE NOTE_SECTIONS SET NOTE_ID = null, SORT_ORDER = null WHERE NOTE_ID = #p0] ---> System.Data.SqlClient.SqlException: Cannot insert the value NULL into column 'NOTE_ID', table 'NOTE_SECTIONS'; column does not allow nulls. UPDATE fails.
I have read that in order to save like this it will need to be bidirectional. But I've also read that bidrectional mappings don't work with Lists. It's important that my collection have a maintained order - what's the best way to save?
You should use inverse="true" on your collection mapping if you wish to save child objects in this manner.
<list name="Sections" table="NOTE_SECTIONS" inverse="true" cascade="all" lazy="false">
<key column="NOTE_ID"/>
<index column="SORT_ORDER"/>
<one-to-many class="Section"/>
</list>
Inverse Attribute in NHibernate
probably you would need to refer Note from Note Section as many-to-one relation.

NHibernate Many to Many delete all my data in the table

I would love to thank #Stefan Steinegger and #David helped me out yesterday with many-to-many mapping.
I have 3 tables which are "News", "Tags" and "News_Tags" with Many-To-Many relationship and the "News_Tags" is the link table.
If I delete one of the news records, the following mappings will delete all my news records which have the same tags. One thing I need to notice, I only allowed unique tag stored in the "Tag" table.
This mapping make sense for me, it will delete the tag and related News records, but how can I implement a tagging system with NHibernate?
Can anyone give me some suggestion? Many thanks.
Daoming.
News Mapping:
<class name="New" table="News" lazy="false">
<id name="NewID">
<generator class="identity" />
</id>
<property name="Title" type="String"></property>
<property name="Description" type="String"></property>
<set name="TagsList" table="New_Tags" lazy="false" inverse="true" cascade="all">
<key column="NewID" />
<many-to-many class="Tag" column="TagID" />
</set>
</class>
Tag Mapping:
<class name="Tag" table="Tags" lazy="false">
<id name="TagID">
<generator class="identity" />
</id>
<property name="TagName" type="String"></property>
<property name="DateCreated" type="DateTime"></property>
<!--inverse="true" has been defined in the "News mapping"-->
<set name="NewsList" table="New_Tags" lazy="false" cascade="all">
<key column="TagID" />
<many-to-many class="New" column="NewID" />
</set>
</class>
When I run into trouble like that, the first thing I twiddle with is the cascade option.
As far as I know, the mapping is correct (I'm using mapping files that look exactly the same). The problem is the cascade attribute: the "all" option forces NHibernate to propagate each action on an entity to the instances of the collection. In your case, when you delete a news item all related tags are deleted too.
You probably should use "none" (in that case you'll eventually end up with some unused tags in the database) or "delete-orphans" (on the news item side - use "none" on the tag side).
Use the cascade option "save-update".
The option "all" will cascade deletes, which you do not want in this case. But you the option "none" will require that the Tag entity is already persisted which I guess might not always be the case.
So by setting the cascade to "save-update" new Tags till be inserted in the Tags table and in the link table News_Tags, but when you remove a tag from a News entity it will only be removed from the link table not the Tags table.