-->
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.  [ 7 posts ] 
Author Message
 Post subject: Subquery in Projection and Order By
PostPosted: Fri Mar 07, 2008 12:43 pm 
Newbie

Joined: Fri Jun 09, 2006 4:27 pm
Posts: 10
Subquery in Projection and Order By

I have a situation in which I can not create a Criteria or Query to represent the SQL that I need without resorting to a hack.

I am using Hibernate Core 3.2.5 on Oracle 9i. The tables and mappings in question are:

Code:
CREATE TABLE zai.ideas (
  id_idea_id_pk       NUMBER(19) NOT NULL,
  id_author_id_fk     VARCHAR2(11) NOT NULL,
  id_category_id_fk   NUMBER(19) NOT NULL,
  id_confidential_yn  VARCHAR2(3) NOT NULL,
  id_content          VARCHAR2(4000) NOT NULL,
  id_email            VARCHAR2(70) NOT NULL,
  id_status_cd        VARCHAR2(16) NOT NULL,
  id_submit_ts        DATE NOT NULL,
  id_topic            VARCHAR2(128) NOT NULL,
  id_revenue_yn       VARCHAR2(3) NOT NULL,
  id_version          NUMBER(10) NOT NULL
);

CREATE TABLE zai.ballots (
  ba_idea_id_ck       NUMBER(19) NOT NULL,
  ba_voter_id_ck      VARCHAR2(11) NOT NULL,
  ba_votes            NUMBER(10) NOT NULL,
  ba_closed_yn        VARCHAR2(3) NOT NULL,
  ba_version          NUMBER(10) NOT NULL
);

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
  "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
  "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="domain"
  schema="ZAI">
  <class name="Idea" table="IDEAS">
   
    <id name="id"
        column="ID_IDEA_ID_PK"
        type="long"
        access="field">
      <generator class="sequence">
        <param name="sequence">ZAI_ID</param>
      </generator>
    </id>
   
    <version name="version"
        column="ID_VERSION"
        type="integer"
        access="field" />
       
    <many-to-one name="author"
        column="ID_AUTHOR_ID_FK"
        fetch="join"
        access="field"
        update="false" />
       
    <set name="ballots"
        access="field"
        cascade="all, delete-orphan"
        inverse="true" >
      <key column="BA_IDEA_ID_CK"/>
      <one-to-many class="Ballot" />
    </set>

    <many-to-one name="category"
        column="ID_CATEGORY_ID_FK"
        fetch="join"
        access="field" />

    <property name="content"
        column="ID_CONTENT"
        type="string"
        access="field" />
       
    <property name="confidential"
        column="ID_PRIVATE_YN"
        type="yes_no"
        access="field" />

    <property name="email"
        column="ID_EMAIL"
        type="string"
        access="field" />

    <property name="revenueGenerating"
        column="ID_REVENUE_YN"
        type="yes_no"
        access="field" />

    <property name="status"
        column="ID_STATUS_CD"
        type="idea_status"
        access="field" />
       
    <property name="submitDate"
        column="ID_SUBMIT_TS"
        type="timestamp"
        access="field"
        update="false" />
       
    <property name="topic"
        column="ID_TOPIC"
        type="string"
        access="field" />

  </class>
</hibernate-mapping>

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
  "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
  "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="domain"
  schema="ZAI">
  <class name="Ballot" table="BALLOTS">
 
    <composite-id name="id"
        class="Ballot$Id"
        access="field">
      <key-property name="ideaId"
          column="BA_IDEA_ID_CK"
          type="long"
          access="field" />
      <key-property name="voterId"
          column="BA_VOTER_ID_CK"
          type="encrypted_string"
          access="field" />
    </composite-id>

    <version name="version"
        column="BA_VERSION"
        type="integer"
        access="field" />

    <property name="votes"
        column="BA_VOTES"
        type="int"
        access="field" />

    <property name="closed"
        column="BA_CLOSED_YN"
        type="yes_no"
        access="field" />

    <many-to-one name="idea"
        column="BA_IDEA_ID_CK"
        access="field"
        insert="false"
        update="false" />

    <many-to-one name="voter"
        column="BA_VOTER_ID_CK"
        access="field"
        insert="false"
        update="false" />
   
  </class>
