NHibernate Tree structure update root problem - c#

I have a tree structure mapped to the data base in a table like this:
UID numeric(18, 0)
NodeUID varchar(50)
Text nvarchar(50)
TreeLevel int
ParentUID varchar(50)
OrderInLevel int
IsLeaf bit
It is an old table and I cannot change it so bear with me...
The NodeUID is a GUID.
the ParentUID column is mapped to the NodeId of a different row.
A root node has a value of "0" in its ParentUID column.
I am trying to map the Tree with NHibernate like this:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="BasicFW.Core.Domain.NavigationTree.UsersNavigationTreeNode, BasicFW.Core" table="bDoxTreeNodes" lazy="false" schema="bDox.dbo">
<id name="NodeUId">
<column name="NodeUID"/>
<generator class="assigned"/>
</id>
<property name="Text">
<column name="Text"/>
</property>
<property name="TreeLevel">
<column name="TreeLevel"/>
</property>
<property name="IsLeaf">
<column name="IsLeaf"/>
</property>
<many-to-one name="Parent" class="BasicFW.Core.Domain.NavigationTree.UsersNavigationTreeNode, BasicFW.Core" column="ParentUID" not-found="ignore"/>
<bag name="Children" lazy="false" order-by="OrderInLevel ASC" cascade="all-delete-orphan" inverse="true">
<key column="ParentUID"/>
<one-to-many class="BasicFW.Core.Domain.NavigationTree.UsersNavigationTreeNode, BasicFW.Core" />
</bag>
</class>
</hibernate-mapping>
The thing is that when I try to update a node ( and a root node in specifies ) the Parent property is null and so NHibernate try`s to Update the ParentUID column to null, and so it failles since to column will not accept null.
thanks

Can you create a trigger for that table? If yes, you could perhaps create a trigger that fires before update/insert. Something like:
create trigger TreeNode_before before insert, update
on TreeNode
referencing NEW as _new
for each row
begin
if _new.ParentUID is null then
set _new.ParentUID = '0';
end if;
end;
Edit: An alternative would be using an interceptor. I have not used that myself but according to the following question it should work in your case, too.
Edit: fixed the link.
NHibernate write value = NULL when ID < 0

I have found the correct mapping that worked, here is the mapping for my tree.
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="BasicFW.Core.Domain.NavigationTree.UsersNavigationTreeNode, BasicFW.Core" table="TreeNodes" lazy="false" >
<id name="NodeID">
<column name="NodeUID"/>
<generator class="assigned"/>
</id>
<property name="Text">
<column name="Text"/>
</property>
<property name="TreeLevel">
<column name="TreeLevel"/>
</property>
<property name="ParentID" >
<column name="ParentUID" />
</property>
<property name="IsLeaf">
<column name="IsLeaf"/>
</property>
<many-to-one name="Parent" class="BasicFW.Core.Domain.NavigationTree.UsersNavigationTreeNode, BasicFW.Core" column="ParentUID" not-found="ignore" update="false" insert="false"/>
<bag name="Children" lazy="false" order-by="OrderInLevel ASC" inverse="true" cascade="all-delete-orphan">
<key column="ParentUID"/>
<one-to-many class="BasicFW.Core.Domain.NavigationTree.UsersNavigationTreeNode, BasicFW.Core"/>
</bag>
</class>
</hibernate-mapping>
I have disabled the update and insert in the Parent property.

Related

ORA 01407 - Error - Cannot update to NULL

My problem is that i have two entities (Document and Attach) that has an relationship one-to-one. In my app, i can save first the Document and lately if i want, i can attach an archive to it, that will be on the table Attach.
The error occurs when i have an object Document already inserted in database and then i try to add an attach on it.
Below is the nhibernate mapping:
Document.hbm.xml
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="Sigre.Business"
namespace="Sigre.Business.BusinessEntity">
<class name="Document" lazy="false" table="grsds.documento_fcdr">
<id name="Code" type="int" unsaved-value="0" column="docf_sq_documento_fcdr">
<generator class="sequence">
<param name="sequence">grsds.sq_docf_sq_documento_fcdr</param>
</generator>
</id>
<property name="Nome" type="AnsiString" length="100" not-null="true" column="docf_nm_documento_fcdr" />
<many-to-one name="Manifest" class="TransportManifest" column="mtra_sq_manifesto_transporte" not-null="false" cascade="none" />
<many-to-one name="User" class="User" column="user_id" not-null="false" cascade="none" />
<set name="Attach" inverse="false" lazy="false" cascade="save-update">
<key column="docf_sq_documento_fcdr" />
<one-to-many class="Attach" />
</set>
</class>
</hibernate-mapping>
Attach.hbm.xml
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="Sigre.Business"
namespace="Sigre.Business.BusinessEntity">
<class name="Attach" lazy="false" table="grsds.espec_documento_fcdr">
<id name="Code" type="int" unsaved-value="0" column="docf_sq_documento_fcdr">
<generator class="foreign">
<param name="property">Document</param>
</generator>
</id>
<property name="Archive" column="esdf_mm_documento_fcdr" type="BinaryBlob" not-null="true" />
<one-to-one constrained="true" name="Document" access="property" />
</class>
</hibernate-mapping>
If i am inserting both Document and Attach together, it works. But when i have a Document created and try to insert an Attach, the following error occurs:
ORA-01407: cannot update ("GRSDS"."ESPEC_DOCUMENTO_FCDR"."DOCF_SQ_DOCUMENTO_FCDR") to NULL
What i tried:
1 - Save the Document having the attach to insert:
bmDocument.Save(document);
Ok i solved this question.
When i bring the object Document from database, the attribute Attach when has no Attach, come as an empty list: {}
When i tried to add an Attach to this Document, i was overwriting the reference, like this:
Document.Attach = new List() { new Attach() };
The solution was use the method Add of the list that was brought from the database:
Document.Attach.Add( new Attach() );

Mapping a non existing column in Nhibernate

I am working with an Oracle legacy database and I am trying to map a relationship with two tables. Unfortunately the company who designed the database used composite keys.
I've got this table (ORDERS) which has a primary key composed on 3 fields: ordernumber (number), version and company.
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="BpSalesOrders.Domain"
namespace="BpSalesOrders.Domain">
<class name="Order" table="OCSAORH" mutable="false" where="OCHAMND = 0">
<cache usage="read-only"/>
<composite-id>
<key-property name="Number" column="OCHORDN" type="String" length="10"></key-property>
<key-property name="Ver" column="OCHAMND" type="Int32"></key-property>
<key-property name="Company" column="OCHCOSC" type="String" length="5"></key-property>
</composite-id>
<property name="Reference" column="OCHOCNO" type="String" length="25"></property>
<property name="Date" column="OCHOCDT" type="Int32"></property>
<property name="Status" column="OCHSTA1" type="Char"></property>
<property name="LineOfCreditStatus" column="OCHSTA4" type="Char"></property>
<property name="WareHouse" column="OCHRESP" type="String"></property>
<set name="OrderLines" access="field.pascalcase-underscore" inverse="true" lazy="true" mutable="false">
<key>
<column name="OCLORDN" not-null="true"/>
<column name="OCLAMND" not-null="true" default="0"/>
<column name="OCLCOSC" not-null="true"/>
</key>
<one-to-many class="OrderLine" not-found ="ignore"/>
</set>
<set name="OrderReleases" access="field.pascalcase-underscore" inverse="true" lazy="true" mutable="false">
<key>
<column name="ORLORDINE" not-null="true" />
<column name="ORLSOCIETA" not-null="true"/>
</key>
<one-to-many class="OrderRelease" not-found ="ignore"/>
</set>
</class>
</hibernate-mapping>
There's a relation with OrderLines which works fine cause that table has the same exact key structure.
I am having problem with the OrderReleases relation. That table has got a primary key composed on a OrderNumber, Company and date. There's no version here:
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="BpSalesOrders.Domain"
namespace="BpSalesOrders.Domain">
<class name="OrderRelease" table="OCBPORDRIL" mutable="true">
<composite-id>
<key-property name="Company" column="ORLSOCIETA" type ="String" length="5"></key-property>
<key-property name="OrderNumber" column="ORLORDINE" type ="String" length="10"></key-property>
<key-property name="RequestDate" column="ORLDATARICHIESTA" type ="Int32"></key-property>
</composite-id>
<property name="Status" column="ORLSTATUS" type="Char" length="1"></property>
<property name="StatusMessage" column="ORLSEGNALAZIONE" type="String" length="90"></property>
<property name="ProcessingDate" column="ORLDATAELABORA" type ="Int32"></property>
</class>
</hibernate-mapping>
Considering that the relationship is one-to-many I would like to join the table Orders and OrderRelease on OrderNumber and Company. If I try to do that (as in the example) nHibernate throws an exception telling me that the key it's trying to map is composed with 3 fields.
Is there a way to create an non-existing column which wouldn't be persisted so that I can setup my mapping and relations?
Any help will be apreciated.
Ended up creating a view with a fake column.

NHibernate save object, a column is null

the database has two table,one is mb_user table and other is mb_comment table。A user have some comments.When I insert a comment into mb_comment ,the comment_user_id of mb_comment is null.I can't save value of comment_user_id.Please help me!
Map file
mb_user Map file
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="MiserBlogData" namespace="MiserBlogData.Entities" default-lazy="false">
<class name="A_Person" table="mb_user" discriminator-value="0">
<id name="Id" column ="user_id" >
<generator class ="native"/>
</id>
<discriminator column="user_role" type="int" />
<property name="State" column="user_state" />
<property name ="Name" column="user_name" />
<property name ="Pwd" column="user_pwd" />
<property name ="CDate" column="user_cdate" />
<property name ="UDate" column="user_udate" />
<property name ="Role" column="user_role" />
<property name ="Face" column="user_face" />
<bag name="CommentList" >
<key column="comment_user_id" foreign-key="FK_PersonComment" />
<one-to-many class="Comment" />
</bag>
</class>
</hibernate-mapping>
mb_comment Map file
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="MiserBlogData" namespace="MiserBlogData.Entities">
<class name ="Comment" table="mb_comment">
<id name="Id" column ="comment_id">
<generator class ="native"/>
</id>
<property name ="ArticleId" column="comment_article_id" />
<property name ="Content" column="comment_content" />
<property name ="State" column="comment_state" />
<property name ="CDate" column="comment_cdate" />
<many-to-one name="Person" column="comment_user_id" class="A_Person" not-null="true" foreign-key="FK_PersonComment" />
</class>
</hibernate-mapping>
C# code
protected ISession _session = NHibernateHelper.GetCurrentSession();
public virtual object Save<T>(T obj) where T : class
{
return _session.Save(obj);
}
error :not-null property references a null or transient valueMiserBlogData.Entities.Comment.Person
I know,if I remove not-null="true" in mb_comment file ,system is good.But comment_user_id column is null.How to solve?
You should assign the property Person on your Comment to a valid A_Person instance. Then NHibernate will know which user id to write in the table.

Joining NHibernate Classes that share a common column but no foreign key

I have a couple of tables that I want to map to classes. The tables look like this:
Asset
---------
AssetId
AssetName
Product
---------
ProductId
ProductName
AssetId
Disposal
---------
DisposalId
AssetId
DisposalDate
Basically what I want to do is join the Product Table to the Disposal table on AssetId so that my Product has a collection of Disposals joined by asset. I have defined the following mapping but NHibernate (1.2) seems to ignore the key column defined in the bag and chooses to join the Product table to the Disposal table by ProductId (ie Product.ProductId = Disposal.AssetId). I'm not sure if this is a bug or if I'm not defining it properly but if anyone has a way to do this I'd be most greatful.
<class name="Product" table="Product" lazy="false">
<id name="ProductId" column="ProductId" type="int">
<generator class="native" />
</id>
<property name="ProductName" column="ProductName"/>
<bag name="Disposals" fetch="join" >
<key column="AssetId" foreign-key="AssetId/>
<many-to-many class="Disposal"/>
</bag>
</class>
Have you got your Disposals mapped to Products?
Your schema doesn't unique relate a Disposal to a Product. A Disposal can only relate to an Asset, not a Product.
You schema says to me that an Asset has many Products, and an Asset has many Disposals. There's nothing that says a Disposal is for a particular product.
The clean way:
<class name="Product" table="Product" lazy="false">
<id name="ProductId" column="ProductId" type="int">
<generator class="native" />
</id>
<property name="ProductName" column="ProductName"/>
<many-to-one name name="Asset" class="Asset" column="AssetId" />
</class>
<class name="Asset">
<id name="AssetId" >
<generator class="native" />
</id>
<property name="AssetName" />
<bag name="Disposals">
<key column="AssetId" />
<many-to-many class="Disposal" />
</bag>
</class>
foreign-key is used for DDL, I think it is the name of the foreign key constraint generated by schema export.
You can try property-ref, not completely sure if it works:
<class name="Product" table="Product" lazy="false">
<id name="ProductId" column="ProductId" type="int">
<generator class="native" />
</id>
<property name="ProductName" column="ProductName"/>
<property name="AssetId" />
<bag name="Disposals" fetch="join" >
<key column="AssetId" property-ref="AssetId/>
<one-to-many class="Disposal"/>
</bag>
</class>

NHibernate mapping of lookup collection (if that makes any sense)

I'm not sure how to ask the question, for I don't know what I don't know, and therefore I don't know the proper terminology for what I'm trying to get the answer to. I will explain my scenario, in hopes that it will help:
I've got three tables, a Book table, a Tag table and a BookTag lookup table.
Each book has an ID, a Title (for starters)
Each tag has an ID, and a Title
Each BookTag has an ID, a BookID, and a TagID.
A book can be tagged with multiple tags, and a tag can be used on more than one BookID.
I've got my objects setup in this fashion:
Book.cs
int BookID
string Title
List<BookTag> Tags
Tag.cs
int TagID
string Title
BookTag.cs
int ID
int BookID
int TagID
I would like the Books.cs class to have a collection of Tags, and not BookTags, but I cannot seem to get the mapping right in NHibernate. This is what I've got for the Book.hbm.xml file:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="DomainModel" namespace="DomainModel.Books">
<class name="DomainModel.Books.Book" table="Books">
<id name="BookID" type="Int32" unsaved-value="0">
<generator class="native"/>
</id>
<property name="Title" type="String" not-null="true"/>
<set lazy="true" name="Tags" table="BookTags" generic="true" inverse="true" cascade="delete">
<key column="BookID"/>
<one-to-many class="DomainModel.Books.BookTag, DomainModel"/>
</set>
</class>
</hibernate-mapping>
And this is my BookTag.hbm.xml:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="DomainModel" namespace="DomainModel.Books">
<class name="DomainModel.Books.BookTag" table="BookTags">
<id column="BookTagID" name="BookTagID" type="Int32" unsaved-value="0">
<generator class="native"/>
</id>
<many-to-one name="Tag">
<column not-null="true" name="TagID"/>
</many-to-one>
<many-to-one name="Book">
<column not-null="true" name="BookID"/>
</many-to-one>
</class>
</hibernate-mapping>
Under this model, I can get to the tag I want by using my object model: Book.Tags[0].Tag, but that just seems inefficient. Can I use NHibernate to map out the BookTags.TagID with the Tags.TagID in the database so that I can get Book.Tags[0] to return a Tag object, instead of a BookTags object? I didn't know of a better way to associate Books to tags so that a tag used on Book1 can be used on Book2 without adding a new entry to the Tags table.
I hope this makes at least some sense. Let me know if you need further clarification. I'll post my solution here if I figure it out before someone answers.
You don't need a BookTag class at all. You can map Book.Tags collection as many-to-many. To do this you will specify BookTag in the map to connect the association. Look here in section 6.8 Bidirectional Associations.
Thank you Tim, that was what I needed. For those that are curious, I "un-mapped" the BookTag table/objects, and now just have a Book object and a Tag object that are used and mapped to NHibernate.
My Book.hbm.xml was updated to this:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="DomainModel" namespace="DomainModel.Books">
<class name="DomainModel.Books.Book" table="Books">
<id name="BookID" type="Int32" unsaved-value="0">
<generator class="native"/>
</id>
<property name="Title" type="String" not-null="true"/>
<bag name="Tags" table="BookTag" generic="true">
<key column="BookID" on-delete="noaction"></key>
<many-to-many class="Tag" column="TagID"></many-to-many>
</bag>
</class>
</hibernate-mapping>

Categories

Resources