History | Log In     View a printable version of the current page.  
Issue Details (XML | Word | Printable)

Key: HB-520
Type: New Feature New Feature
Status: Open Open
Priority: Minor Minor
Assignee: Unassigned
Reporter: Stephen Owens
Votes: 60
Watchers: 45
Operations

If you were logged in you would be able to see more operations.
Hibernate2

Add Distinct to Criteria API

Created: 04/Dec/03 07:08 AM   Updated: 04/Dec/06 10:56 AM
Component/s: core
Affects Version/s: 2.1 rc1
Fix Version/s: None

Time Tracking:
Not Specified

Environment: Hibernate 2.1+, (Any DB)
Issue Links:
Duplicate
 
Relates
 


 Description  « Hide
Need the ability to specify that results of a Criteria query should be 'DISTINCT', especially when joining to a collection. So the Criteria equivalent of the HQL statement

"select distinct parent from Parent as parent join Parent.children as child where child.age>5"

 All   Comments   Work Log   Change History      Sort Order: Ascending order - Click to sort in descending order
Michael Greer - 13/Dec/03 09:14 AM
<p>I believe this is similar to <a href="http://opensource.atlassian.com/projects/hibernate/secure/ViewIssue.jspa?key=HB-123">Apply distinctness to HQL fetch of collection</a>, but I voted for this anyway.</p>
<p>The return of duplicates in an entity list if you eagerly fetch their children is unexpected behavior which, while easily remedied in the app, perhaps does not belong in this layer. I understand that adding "DISTINCT" to the query would be a major performance hit, so why not handle it programatically after returning the results? We have to handle it in any case, since it is an unwieldy and unexpected result, so we might as well hide it in the proper layer.</p>

Michael Greer - 13/Dec/03 09:15 AM
Duplicate. I still think this is important, however.

John Ramkawsky - 16/Aug/05 01:21 PM
It seems you may be able to accomplish what you want by using
 setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY).

See: http://www.hibernate.org/hib_docs/api/net/sf/hibernate/Criteria.html#setResultTransformer(net.sf.hibernate.transform.ResultTransformer)

Artti Jaakkola - 29/Aug/05 06:22 AM
Problem with setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY) is that it's done only after the query is executed.

If you happen to have criteria.setMaxResults(10) and first 8 items equal with each other, then you get only 3 items as a result:

List results = session
    .createCriteria(User.class)
    .setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY)
    .setMaxResults(itemCount.intValue())
    .list();

Now I don't know if I should fetch all the items from database and then just cut the first 10 or what...

Erick Dovale - 19/Oct/05 08:22 PM
Another problem related to using setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY) happens when using pagination. The counting the results is fairly easy and works fine even when counting distinct results, however, the count won't match the actual results if setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY) is used afterward.

Bill Burke - 05/Jan/06 03:33 PM
Any decision on this matter? I'm writing OReilly EJB3 book and need to know sooner or later. Couldn't this be as simple as taking the results and putting them into a LinkedhashMap and then doing a new List(map.keySet());?

Caleb Phillips - 05/Jan/06 03:55 PM
Would the results of that be any different from using the ResultsTransformer? I had the same issue as mentioned above - I need the distinct to be applied before the maxResults and since the maxResults is accomplished in the database (at least in the Oracle implementation), it seems like the distinct would have to be done there too.

Brad Koehn - 31/Jan/06 04:00 PM
If you're only using the joined criteria for restricting the list of entities returned, there's no need to fetch the columns of the joined entity at all.

Pavel Grushetzky - 26/Apr/06 09:04 AM
It would be nice if hibernate could fetch associations this way
1. Select root entities, apply max rows limit
2. In one shot select required associations with something like "WHERE root_entity_id IN (....)"

Sylvain Tremblay - 04/May/06 08:46 AM
I have been working on a new Projection called RootProjection which can be used to solve this problem.
This projection can be compared to the PropertyProjection but all fields from the Root criteria are returns.

You can then set the projection on the Criteria and chain it with the Distinct Projection:

Criteria myCriteria = mySession.createCriteria(MyRootClass.class);
myCriteria.createCriteria("association1").createCriteria("association2)......add(Restrictions.eq("someField", "someValue");

myCriteria.setProjection(Projections.distinct(Projections.RootProjection()));


The select generated will then look like this:

select root.field1, root.field2, .... from root inner join association1......inner join associations2........where..........


Then you use the myCriteria.list() to get DISTINCT root entities.

However to make this work minor changes needs to be done in Hibernate.
I will be submitting a patch so this gets integrated in future version.

Sylvain

Paul Malolepsy - 06/Sep/06 06:09 PM
Has there been any more progress on this one?

The only "workaround" for this that I've been able to come up with is to issue two queries from the criteria same criteria object. The first one gets the id's the second one is contrained to the ids.

//set up crtieria as you wish, including pagination
myCriteria = doStuffToSetupCriteria();
myCriteria.setFirstResult((page-1)*itemsPerPage);
myCriteria.setMaxResults(itemsPerPage);

//get the list if primary keys
myCriteria.setProjection(Projections.distinct(Projections.property("myAllias.id"));
List ids = gacc.list();

//now add the id's into the restriction
myCriteria.add(Restrictions.in("myAlias.id, ids));

//clean up from the last critiera run
gacc.setProjection(null);
gacc.setFirstResult(0);
gacc.setMaxResults(Integer.MAX_VALUE);

//your results
List objects = gacc.list()


A little hacky I agree, but the only acceptable soltion I could find given this limitiation.


Daniel Poirier - 04/Dec/06 10:56 AM
Can someone explain to me how come this issue still unresolved? In my mind it is not a trivial one. What happened to the solution provided by Sylvain Tremblay?