</hibernate-mapping>


What I need to do is select Ideas that could match any of the following: status, category, confidential, or revenue generating if provided. I wrote the following criteria to retrieve the matching Ideas:

Code:
Criteria criteria = getSession().createCriteria(getPersistentClass());
criteria.setFirstResult(firstResult);
criteria.setMaxResults(pageSize);

if (status != null) {
   criteria.add(Restrictions.eq("status", status));
}

if (category != null) {
   criteria.add(Restrictions.eq("category", category));
}

if (excludeConfidential) {
   criteria.add(Restrictions.eq("confidential", false));
}

if (excludeNonRevenueGenerating) {
   criteria.add(Restrictions.eq("revenueGenerating", true));
}

The problem comes in when I want to sort the results. I want to configure the criteria to implement sorting by submitDate, total votes and submitDate, or ballot count and submitDate. I would like to be able to do something like the following:

Code:
if (IdeaSortBy.MOST_POPULAR.equals(sortBy)) {
   criteria.addOrder(Order.desc("sum(ballots.votes)"));
}
else if (IdeaSortBy.MOST_ACTIVE.equals(sortBy)) {
   criteria.addOrder(Order.desc("ballots.size"));
}
criteria.addOrder(Order.desc("submitDate"));

This, of course, is not possible. Order only accepts a property name. If Order could accept a sql fragment then I could at least do something like the following:

Code:
if (IdeaSortBy.MOST_POPULAR.equals(sortBy)) {
   criteria.addOrder(Order.sqlFragment("select nvl(sum(b.ba_votes),0) from zai.ballots b where b.ba_idea_id_ck = id_idea_id_pk").desc());
}
else if (IdeaSortBy.MOST_ACTIVE.equals(sortBy)) {
   criteria.addOrder(Order.sqlFragment("select count(b.ba_idea_id_ck) from zai.ballots b where b.ba_idea_id_ck = id_idea_id_pk) as ballots").desc());
}
criteria.addOrder(Order.desc("submitDate"));

To get around this problem I created two private fields on Idea named votesHack and ballotCountHack. These fields have no accessors. The mappings for these fields look like this:

Code:
<property name="votesHack"
      formula="(select nvl(sum(b.ba_votes),0) from zai.ballots b where b.ba_idea_id_ck = id_idea_id_pk)"
      type="int"
      access="field" />

<property name="ballotCountHack"
      formula="(select count(b.ba_idea_id_ck) from zai.ballots b where b.ba_idea_id_ck = id_idea_id_pk)"
      type="int"
      access="field" />

Having done this I am able to write the order by on the criteria as:

Code:
if (IdeaSortBy.MOST_POPULAR.equals(sortBy)) {
   criteria.addOrder(Order.desc("votesHack"));
}
else if (IdeaSortBy.MOST_ACTIVE.equals(sortBy)) {
   criteria.addOrder(Order.desc("ballotCountHack"));
}
criteria.addOrder(Order.desc("submitDate"));

The net affect of all of this is that votesHack and ballotCountHack formulas are added to the projection and to the order by.

I didn't like this hack so I tried to find another way to implement the required query. I turned to DetachedCriteria but that only works for Restrictions and not Projections. Using HQL or SQL wouldn't work due to the dynamic nature of the query. By that I mean the restrictions and order by are dynamic based on the input specification. Furthermore, this hack solution would not allow formula with input parameters.

Finally the questions...

Has anybody encountered a situation similar to this one and found a solution other than the hack I used?

To the Hibernate team...

It is possible to update Order to include a sqlFragment and/or DetachedCriteria?

It is possible to update Projections to use a DetachedCriteria as a property? This would useful when mapping to non-entities.

It is possible to update Projections to accept input parameters in a sqlFragment as Restrictions does? This would be an alternative to the question above using DetachedCriteria.


In general, is anybody else interested in these features? If so I will submit to JIRA.


Samer Kanjo


Top
 Profile  
 
 Post subject: Encountered the similar problem
PostPosted: Tue Oct 07, 2008 4:59 am 
Newbie

Joined: Fri Dec 14, 2007 8:34 am
Posts: 3
Hi Sameer,

