-->
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.  [ 5 posts ] 
Author Message
 Post subject: Criteria API and distinct entities
PostPosted: Tue Dec 20, 2005 4:05 pm 
Newbie

Joined: Wed Mar 03, 2004 11:22 am
Posts: 12
Hibernate version: 3.1

My current problem is that I have a query builder that uses the Criteria API to construct a query based on user input. My Criteria query joins to several other tables and specifies a max results. An example of a typical query in HQL that is impossible with the Criteria API is:
Code:
Query query = session.createQuery( "select distinct entity from MyEntity entity join entity.children child where child.name is not null" );
query.setMaxResults( 10 );
query.list();


In HQL, the previous query will select 10 distinct entities that have children with a non-null name. With the Criteria API, there are two problems:
Problem #1
Currenty with the Criteria API, distinct is very difficult to use. The only way I know of to return distinct results is to use a projection:
Code:
Criteria criteria = session.createCriteria( MyEntity.class );
criteria.setProjection( Projections.distinct( Projections.property( "id" ) ) );


Unfortunately, the Criteria API works in one of two modes: with a projection and without a projection. When a projection is not specified, Hibernate will hydrate the root entities. If a projection is specified, Hibernate will return the results of the projection to you - which will not include the root entity.

For example, in the previous example, Hibernate will return a list of "id"s - there is no way to specify a projection that will actually return instances of MyEntity. In fact, there is no way to specify any projection that I know of that will return a list of MyEntity objects.

So, the only way to make the Criteria API use a distinct clause is to add a projection - but adding a projection causes Hibernate to not hydrate the root entity, defeating the purpose of the distinct clause. The Criteria API needs to either be modified to support a distinct flag outside of the Projection API or support projections that return the root entity.

Problem #2
I have done some digging in the Criteria source and see that unlike HQL, the Criteria API selects and hydrates all entities that are joined to by the query (any aliases or associations) This is inefficient and problematic. It is inefficient because typically a Criteria call only returns the root entity - so why hydrate all associated entities, especially if the associated entities are not eagerly fetched. It is problematic because, as in the example above, a distinct clause assumes that only the root entity columns are selected.

To clarify, the HQL above will result in:
Code:
select my_entities.col1, my_entities.col2 from .....


whereas the same in the Criteria API would result in:
Code:
select my_entities.col1, my_entities.col2, my_children.col1, my_children.col2 from .....

even though there is no need to include the children columns in the select clause. This appears to be due to the fact that the CriteriaLoader shares the same common superclass (OuterJoinLoader) I think it would be more efficient and support things like distinct if the CriteriaLoader was smarter about which objects it selects and hydrates. The QueryLoader used by HQL queries only selects columns that are either specified in the select clause or need to be eagerly fetched. I think the CriteriaLoader should only select columns that are part of the root entity or need to be eagerly fetched - rather than including all columns in all associations.

Solution
I would imagine the example should look like this with the Criteria API:
Code:
Criteria criteria = session.createCriteria( MyEntity.class );
criteria.add( ... ); // Add where-clause restrictions
criteria.setDistinctEntities( true );
criteria.setMaxResults( 10 );
criteria.list();


Alternatively, it could look like this:
Code:
Criteria criteria = session.createCriteria( MyEntity.class );
criteria.add( ... ); // Add where-clause restrictions
criteria.setProjection( Projections.distinct( Projections.rootEntity() ) );
criteria.setMaxResults( 10 );
criteria.list();


I am willing to spend some time making a patch, but when I dug into Problem #2 above I wasn't sure what all the implications of changing the select behavior would be. If a Hibernate developer (Gavin, Christian, ? =D ) could point me in the right direction I'd appreciate it.

Is this behavior a problem for many other users? I've seen several posts in the past regarding distinct selection using the Criteria API.


Top
 Profile  
 
 Post subject: possible solution - criteria
PostPosted: Sun Jan 22, 2006 1:04 pm 
Newbie

Joined: Tue Jun 14, 2005 12:44 pm
Posts: 19
Hi there

I too am having immense difficulty with doing complex things with criteria.

For Problem #1, I have come across a workaround.

I use a detached criteria and projections, which I need for certain Order By's
on calculated fields.

The solution I found was creating a criteria for my entire criteria class (the object I am querying). Thus in the result I have both a calculated field AND the entire object, thus not suffering the projections limitation.


Code:
DetachedCriteria criteria = DetachedCriteria.forClass(Session.class, alias);

criteria.setProjection( Projections.projectionList()
              .add(Projections.alias(Projections.sqlProjection("Ended - Started as duration",new String[] {"duration"},new org.hibernate.type.Type[] {Hibernate.TIMESTAMP}),"duration"))
               .add(Projections.sqlProjection("*",new String[] {"Id"},new org.hibernate.type.Type[]{Hibernate.entity(Session.class)}),"Id")



I do have a question if we are on the subject - has nayone managed to create a criteria that reads from TWO tables?

I am trying to create a criteria that counts the number of messages that have a sessionId, and get this count to come out as a projection result.
I have failed so far...criteria doesn't go well with Joins does it??


E.


[/code]


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jun 08, 2006 11:10 am 
Newbie

Joined: Mon Feb 27, 2006 12:02 pm
Posts: 8
Hello

I am having both problems you mentioned :( . Does anyone know whether those problems have been fixed or there are any patches. If anyone has workaround, can you share it with us :)

Best regards,


Top
 Profile  
 
 Post subject: Please vote!
PostPosted: Mon Dec 04, 2006 4:38 pm 
Newbie

Joined: Mon Dec 04, 2006 3:54 pm
Posts: 1
Please go vote for the following JIRA if you want this bug to be resolved:

http://opensource.atlassian.com/projects/hibernate/browse/HB-520

It seems that they consider this bug as a minor one! I just don't believe it.

_________________
DanP


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jan 31, 2007 11:58 am 
Regular
Regular

Joined: Mon Jan 22, 2007 10:32 am
Posts: 101
Hi,

I have also created a generic search component (as mentioned in first post) using criteria api.

I believe following could be possible solutions to the problem:

1. In order to return distinct root entities, you can use Criteria.setResultTransformer() method.
2. In my component, I also use projection to get out the list of id's and then use session.load() to get objects corresponding to these ids.


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