-->
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.  [ 7 posts ] 
Author Message
 Post subject: Error- NH tries to save child after saving the parent failed
PostPosted: Tue Jan 01, 2008 12:27 pm 
Newbie

Joined: Tue Nov 20, 2007 12:03 pm
Posts: 13
Hi,

In my project I have two tables:
Store (ID, StoreNum, Name, Location)
Item (ID, StoreID, Name, Price)

Store has a unique constraint on StoreNum column.
The Item's StoreID column references the Store table's ID (foreign key).

I use the following nhibernate mapping:

Item
<?xml version="1.0"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
namespace="SandlinksReaderDSPI.Model"
assembly="SandlinksModel">

<!-- Mappings for class 'Customer' -->
<class name="Item" table="items" lazy="false">

<!-- Identity mapping -->
<id name="ID">
<column name="ID" />
<generator class="native" />
</id>
<column name="Name"/>
<column name="Price"/>

<many-to-one name="Store" column="StoreID" ass="Store" not-null="true" />

</class>

</hibernate-mapping>

================

Store
<?xml version="1.0"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
namespace="SandlinksReaderDSPI.Model"
assembly="SandlinksModel">

<!-- Mappings for class 'Customer' -->
<class name="Store" table="observation" lazy="false">

<!-- Identity mapping -->
<id name="ID">
<column name="ID" />
<generator class="native" />
</id>

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

<bag name="Items" table="items"
inverse="true" cascade="all-delete-orphan">
<key column="StoreID" />
<one-to-many class="Item" />
</bag>
</class>

</hibernate-mapping>

=============================================

When I try to save a new store object that already has items in its list, everything workes great. The store is added and so are its items.
Loading, Updating and deleting a store / item works great as well.
But... This only works if the store doesn't exist in the DB already.

My project gets a text file of stores and itesm each day. Its goal is to iterate it, create store object, fill it with it's items and save it to the db.
A situation where the store I'm currently trying to save already exists in the DB (same StoreNum) is very common.
When I try to do
Code:
seesion.save(store)
, I get a justified exception:
Quote:
Violation of UNIQUE KEY constraint 'U_Store_StoreNum'. Cannot insert duplicate key in object 'dbo.store'. The statement has been terminated.


Since I'm not running in a transaction, In that case i'd like to continue to the next store and silently discard the error.
The problem is that when I finally do
Code:
session.Flush()
I get this nasty exception:

NHibernate.PropertyValueException: not-null property references a null or transient value: MyModel.Item.Store
at NHibernate.Impl.SessionImpl.CheckNullability(Object[] values, IEntityPersister persister, Boolean isUpdate)
at NHibernate.Impl.SessionImpl.DoSave(Object theObj, EntityKey key, IEntityPersister persister, Boolean replicate, Boolean useIdentityColumn, CascadingAction cascadeAction, Object anything)
at NHibernate.Impl.SessionImpl.DoSave(Object obj, Object id, IEntityPersister persister, Boolean useIdentityColumn, CascadingAction cascadeAction, Object anything)
at NHibernate.Impl.SessionImpl.SaveWithGeneratedIdentifier(Object obj, CascadingAction action, Object anything)
at NHibernate.Impl.SessionImpl.Save(Object obj)
at NHibernate.Impl.SessionImpl.SaveOrUpdate(Object obj)
at NHibernate.Engine.Cascades.CascadingAction.ActionSaveUpdateClass.Cascade(ISessionImplementor session, Object child, Object anything)
at NHibernate.Engine.Cascades.Cascade(ISessionImplementor session, Object child, IType type, CascadingAction action, CascadeStyle style, CascadePoint cascadeTo, Object anything)
at NHibernate.Engine.Cascades.CascadeCollection(CascadingAction action, CascadeStyle style, CollectionType collectionType, IType elemType, Object child, CascadePoint cascadeVia, ISessionImplementor session, Object anything)
at NHibernate.Engine.Cascades.Cascade(ISessionImplementor session, Object child, IType type, CascadingAction action, CascadeStyle style, CascadePoint cascadeTo, Object anything)
at NHibernate.Engine.Cascades.Cascade(ISessionImplementor session, IEntityPersister persister, Object parent, CascadingAction action, CascadePoint cascadeTo, Object anything)
at NHibernate.Impl.SessionImpl.PreFlushEntities()
at NHibernate.Impl.SessionImpl.FlushEverything()
at NHibernate.Impl.SessionImpl.Flush()
at MyDAL.MyDao.Flush()