A very interesting post indeed. I am trying to achieve the exact same thing. And have faced same sort of challanges. I am going to try the solution you have suggested.

I think its worth raising in JIRA.

Regards,
Jeevan.


Top
 Profile  
 
 Post subject: Projections
PostPosted: Tue Jan 20, 2009 9:34 am 
Newbie

Joined: Tue Apr 01, 2008 3:34 pm
Posts: 10
At the risk of "waking zombies", I thought I'd contribute a quick piece to this thread.

You can't sort by a sum if you haven't selected it. You need to create a Projection to do the sum, give it an alias and you can then sort by the alias.

No time to post the details but I'm pretty sure that would work.


Last edited by midnightraver on Wed Jan 21, 2009 11:33 am, edited 1 time in total.

Top
 Profile  
 
 Post subject: Need brains
PostPosted: Tue Jan 20, 2009 11:32 am 
Newbie

Joined: Fri Jun 09, 2006 4:27 pm
Posts: 10
I do remember looking at using projections but it didn't seem to work. I will revisit to see if I can get it to work in my scenario.


Top
 Profile  
 
 Post subject: Re: Projections Alias does not work in order by
PostPosted: Sat Nov 28, 2009 11:21 am 
Senior
Senior

Joined: Tue Sep 13, 2005 2:01 am
Posts: 137
midnightraver wrote:
At the risk of "waking zombies", I thought I'd contribute a quick piece to this thread.

You can't sort by a sum if you haven't selected it. You need to create a Projection to do the sum, give it an alias and you can then sort by the alias.

No time to post the details but I'm pretty sure that would work.


It did not work for me. We are using JBoss 5.1.0.GA that uses hibernate-core.jar 3.3.1.GA.
We tried to achieve the following using Criteria API:

select studentName, sum(score) as totalScore from Score group by studentName order by totalScore desc;

sess.createCriteria(Score.class).add(Projections.getProjectionList().add(Projections.sum("score"), "totalScore")
.add(Projections.groupProperty("studentName")).addOrder(Order.desc("totalScore")).list();

The error message: totalScore is not a property of Score.
The projection alias did not work in order by.

Is this a bug? Thanks for help or any workaround.
Dave


Top
 Profile  
 
 Post subject: Re: Projections Alias does not work in order by
PostPosted: Sat Nov 28, 2009 11:11 pm 
Senior
Senior

Joined: Tue Sep 13, 2005 2:01 am
Posts: 137
javatwo wrote:
midnightraver wrote:
At the risk of "waking zombies", I thought I'd contribute a quick piece to this thread.

You can't sort by a sum if you haven't selected it. You need to create a Projection to do the sum, give it an alias and you can then sort by the alias.

No time to post the details but I'm pretty sure that would work.


It did not work for me. We are using JBoss 5.1.0.GA that uses hibernate-core.jar 3.3.1.GA.
We tried to achieve the following using Criteria API:

select studentName, sum(score) as totalScore from Score group by studentName order by totalScore desc;

sess.createCriteria(Score.class).add(Projections.getProjectionList().add(Projections.sum("score"), "totalScore")
.add(Projections.groupProperty("studentName")).addOrder(Order.desc("totalScore")).list();

The error message: totalScore is not a property of Score.
The projection alias did not work in order by.

Is this a bug? Thanks for help or any workaround.
Dave

Sorry, my mistake. Adding a rowCount projection deleted all existing projections.


Top
 Profile  
 
 Post subject: Re: Subquery in Projection and Order By
PostPosted: Sun Nov 29, 2009 9:48 am 
Newbie

Joined: Tue Apr 01, 2008 3:34 pm
Posts: 10
I'm not clear on whether or not you got it working with projections. The other thing you could do, if you database supports it, is create a view with the sum(score) in it with a column name like "total_score". You could then write your criteria query against the view using totalScore just like any "normal" column...

[code]
create view summary_view as
select
student_name,
sum(score) total_score
group by
student_name
[/code]

...this is getting away from your original idea a bit, but it's something I've used here and there in a different context. You can add additional columns to your view so that you could sort the results differently. The added columns also have to get added to the group by.

I don't recommend sorting in the view. I think in Oracle sorted views can't be optimized, not sure about other databases...


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