Nhibernate: distinct results in second level Colle

2019-04-11 22:08发布

问题:

I have an object model like this:

   class EntityA
   {
        ...
        IList<EntityB> BList;
        ...
   }

   class EntityB
   {
       ...
       IList<EntityC> CList;
   }

I have to fetch all the colelctions (Blist in EntityA and CList in EntityB), because if they all will be needed to make some operations, if i don't eager load them i will have the select n+1 problem. So the query was this:

  select a from EntityA a left join fetch a.BList b left join fetch b.CList c

The fist problem i faced with this query, was the return of duplicates from the DB, i had EntityA duplicates, because of the left join fetch with BList. A quick read through the hibernate documentation and there were some solutions, first i tried the distinct keyword that supposelly wouldn't replicate the SQL distinct keyword except in some cases, maybe this was one of those cases because i had a SQL error saying that i cannot select distict text columns (column [Observations] in EntityA table). So i used one of the other solutions:

  query.SetResultTransformer(new DistinctRootEntityResultTransformer());

This worked fine. But the result of the operations were still not passing the tests. I checked further and i found out that now there were duplicates of EntityB, because of the left join fetch with CList.

The question is, how can i use the distinct in a second level collection? I searched and i only find solutions for the root entity's direct child collection, but never for the second level child collections...

Thank you for your time

回答1:

You cannot accomplish what you want in a simple manner. I believe that it's the left join fetch of C that causes the duplicates. The generated query is something like this:

select a 
left join a.b b
left join b.c c

and the rows returned by the db will be like this:

1: a1 | b1 | c1
2: a1 | b1 | c2
3: a1 | b2 | c3
4: a1 | b2 | c4
...

Althouh you already have filtered the root entity duplicates (the A's), due to the outer join on C, the DB needs to return repeated entries for the B table for each of C's entry. A simple way to solve this is to filter the items through a utility collection. Depending on your Entity requirements, you may also use a HashSet so it automatically filters them out.



回答2:

Use an ISet instead of an IList (and map it as a set offcourse, instead as a bag).

A Set does not allow duplicate entities.



回答3:

I encountered the same problem and was not able to solve the duplicate issue through hql. However, I created IEqualityComparer for all collections and did Disinct() on each Collection to eliminate duplicates on top of the hql result.