It looks like Nhibernate tries to save the Item/s even though it failed earlier while trying to save the parent store.
I believed that in case NH fails to persist the parent object, it won't try to save the child elements - it makes no sense!
I know I can try to load a store with a given StoreNum and delete it if it exists, but this requiers executing 2 unnecessary sql statements (select & delete) for each store!
when a table is realy big it'll take too much time even when indexed.



Can someone review this and tell me if I'm doing something wrong?
Can I specifically tell NH to ignore chiled elements in this case, maybe by mapping differently?

Thanks,
Shaul


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jan 02, 2008 5:01 pm 
Expert
Expert

Joined: Fri May 13, 2005 11:13 am
Posts: 292
Location: Rochester, NY
First problem: you should be using transactions.

Second problem: it sounds like you are trying to create/update several stores in the same session. You should use a smaller unit of work. If this is a tight loop, you can use a single connection for all the sessions. And use transactions within your sessions.

In general, smaller sessions are more efficient, plus...

Third problem: when you encounter an exception from NH, you must discard the session. As you have discovered, when an exception occurs there is no guarantee of consistent session state. If you are using more granular sessions, you lose less work.

Fourth problem: Save() is not for updates. See http://www.hibernate.org/hib_docs/nhibe ... a-updating


Top
 Profile  
 
 Post subject: RE: Transactions kills performance...
PostPosted: Sun Jan 06, 2008 9:39 am 
Newbie

Joined: Tue Nov 20, 2007 12:03 pm
Posts: 13
Hi Marcal,
First of all - Tnx :)

1) Using transactions is something I'm trying to avoid at all cost since I'm dealing with processing +- 1500 new "Store"s with "Item"s per second ("Store" & "Item" are used as general example of heirarchy). Using transactions increases the time it takes to save an object and therefor significantelly reduces the objects per second ratio.

2) My algoritem is more or less like this:
Code:
Process(StoresList stores)
{
ISession s = factory.OpenSession();
foreach (Store store in stors)
{
s.Save(store);
}
s.Flush();
s.Close();
}

This function gets executed aprx 20 times per socond and each time it's supplied with a store list of -+ 70 stores in avg.
If I'll create a session for each store is storeList , won't it create a huge overhead? it opens a new db connection which is time consuming.

3) Regarding Exceptions throws from an ISession:
The temporary bypass I've created (not eficiant at all) is to catch the exception from the Save() function and execute session.Evict(store) on the store that just failed saving. This removes the store instance from the session and alows me to continue using the session as if nothing happend.

4) My code is not suppose to Update items since I don't now if an item already exists when I receive it from the external source - It might already be in the DB and it might not. Trying to load it first in order to determine if it exists adds too much overhead, thus not an option.
Anyway, only 3% off all incouming stores already exists so catching the exception from the contraint is OK in terms of cost vs. value.

========================================
I'de appreciate your reply regarding session managment (section 2) and any other remarks / tips.

Thanks,
Shaul

marcal wrote:
First problem: you should be using transactions.

Second problem: it sounds like you are trying to create/update several stores in the same session. You should use a smaller unit of work. If this is a tight loop, you can use a single connection for all the sessions. And use transactions within your sessions.

In general, smaller sessions are more efficient, plus...

