Why do I need SetResultTransformer DistinctRootEntity when using FetchMode.Eager?

715 views
Skip to first unread message

Rui Lopes

unread,
May 27, 2010, 2:06:22 PM5/27/10
to nhusers
Hi,

I'm puzzled about why do I need to call SetResultTransformer on this
snippet:

var products = session.CreateCriteria<Product>()
.SetFetchMode("Descriptions", FetchMode.Eager)
// TODO why do I need this?
.SetResultTransformer(Transformers.DistinctRootEntity)
.List<Product>();

If I don't do that "products" will end up with duplicated Product's
instances.

My test data is as follow. There is one Product tuple, which has two
ProductDescription tuples. something like:

--select * from product
id price
1 2000.00

--select * from productdescription
id productId lang name
1 1 en product 1089eefe-ad8e-4835-b817-7d1e37db0574 (en)
2 1 pt product 1089eefe-ad8e-4835-b817-7d1e37db0574 (pt)

--select * from product as p inner join productdescription as d on
p.id=d.productId
id price id productId lang name
1 2000.00 1 1 en product 1089eefe-ad8e-4835-b817-7d1e37db0574 (en)
1 2000.00 2 1 pt product 1089eefe-ad8e-4835-b817-7d1e37db0574 (pt)


While running the nhibernate query, I would expect it to return just
one Product instance, but it returns two. Why?


I'm using these (fluent) mappings:

public class ProductDescriptionMap :
ClassMap<ProductDescription>
{
public ProductDescriptionMap()
{
Id(x => x.Id)
.Column("id");
...
References(x => x.Product)
.Not.Nullable()
.Column("productId");
}
}

public class ProductMap : ClassMap<Product>
{
public ProductMap()
{
Id(x => x.Id)
.Column("id");
...
HasMany(x => x.Descriptions)
.KeyColumn("productId")
.Cascade.All();
}
}


What I'm doing wrong?

If this is the expected behavior, can you point me to the rationale
behind it?

Please, see the whole code at http://pastie.org/980393

TIA!

Best regards,
Rui Lopes

Diego Mijelshon

unread,
May 27, 2010, 9:58:04 PM5/27/10
to nhu...@googlegroups.com
Easy. A query that includes joined child entities returns many copies of the parent, as you saw in your query:
id      price   id      productId       lang    name
1       2000.00 1       1       en      product 1089eefe-ad8e-4835-b817-7d1e37db0574 (en)
1       2000.00 2       1       pt      product 1089eefe-ad8e-4835-b817-7d1e37db0574 (pt)

Now, NHibernate returns an item in the result for each row returned by the query, even though both will effectively be the same Product instance, thanks to the identity map.


I believe I mention that article at least 4 times a week, between the list and Stackoverflow :-)

   Diego


--
You received this message because you are subscribed to the Google Groups "nhusers" group.
To post to this group, send email to nhu...@googlegroups.com.
To unsubscribe from this group, send email to nhusers+u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/nhusers?hl=en.


Rui Lopes

unread,
May 28, 2010, 4:05:22 AM5/28/10
to nhusers
On May 28, 2:58 am, Diego Mijelshon <di...@mijelshon.com.ar> wrote:
> Easy. A query that includes joined child entities returns many copies of the
> parent, as you saw in your query:
> id      price   id      productId       lang    name
> 1       2000.00 1       1       en      product
> 1089eefe-ad8e-4835-b817-7d1e37db0574 (en)
> 1       2000.00 2       1       pt      product
> 1089eefe-ad8e-4835-b817-7d1e37db0574 (pt)
>
> Now, NHibernate returns an item in the result for each row returned by the
> query, even though both will effectively be the same Product instance,
> thanks to the identity map.

Humm, when is it useful to return multiple Product instances?


> A better way to do eager collection loading is detailed by Oren here:http://ayende.com/Blog/archive/2010/01/16/eagerly-loading-entity-asso...

