-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 5 posts ] 
Author Message
 Post subject: Should be simple (Insert Null into FK column)
PostPosted: Mon May 12, 2008 2:07 pm 
Newbie

Joined: Mon May 12, 2008 1:33 pm
Posts: 3
I have a Company obj that has a one-to-many relationship with a companyDetails obj via the CompanyID foreign key in my CompanyDetails table.

[Company Table]
CompanyID GUID (PK) (not null)


[CompanyDetail Table]
CompanyDetailsID GUID(PK) (not null)
CompanyID GUID (FK) (not null)

The current problem is that when I save the company object that contains a IList<CompanyDetail> NHibernate does the insert into the companyDetail table first before creating the company record. This results in a null value in the CompanyID for the CompanyDetail record and thus violates the foreign key constraint on the CompanyDetail table.

What am I doing wrong it's cascading the save but it's going backwards and inserting the CompanyDetails object before the Company Object which makes no sense based on how I understand the mappings to be set.

Note (when I remove the (non null) constraint on the CompanyID column in the CompanyDetail table and run the code a record is inserted in the CompanyDetail table without a CompanyID


Hibernate version:1.2.1.400

Mapping documents:
<?xml version="1.0"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
namespace="NHibernate.Licensing"
assembly="NHibernate.Licensing">

<!-- Mappings for class 'Customer' -->
<class name="Company" table="mt_Company" lazy="false">

<!-- Identity mapping -->
<id name="CompanyID">
<column name="CompanyID" />
<generator class="guid"/>
</id>

<bag name="CompanyDetails" cascade="all-delete-orphan">
<key column="CompanyID" />
<one-to-many class="CompanyDetail" />
</bag>

<!-- Simple mappings -->
<property name="Name" />

</class>

</hibernate-mapping>


<?xml version="1.0"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
namespace="NHibernate.Licensing"
assembly="NHibernate.Licensing">

<!-- Mappings for class 'CompanyDetail' -->
<class name="CompanyDetail" table="mt_CompanyDetail" lazy="false">

<!-- Identity mapping -->
<id name="CompanyDetailID">
<column name="CompanyDetailID" />
<generator class="guid"/>
</id>

<many-to-one name="Company" class="Company" column="CompanyID" cascade="all" not-null="true" />

<!-- Simple mappings -->
<property name="AddressLine1" />
<property name="AddressLine2" />
<property name="City" />
<property name="Phone" />
<property name="State" />
<property name="Zip" />

</class>

</hibernate-mapping>

Code between sessionFactory.openSession() and session.close():

// instantiate a new company object as well as a new companydetails object

Company co = new Company();
co.Name = "TestCompany1" + DateTime.Now;
co.CreatedOn = DateTime.Now;


// Create the Company Detail obj
CompanyDetail coDetail = new CompanyDetail();
coDetail.AddressLine1 = "Address one in da house";
coDetail.AddressLine2 = "Address two in da house";
coDetail.City = "Boise";
coDetail.State = "ID";
coDetail.Phone = "555-555-5555";
coDetail.Zip = "83709";


// Associate the CompanyDetails obj with the new company obj
co.CompanyDetails.Add(coDetail);



// save the company and as a result save the CompanyDetail Objects
_PersistenceManager.Save<Company>(co);



//PersistenceManager class
public void Save<T>(T item)
{
using (ISession session = m_SessionFactory.OpenSession())
{
using (session.BeginTransaction())
{
session.SaveOrUpdate(item);
session.Transaction.Commit();
}
}
}


Name and version of the database you are using:
SQL Server 2005


Top
 Profile  
 
 Post subject: Should be simple (Insert Null into FK column)
PostPosted: Mon May 12, 2008 3:34 pm 
Senior
Senior

Joined: Thu Jun 21, 2007 8:03 am
Posts: 127
Location: UK
Hi,

You need to manage the inverse relationship yourself.

First, set the inverse relationship:
Code:
coDetail.Company = co;


Then update the mapping to indicate to NHibernate that you'll handle this yourself:
Code:
<bag name="CompanyDetails" cascade="all-delete-orphan" inverse="true">


The reference for this is here:
http://www.hibernate.org/hib_docs/nhibernate/1.2/reference/en/html/example-parentchild.html#example-parentchild-collections

Regards,
Richard


Top
 Profile  
 
 Post subject:
PostPosted: Mon May 12, 2008 3:56 pm 
Newbie

Joined: Mon May 12, 2008 1:33 pm
Posts: 3
So what you're saying is that there isn't a way for hibernate to manage these types of associations? This is really surprising to me because I was under the impression that this was exactly what it was built to do.

If I have these relationships

Company
--CompanyDetail
--License
--LicenseDetail
--Orders


Where there is a one-to-many relationship from Company to CompanyDetail, License and Orders as well as a one-to-many relationship from License to LicenseDetail I should be able to create this tree from the bottom up and call save on the parent level Company object and have the inserts cascade up the tree and create all appropriate objects and id's.

I've been working from this example http://www.codeproject.com/KB/database/ ... imple.aspx

And in this example the author manages to make this same type of relationship work without an error, and so I believe it's possible I'm just doing something wrong.


Top
 Profile  
 
 Post subject:
PostPosted: Mon May 12, 2008 4:31 pm 
Newbie

Joined: Mon May 12, 2008 1:33 pm
Posts: 3
I made some changes to my code and how I am populating the Company object. I am now doing this:

I have a many-to-many relationship between Company and License, otherwise all the relationships are one-to-many

-------------------------------------------
// Associate the CompanyDetails obj with the new company obj
coDetail.Company = co;
co.CompanyDetails.Add(coDetail);

// associate the product details with the license
pd.License = li;
li.ProductDetails.Add(pd);

// Associate the license with the company
li.Companies.Add(co);
co.Licenses.Add(li);


// assign this new co obj to the current working obj of this system.
Companies.Add(co);

session.SaveOrUpdate(co);


------------------------------------------
This seems extremely verbose to me is there anything I'm doing wrong or could be done with less code?


Top
 Profile  
 
 Post subject: Should be simple (Insert Null into FK column)
PostPosted: Tue May 13, 2008 4:55 am 
Senior
Senior

Joined: Thu Jun 21, 2007 8:03 am
Posts: 127
Location: UK
Hi,

This is consistent with the documentation, which states (in section 17.2):
Quote:
...
NHibernate would issue two SQL statements:

an INSERT to create the record for c

an UPDATE to create the link from p to c

This is not only inefficient, but also violates any NOT NULL constraint on the parent_id column.


Adding the code to set the parent property prevents the two-stage update (remember to set your inverse="true" property on your bag mapping). NHibernate can track the inverse relationship for you, but it uses the two-stage update described above, so your foreign-key needs to be nullable.

Quote:
This seems extremely verbose to me ...


Unless I'm missing something, there's only one extra line of code required to maintain the inverse relationship (coDetail.Company = co;). This is hardly 'extremely verbose'.

NHibernate's job is to persist (and restore) objects the same way that they would be held in memory. If you were writing the above code without a database and running entirely in memory, the same code to populate the inverse relationship would be required.

Hope that helps to clarify.

Regards,
Richard


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 5 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.