Third problem: when you encounter an exception from NH, you must discard the session. As you have discovered, when an exception occurs there is no guarantee of consistent session state. If you are using more granular sessions, you lose less work.

Fourth problem: Save() is not for updates. See http://www.hibernate.org/hib_docs/nhibe ... a-updating
[/code][/list]


Top
 Profile  
 
 Post subject: Re: RE: Transactions kills performance...
PostPosted: Wed Jan 09, 2008 12:11 pm 
Expert
Expert

Joined: Fri May 13, 2005 11:13 am
Posts: 292
Location: Rochester, NY
It looks like you've wrangled some better performance out of NH with some non-standard uses, so here's my only suggestion:

Shaul wrote:
If I'll create a session for each store is storeList , won't it create a huge overhead? it opens a new db connection which is time consuming.


Yes, but you can use one connection across multiple sessions. Try this:

Code:
Process(StoresList stores)
{
IDbConnection conn = factory.ConnectionProvider.GetConnection()foreach (Store store in stors)
{
ISession s = factory.OpenSession( conn );
s.Save(store);
s.Flush();
s.Close();
}
conn.Close();
}


That way when your exception occurs, you can just discard the session (unit-of-work).


Top
 Profile  
 
 Post subject: Re: RE: Transactions kills performance...
PostPosted: Sun Jan 13, 2008 5:28 am 
Newbie

Joined: Tue Nov 20, 2007 12:03 pm
Posts: 13
Hi Marc,
Thanks for the idea, I'll use this "unit-of-work" concept instead of the evict since it's a more efficient way to handle the ISession's exceptions.

Still, my question remains the same:
Isn't NHibernate suppose to be "smart" enough to know that when an object's insert fails, all the dependent items (i.e. foreign key) shouldn't be persisted at all?

marcal wrote:
It looks like you've wrangled some better performance out of NH with some non-standard uses, so here's my only suggestion:

Shaul wrote:
If I'll create a session for each store is storeList , won't it create a huge overhead? it opens a new db connection which is time consuming.


Yes, but you can use one connection across multiple sessions. Try this:

Code:
Process(StoresList stores)
{
IDbConnection conn = factory.ConnectionProvider.GetConnection()foreach (Store store in stors)
{
ISession s = factory.OpenSession( conn );
s.Save(store);
s.Flush();
s.Close();
}
conn.Close();
}


That way when your exception occurs, you can just discard the session (unit-of-work).


Top
 Profile  
 
 Post subject: Re: RE: Transactions kills performance...
PostPosted: Mon Jan 14, 2008 12:15 pm 
Expert
Expert

Joined: Fri May 13, 2005 11:13 am
Posts: 292
Location: Rochester, NY
Shaul wrote:
Still, my question remains the same:
Isn't NHibernate suppose to be "smart" enough to know that when an object's insert fails, all the dependent items (i.e. foreign key) shouldn't be persisted at all?


Would that it were, but NHibernate instead takes a minimalist approach to handling: Run Away! (see http://www.hibernate.org/hib_docs/nhibe ... exceptions )


Top
 Profile  
 
 Post subject: Re: RE: Transactions kills performance...
PostPosted: Tue Jan 15, 2008 7:39 am 
Newbie

Joined: Tue Nov 20, 2007 12:03 pm
Posts: 13
good one :)
Well, since there seems to be no "correct" way to do what I want, I guess I'll stick to the run away approach, though I don't like it at all.

After all, A clever man knows how the get out of trouble, A Wise man knows how not to get in trouble in the first place..

Thanks for all of your replies!
Shaul

marcal wrote:
Shaul wrote:
Still, my question remains the same:
Isn't NHibernate suppose to be "smart" enough to know that when an object's insert fails, all the dependent items (i.e. foreign key) shouldn't be persisted at all?


Would that it were, but NHibernate instead takes a minimalist approach to handling: Run Away! (see http://www.hibernate.org/hib_docs/nhibe ... exceptions )
[/i]


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 7 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.