-->
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.  [ 13 posts ] 
Author Message
 Post subject: Problem with <list> collections
PostPosted: Wed May 09, 2007 9:53 pm 
Newbie

Joined: Thu Apr 12, 2007 10:35 pm
Posts: 11
Hi all,

(Using NHibernate 1.2)

Am having trouble with excessive database activity. Have tracked the problem down to uni-directional lists (with cascade) that are generating an INSERT followed by an UPDATE for every element in the list. After investigation, this appears to be an NHibernate problem.

The following mapping is from the NHibernate GenericTest\ListGeneric\Simple test.

Code:
<class name="A" table="a" lazy="false">
   <id name="Id" column="id" unsaved-value="null">
      <generator class="native" />
   </id>
   <property name="Name" column="aname" />
   <list name="Items" cascade="all-delete-orphan" generic="true">
      <key column="a_id" />
      <index column="a_idx" />
      <one-to-many class="B" />
   </list>
</class>
<class name="B" table="b" lazy="false">
   <id name="Id" column="id" unsaved-value="null">
      <generator class="native" />
   </id>
   <property name="Name" column="aname" />
</class>


This is then exercised with the following code:
Code:
A a = new A();
a.Name = "first generic type";
a.Items = new List<B>();
B firstB = new B();
firstB.Name = "first b";
B secondB = new B();
secondB.Name = "second b";

a.Items.Add(firstB);
a.Items.Add(secondB);


This results in the following five SQL statements:
Code:
INSERT INTO a (aname) VALUES (@p0); select SCOPE_IDENTITY(); @p0 = 'first generic type'
INSERT INTO b (aname) VALUES (@p0); select SCOPE_IDENTITY(); @p0 = 'first b'
INSERT INTO b (aname) VALUES (@p0); select SCOPE_IDENTITY(); @p0 = 'second b'
UPDATE b SET a_id = @p0, a_idx = @p1 WHERE id = @p2; @p0 = '1', @p1 = '0', @p2 = '1'
UPDATE b SET a_id = @p0, a_idx = @p1 WHERE id = @p2; @p0 = '1', @p1 = '1', @p2 = '2'


Am I missing something or is this a serious problem with NHibernate? This is doubling the database activity for virtually every insert. Conceptually it also makes no sense since, when inserting a B, NHibernate knows both the Id of the parent A and the index value.

Why is it doing an Update, rather than passing all B columns in the Insert?

(My whole application is using lists rather than bags to ensure sequence is respected!!)


Top
 Profile  
 
 Post subject:
PostPosted: Thu May 10, 2007 2:11 am 
Expert
Expert

Joined: Thu Dec 14, 2006 5:57 am
Posts: 1185
Location: Zurich, Switzerland
You have do define a "parent" mapping in your child class and set the collection mapint to inverse="true", see chapter 17 in the reference documentation:

Code:
<class name="A" table="a" lazy="false">
   ...
   <list name="Items" cascade="all-delete-orphan" generic="true">
      ...
      <one-to-many class="B" inverse="true"/>
   </list>
   ...
</class>
<class name="B" table="b" lazy="false">
   ...
   <many-to-one name="A" column="a_id" not-null="true"/>
   ...
</class>


Hope that helps,
Wolfgang


Top
 Profile  
 
 Post subject:
PostPosted: Thu May 10, 2007 2:26 am 
Newbie

Joined: Thu Apr 12, 2007 10:35 pm
Posts: 11
Thanks for the reply but that's not going to work.

The documentation says quite clearly you cannot use a <list> with a bidirectional association, which is what I would be creating with your suggestion.

The schema is from the NHibernate Unit Tests.


Top
 Profile  
 
 Post subject:
PostPosted: Thu May 10, 2007 2:40 am 
Expert
Expert

Joined: Thu Dec 14, 2006 5:57 am
Posts: 1185
Location: Zurich, Switzerland
Sorry, missed that one. Seems that I always used bags accidentally. Can you switch to a set and use "order-by" instead of mapping an index value ?


Top
 Profile  
 
 Post subject:
PostPosted: Thu May 10, 2007 3:05 am 
Newbie

Joined: Thu Apr 12, 2007 10:35 pm
Posts: 11
It's probably no accident you use bags. From what I can figure out, it looks like bags and sets are the only child collections that work properly...

Sets with 'order-by' may be a work-around but it means polluting my domain model with stuff that should be handled by the ORM.

Have now trawled through the NHibernate unit tests and domain model libraries and cannot find a single working example of a <list> collection of <one-to-many> objects that does not require some sort of domain-model hack to work properly.

This is starting to look like a major concern for our project. May be better off waiting for DLINQ...


Top
 Profile  
 
 Post subject:
PostPosted: Fri May 18, 2007 12:27 am 
Regular
Regular

Joined: Wed Oct 25, 2006 10:51 pm
Posts: 71
I thought the quick answer is that you need to add "insert=false" and "update=false" in class B.


Last edited by PandaWood on Sat Jul 12, 2008 11:17 am, edited 1 time in total.

Top
 Profile  
 
 Post subject:
PostPosted: Fri May 18, 2007 9:25 am 
Senior
Senior

Joined: Thu Feb 09, 2006 1:30 pm
Posts: 172
pdegenhardt wrote:
Sets with 'order-by' may be a work-around but it means polluting my domain model with stuff that should be handled by the ORM.

Have now trawled through the NHibernate unit tests and domain model libraries and cannot find a single working example of a <list> collection of <one-to-many> objects that does not require some sort of domain-model hack to work properly.

This is starting to look like a major concern for our project. May be better off waiting for DLINQ...


Now, Are you sure there are really excessive database calls? Why is it excessive? I believe it is clear that it is unnecessary at this point, but is it really excessive? The performance difference should be minimal to non-existant.

This behavior has bothered me for a long time with NHibernate, where it will insert with an incorrect key and then update the key to the correct value. I always assumed it had to do with support for changing items in lists. Meaning that Maybe you are deleting one parent, and moving the child to a different parent. Maybe it would remove all children, then delete parent A then update the children to parent B. I really can't explain why it works this way beyond that.

The biggest problem I have is that I cannot mark a foreign key with a not null constraint when there should never be a child without a parent. This offends me, but I get by because NHibernate offers so many numerous benefits in other areas.

As for waiting for DLINQ, that's just silly. Microsoft has tried before, and they don't seem to deliver. Even so aren't we talking about LINQ to Entities (or whatever they call it now) and not really DLINQ?

Microsoft has crippled their product for the first release. An NHibernate competitor has been removed from the Orcas release (a basic ORM is there however) meaning it won't be a defacto standard for new development at that time. Even so, who is to say it won't be filled with more bugs? Why should we just blindly trust that microsoft will do better than NHibernate? So far I've been very pleased overall with the stability, performance and general features of NHibernate.


Top
 Profile  
 
 Post subject:
PostPosted: Fri May 18, 2007 10:50 am 
Newbie

Joined: Thu Apr 12, 2007 10:35 pm
Posts: 11
NHibernate offers some wonderful functionality but this is one of a few areas where it seems to fall short of the mark required of enterprise-level applications.

To me doubling the number of database calls for what will be frequently executed processes is excessive and something on which I would reluctant to build an enterprise-scale application.

As Wolli pointed out, I could change it to a bag and avoid the excessive calls with an inverse="true" attribute, with the relation managed at the other end. This would also fix the problem you mention about not-null constraints on foreign-keys. All this works beautifully, but not for Lists or Maps, which can only be uni-directional.

So if I want an efficient database layer, I need to re-work my domain model to workaround NHibernate's lack of support for collections with List or Map semantics. This greatly undermines the entire reason for using an ORM in the first place - to de-couple the various layers of an enterprise-scale app.

I guess the frustrating part of all this is that I am a believer in NHibernate and can see the huge benefits it offers. But Lists and Maps are a big disappointment. The documentation talks much about the theory and then ends it all with a small notice that they can't be bi-directional. It kind of gets you all juiced up and then you try to actually utilise them and you find nothing quite works or doesn't work efficiently...