From my (newbie) NH user viewpoint, I don't see why it's better. I
mean, I have to be bothered with too much detail "just" for doing a
eager loading. Nonetheless, I will try it. Thanks!


> I believe I mention that article at least 4 times a week, between the list
> and Stackoverflow :-)

Humm, that seems a sign that maybe there is something odd with the
default eager loading behavior? It surely triggered my POLA :-(

Thanks for the insights Diego!

Best regards,
Rui Lopes
> > Please, see the whole code athttp://pastie.org/980393
>
> > TIA!
>
> > Best regards,
> > Rui Lopes
>
> > --
> > You received this message because you are subscribed to the Google Groups
> > "nhusers" group.
> > To post to this group, send email to nhu...@googlegroups.com.
> > To unsubscribe from this group, send email to
> > nhusers+u...@googlegroups.com<nhusers%2Bunsu...@googlegroups.com >
> > .

Diego Mijelshon

unread,
May 28, 2010, 9:13:25 AM5/28/10
to nhu...@googlegroups.com
On Fri, May 28, 2010 at 05:05, Rui Lopes <r...@ruilopes.com> wrote:
On May 28, 2:58 am, Diego Mijelshon <di...@mijelshon.com.ar> wrote:
> Easy. A query that includes joined child entities returns many copies of the
> parent, as you saw in your query:
> id      price   id      productId       lang    name
> 1       2000.00 1       1       en      product
> 1089eefe-ad8e-4835-b817-7d1e37db0574 (en)
> 1       2000.00 2       1       pt      product
> 1089eefe-ad8e-4835-b817-7d1e37db0574 (pt)
>
> Now, NHibernate returns an item in the result for each row returned by the
> query, even though both will effectively be the same Product instance,
> thanks to the identity map.

Humm, when is it useful to return multiple Product instances?

Not in this case, that's clear.
But this is because we are working with an IList<SomeEntity>.
If we were using projections for listing/reporting purposes, joins would have a different meaning.
 


> A better way to do eager collection loading is detailed by Oren here:http://ayende.com/Blog/archive/2010/01/16/eagerly-loading-entity-asso...

From my (newbie) NH user viewpoint, I don't see why it's better. I
mean, I have to be bothered with too much detail "just" for doing a
eager loading. Nonetheless, I will try it. Thanks!

It's better because it gets you the result you are expecting in a more performant way.
Is it easier? no. More intuitive? no.
Can you write a wrapper for that and contribute it to the community? Yes :-)
 
> I believe I mention that article at least 4 times a week, between the list
> and Stackoverflow :-)

Humm, that seems a sign that maybe there is something odd with the
default eager loading behavior? It surely triggered my POLA :-(

Well... the thing with eager loading is that it's the wrong approach in 90% of the cases, especially for collections.
Using batch-size is usually better and it does not come with any of the problems that join fetching has.
 
    Diego

John Davidson

unread,
May 28, 2010, 9:27:31 AM5/28/10
to nhu...@googlegroups.com
>Well... the thing with eager loading is that it's the wrong approach in 90% of the cases, especially for collections.
>Using batch-size is usually better and it does not come with any of the problems that join fetching has.

Remember though that not all databases support batch-size, Oracle for instance.

John Davidson

Diego Mijelshon

unread,
May 28, 2010, 9:37:01 AM5/28/10
to nhu...@googlegroups.com
I'm not referring to adonet.batch_size (used for updates), but to the batch-size attribute used in mappings (for loading), which _IS_ supported by all DBs.

   Diego


--
You received this message because you are subscribed to the Google Groups "nhusers" group.
To post to this group, send email to nhu...@googlegroups.com.
To unsubscribe from this group, send email to nhusers+u...@googlegroups.com.

John Davidson

unread,
May 28, 2010, 11:21:53 AM5/28/10
to nhu...@googlegroups.com
Thanks Diego,

I thought they were the same, based on adonet.batch_size.

I may be able to get another level of optimization without caching then.

John Davidson
Reply all
Reply to author
Forward
0 new messages