16

When doing a query like this (using Nhibernate 2.1.2):

 ICriteria criteria = session.CreateCriteria<MyRootType>()
                .SetFetchMode("ChildCollection1", FetchMode.Eager)
                .SetFetchMode("ChildCollection2", FetchMode.Eager)
                .Add(Restrictions.IdEq(id));

I am getting multiple duplicate objects in some cartesian fashion. E.g. if ChildCollection1 has 3 elements, and ChildColection2 has 2 elements then I get results with each element in ChildColection1 one duplicated, and each element in ChildColection2 triplicated! This was a bit of a WTF moment for me...

So how to do this correctly?

  • Is using SetFetchMode like this only supported when specifying one collection?
  • Am I just using it wrong (I've seen some references to results transformers, but imagined this would be simplier).
  • Is this something that's different in NH3?

Update:

As per Felice's suggestion, I tried using the DistinctRootEntity transformer, but this is still returning duplicates. Code:

ICriteria criteria = session.CreateCriteria<MyRootType>()
                .SetFetchMode("ChildCollection1", FetchMode.Eager)
                .SetFetchMode("ChildCollection2", FetchMode.Eager)
                .Add(Restrictions.IdEq(id));

 criteria.SetResultTransformer(Transformers.DistinctRootEntity);

 return criteria.UniqueResult<MyRootType>();
2
  • still no way to do this ? Commented Aug 20, 2013 at 9:19
  • Check if this works (I have only tested it with QueryOver on NH 3, but it seems to be the same): stackoverflow.com/a/19717548/806975 Commented Oct 31, 2013 at 22:19

5 Answers 5

15

You can select single distinct results by specifying SetResultsTransformer(Transformers.DistinctRootEntity);

Sign up to request clarification or add additional context in comments.

10 Comments

Thanks. I'll take a look at that. Do you know why this is not the default behaviour? Who would want the duplicates?!
This is the same question I asked when I faced your problem. Not a real answer, but let's think what happen in the query side: the resulting recordset will contain duplicates, and NH mimic this background behavior. In any case NH internally uses the same entity, they are only duplicated entries into the returned list.
@Felice Pollano, just added the resultstransformer. Unfortunately this did not change anything for me - I'm still getting the duplicates. Perhaps this is down to the type of collection?
DistinctRootEntity only applies to root entities, which is not the problem here.
@FrancoisBotha: It also works for me as long as I only have one join, the moment I have 3 joins it stops working.
|
11

You're doing a cartesian product here. Don't. Instead, fetch each collection separately.

BTW: this isn't something NHibernate-specific, the same applies to any ORM in any platform, or even pure SQL without any ORM at all. In general, you don't want to fetch N*M rows when you can fetch N+M instead.

3 Comments

What if you were fetching 1000 entries instead of a unique result? Surely two JOINs (Cartesian product) in a single SQL query is more efficient than 2001 selects, even if it returns more rows (this depends in part on how large the collections are).
I may have jumped the gun--you are right in most cases. It probably makes sense to issue two queries, one with a single join.
I used the Future methode to fix this, NH did the rest for me.
3

I am not using NHibernate (rather, regular 'ol Hibernate), but in the Java version of Hibernate you can define One-To-Many collections as either Lists or Sets. If you define them as Sets you will not get duplicates. Surprisingly enough, this happens without overriding equals(). Unfortunately, I have the same problem and I want Lists so I can integrate with Wicket but no duplicates...

Comments

3

What were you mapping your collection as? If your using a bag for example you will get duplicates. You can use a set instead and you won't get duplicates.

Comments

2

Try using Futures:

ICriteria criteriaFuture1
    = session.CreateCriteria<MyRootType>()
        .SetFetchMode("ChildCollection1", FetchMode.Eager)
        .Add(Restrictions.IdEq(id))
        .SetResultTransformer(Transformers.DistinctRootEntity)
        .FutureValue<MyRootType>();
 ICriteria criteriaFuture2
    = session.CreateCriteria<MyRootType>()
        .SetFetchMode("ChildCollection2", FetchMode.Eager)
        .Add(Restrictions.IdEq(id))
        .SetResultTransformer(Transformers.DistinctRootEntity)
        .FutureValue<MyRootType>();

 return criteriaFuture1.Value;

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.