This may of course, all simply be because I have missed something totally obvious. I would love to be proven wrong and for someone to explain how/where they are using Lists and Maps in a real, production app.
Anyone?


Top
 Profile  
 
 Post subject:
PostPosted: Fri May 18, 2007 10:56 am 
Senior
Senior

Joined: Thu Feb 09, 2006 1:30 pm
Posts: 172
pdegenhardt wrote:
NHibernate offers some wonderful functionality but this is one of a few areas where it seems to fall short of the mark required of enterprise-level applications.

To me doubling the number of database calls for what will be frequently executed processes is excessive and something on which I would reluctant to build an enterprise-scale application.


See what do you mean by "enterprise-level" though? A 150GB+ transactional database? Thousands of active users on a daily basis? Used by employees and customers across the globe?

That is the current environment we use NHibernate for. The key is that often times people get caught up with theoretical performance bottlenecks. While I don't like this particular implementation, I do not believe it is a performance killer. I think all too often people think the wrong thing will be the bottleneck. Very often it turns out to be of minor importance and there is a far bigger bottleneck somewhere else.

I'm not saying it doesn't matter, it does. I'm just surprised it would be that kind of a deal breaker.


Top
 Profile  
 
 Post subject:
PostPosted: Fri May 18, 2007 1:14 pm 
Regular
Regular

Joined: Wed Oct 25, 2006 10:51 pm
Posts: 71
jchapman wrote:

Why is it excessive? I believe it is clear that it is unnecessary at this point, but is it really excessive?

If it's not necessary, it's excessive. Don't make me pull out the dictionary... ;-)


Top
 Profile  
 
 Post subject:
PostPosted: Fri May 18, 2007 1:29 pm 
Contributor
Contributor

Joined: Wed May 11, 2005 4:59 pm
Posts: 1766
Location: Prague, Czech Republic
Inverse lists and maps are not supported because since they are inverse, the changes to the collection actually don't matter and it's the contained items that matter. Now if you have to update such a contained item, you have no way of knowing, just by looking at it, which list index or map key it is located under in the parent.

Hibernate3 solves this by looking through all collections of the appropriate role in the session and asking them to look up the item. As you can see, it's not exactly a fast operation, but it is convenient so I'm going to implement this in NHibernate at some point in the future.


Top
 Profile  
 
 Post subject:
PostPosted: Fri May 18, 2007 1:42 pm 
Regular
Regular

Joined: Wed Oct 25, 2006 10:51 pm
Posts: 71
Sorry, my mapping suggestion was a bit off the nut... I meant to put the insert=false and update=false in the many-to-one element.

I haven't checked what the doco says, but I thought you could do a bidirectional list. Not the same way as bidirectional bags, but doable.

Code:
<class name="B" table="b" lazy="false">
   <id name="Id" column="id" unsaved-value="null">
      <generator class="native" />
   </id>
   <property name="Name" column="aname" />
   <many-to-one name="a" column="a_id" class="A" not-null="true" insert="false" update="false" />
</class>


Even if you don't do this, I'm sure you need to add 'not=null="true" to your 'key' attribute in class A. Hibernate needs the hint to order insert and update statements correctly to avoid constraint violations. At least this is what the bible says "Java Persistence with Hibernate" pages 292,293.

But I'm probably confusing Hibernate and NHibernate functionality.


Top
 Profile  
 
 Post subject:
PostPosted: Fri May 18, 2007 2:46 pm 
Senior
Senior

Joined: Thu Feb 09, 2006 1:30 pm
Posts: 172
PandaWood wrote:
jchapman wrote:

Why is it excessive? I believe it is clear that it is unnecessary at this point, but is it really excessive?

If it's not necessary, it's excessive. Don't make me pull out the dictionary... ;-)


"mean going beyond a normal limit. EXCESSIVE implies an amount or degree too great to be reasonable or acceptable"
from Merrian-Webster
http://www.m-w.com/cgi-bin/dictionary?sourceid=Mozilla-search&va=excessive

So what I'm saying is I don't believe it is unreasonable, even though it isn't strictly necessary.


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