-->
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.  [ 14 posts ] 
Author Message
 Post subject: Expression.isNull("field") returns all records
PostPosted: Wed Sep 10, 2003 6:30 pm 
Senior
Senior

Joined: Sun Aug 31, 2003 3:14 pm
Posts: 151
Location: Earth (at the moment)
I'm using version 2.1b3 (I still can't connect to the CVS server to get the latest code??? but anyway...).

I have the following Criteria:
Code:
         criteria = session.createCriteria(WorksheetHeader.class).add(
               Expression.eq(WORKSHEET_ID, new Integer(worksheetID)));
         criteria = criteria.createCriteria("products");
         criteria = criteria.add(Expression.isNull(PRODUCT_ID));


I assume that I should only get records where the field PRODUCT_ID is NULL but I get all records. Should I be getting all records or is something not working right? Is this something that would not happen if I could get the latest code?

For the record, the path "products" is a one-to-many map of another class which has the desireable NULL field in question. The constant PRODUCT_ID is a String of the property name that I want only records for where it is NULL.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Sep 10, 2003 10:56 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
It would be a *huge* help to post the mapping and the generated SQL.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Sep 10, 2003 11:23 pm 
Senior
Senior

Joined: Sun Aug 31, 2003 3:14 pm
Posts: 151
Location: Earth (at the moment)
Quote:
It would be a *huge* help to post the mapping and the generated SQL.

Sorry, I'm so tired and panic stricken that my brain has shut off.

Here is the generated SQL:
Code:
Hibernate: select x0_.worksheet_product_id as workshee1___, x0_.worksheet_id as workshee2___, x0_.worksheet_product_id as worksheet_product_id0_, x0_.worksheet_id as workshee2_0_, x0_.ina_prod_id as ina_prod3_0_, x0_.uom_qty as uom_qty0_, this.worksheet_id as worksheet_id1_, this.description as descript2_1_, this.vendor_no as vendor_no1_, this.status as status1_, this.created_by as created_by1_, this.created_date as created_6_1_, this.last_revision_date as last_rev7_1_, this.foreign_status as foreign_8_1_, this.foreign_contract_no as foreign_9_1_, this.comments as comments1_ from worksheet_header this inner join worksheet_product x0_ on this.worksheet_id=x0_.worksheet_id where this.worksheet_id=? and not x0_.in a_prod_id is not null


You'll notice that the condition at the end is reversed as not is not null because I switched it to see if it would make a difference.
Code:
criteria = criteria.createCriteria("products").add(Expression.not(Expression.isNotNull(PRODUCT_ID)));


The original output was:
Code:
Hibernate: select x0_.worksheet_product_id as workshee1___, x0_.worksheet_id as workshee2___, x0_.worksheet_product_id as worksheet_product_id0_, x0_.worksheet_id as workshee2_0_, x0_.ina_prod_id as ina_prod3_0_, x0_.uom_qty as uom_qty0_, this.worksheet_id as worksheet_id1_, this.description as descript2_1_, this.vendor_no as vendor_no1_, this.status as status1_, this.created_by as created_by1_, this.created_date as created_6_1_, this.last_revision_date as last_rev7_1_, this.foreign_status as foreign_8_1_, this.foreign_contract_no as foreign_9_1_, this.comments as comments1_ from worksheet_header this inner join worksheet_product x0_ on this.worksheet_id=x0_.worksheet_id where this.worksheet_id=? and x0_.ina_prod_id is null


The mapping (abbreviated to classes in direct Criteria use) is:
Code:
   <!-- WorksheetProduct -->
   <class name="com.fgl.ina.costestimation.WorksheetProduct" table="worksheet_product">
      <id name="ID" type="integer" column="worksheet_product_id" unsaved-value="0">
         <generator class="net.sf.hibernate.id.IdentityGenerator"/>
      </id>
      <many-to-one name="parentWorksheet"       column="worksheet_id" not-null="true"/>
      <property    name="productID"             column="ina_prod_id"  type="long"  not-null="false"/>
      <property    name="unitOfMeasureQuantity" column="uom_qty"      type="float" not-null="false"/>

      <bag name="costFactors" table="worksheet_factor" lazy="false" inverse="true" cascade="all-delete-orphan" order-by="worksheet_factor_id" outer-join="true">
         <key column="worksheet_product_id"/>
<!--         <index column="worksheet_factor_id" type="string"/>-->
         <one-to-many class="com.fgl.ina.costestimation.WorksheetFactor"/>
      </bag>
   </class>

   <!-- WorksheetHeader -->
   <class name="com.fgl.ina.costestimation.WorksheetHeader" table="worksheet_header">
      <id name="worksheetID" type="integer" column="worksheet_id" unsaved-value="0">
         <generator class="net.sf.hibernate.id.IdentityGenerator"/>
      </id>
      <property name="description"       column="description"         type="string"/>
      <property name="vendor"            column="vendor_no"           type="string"    not-null="true"/>
      <property name="status"            column="status"              type="short"     not-null="true"/>
<!--      <many-to-one name="status" class="com.fgl.ina.costestimation.lookups.WorksheetStatus"-->
<!--         column="status" update="false" insert="false" cascade="none" outer-join="true"/>-->
      <property name="createdBy"         column="created_by"          type="string"    not-null="true"/>
      <property name="createdDate"       column="created_date"        type="timestamp" not-null="true"/>
      <property name="revisedDate"       column="last_revision_date"  type="timestamp" not-null="true"/>
      <property name="foreignStatus"     column="foreign_status"      type="short"     not-null="true"/>
<!--      <bag name="validForeignStatuses" lazy="false" cascade="none" outer-join="true" order-by="foreign_status">-->
<!--         <many-to-many class="com.fgl.ina.costestimation.lookups.ForeignExchangeStatus" outer-join="true"/>-->
<!--      </bag>   -->
      <property name="foreignContractNo" column="foreign_contract_no" type="long"      not-null="true"/>
      <property name="comments"          column="comments"            type="string"/>

      <map name="products" table="worksheet_product" lazy="true" inverse="true" cascade="all" order-by="worksheet_product_id" outer-join="true">
         <key column="worksheet_id"/>
         <index column="worksheet_product_id" type="string"/>
         <one-to-many class="com.fgl.ina.costestimation.WorksheetProduct"/>
      </map>
   </class>


Thank-you very much for your help.
P.S. How can I get the latest code if I can't connect to the CVS server? I know that's an absurd question but I can connect to other CVS servers just not the Hibernate one, when I use the anonymous connect string listed on the website and hit enter on the password as stated on the website I get the usual connection refused message form the server. Sorry to go off topic.
Thanks again.
Sincerely,
David


Top
 Profile  
 
 Post subject:
PostPosted: Wed Sep 10, 2003 11:49 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
Well, the generated SQL is correct. So I'm guessing it is working just fine.

Why do you think there is some problem?


Top
 Profile  
 
 Post subject:
PostPosted: Wed Sep 10, 2003 11:51 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
P.S. sf.net CVS services are problem at the moment.


Top
 Profile  
 
 Post subject: Extra values exist when looking at the objects...
PostPosted: Thu Sep 11, 2003 12:43 am 
Senior
Senior

Joined: Sun Aug 31, 2003 3:14 pm
Posts: 151
Location: Earth (at the moment)
Quote:
Why do you think there is some problem?


Because the Map on my WorksheetHeader object contains all of the instances of WorksheetProduct and not just the one that has the null field I was trying to narrow down to.

Maybe this is what I should ask, as you can see in the mapping I have used the inverse=true and created a reference to the parent on the child, is it possible that the first query only retrieved the appropriate result but that the child upon having a reference to the parent somehow triggered the loading of the parent again without any criteria and thereby picked up all of the records? Since the object would be cached and compared could it be that the parent reference got modified inversely after the query snippet I posted ran? I actually get several pages of output related to all the various nested objects that get loaded, I will look closely at it to see if there is anything interesting I can find but that is a daunting task.
I too noticed that the highest level query looked correct and were it not for the fact that I have more objects than I criteria-ed I would believe it was working.


Top
 Profile  
 
 Post subject: CVS problem apology and 2.1 branch question
PostPosted: Thu Sep 11, 2003 12:51 am 
Senior
Senior

Joined: Sun Aug 31, 2003 3:14 pm
Posts: 151
Location: Earth (at the moment)
Quote:
P.S. sf.net CVS services are problem at the moment.


I want to offer an apology on this one, it seems that every time I tried to connect and all the various ways I tried to connect I neglected one thing, to go home and try it there. It works fine from my house. Something with the firewall configurations at work must have been getting in the way.

What sticky tags do I need to set to get the latest 2.1 branch?


Top
 Profile  
 
 Post subject:
PostPosted: Thu Sep 11, 2003 12:57 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
Quote:
What sticky tags do I need to set to get the latest 2.1 branch?



v21branch

Quote:
Because the Map on my WorksheetHeader object contains all of the instances of WorksheetProduct and not just the one that has the null field I was trying to narrow down to.


Of course! This is expected functionality!


If you want to retrieve only particular WorksheeProduct instances, start from the WorksheetProduct and navigate back -up- the bidirectional association. Then the retuned instances will be the instances of -WorksheetProduct- that match your Criteria.

Now, the kind of thing you want to do is possible in HQL, where you can return multiple objects in the SELECT clause, but will never be possible in the Criteria API. If I do foo.getBars(), I should ALWAYS get ALL the Bars!


Top
 Profile  
 
 Post subject: v21branch
PostPosted: Thu Sep 11, 2003 12:58 am 
Senior
Senior

Joined: Sun Aug 31, 2003 3:14 pm
Posts: 151
Location: Earth (at the moment)
Quote:
What sticky tags do I need to set to get the latest 2.1 branch?


Sorry, that was probably a dumb question.
I looked up and used v21branch does that sound right?


Top
 Profile  
 
 Post subject:
PostPosted: Thu Sep 11, 2003 1:08 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
P.S. There is a good argument that in the query above, we shouldn't be even fetching the WorksheetProducts, because we still need to hit the db again in order to fetch the collection. I havn't quite decided which way to go on this. Its certainly ok to prefetch objects in a <many-to-one>, and might even make good sense for a <many-to-many>, but probably inefficient for the case of <one-to-many>.


Top
 Profile  
 
 Post subject: Exptected functionality is neither expected nor helpful...
PostPosted: Thu Sep 11, 2003 4:13 pm 
Senior
Senior

Joined: Sun Aug 31, 2003 3:14 pm
Posts: 151
Location: Earth (at the moment)
Dear Gavin, thank-you for your help and responses,

Here is what I have determined that I know happens when I specify a complex mapping of nested objects and try to specify Criteria on properties of the nested objects:

The first sql query generated is correct, it loads only what was outlined by the criteria to be loaded.
But every single subsequent generated sql query completely and utterly ignores anything that was entered in the Criteria and loads every single object on every single association all the way through the entire Hierarchy thereby completely and utterly defeating any reason for having specified a Criteria in the first place.
You say:
Quote:
Of course! This is expected functionality!

But why on earth is that expected? How is it helpful or useful to never be able to narrow down your results without disassociating every single object in the hierarchy and loading them all manually with repeated calls to the Criteria API?

You say that if
Quote:
If I do foo.getBars(), I should ALWAYS get ALL the Bars!

but what if I don't want all the Bars? What good is a Criteria API if the Criteria portion is circumvented further down the tree?


Quote:
If you want to retrieve only particular WorksheeProduct instances, start from the WorksheetProduct and navigate back -up- the bidirectional association. Then the retuned instances will be the instances of -WorksheetProduct- that match your Criteria.


I tried that, the moment I "back -up- " I end up with a parent that has all of the instances and not just the one I loaded. Now, in this scenario, I can see it becomes arguable as to what should be the case, was that one object loaded because it was the only one desired and so should it be the only one its parent reference contains? or was that the only reference point available and does the user really want to get everything else that the parent might have?

Regardless of the above, almost philosophical, question; whatever Criteria is specified as the restriction on what should be loaded at a certain point should be enforced from that point on down the hierarchy.
If the criteria is not enforced but used only loosely as a starting point for loading everything then it serves little purpose. It is like querying a database for only the records where 'x' = 1 but getting all the records where 'x' = 2..n anyway. No one would use a database where this occurred.

Quote:
Now, the kind of thing you want to do is possible in HQL, where you can return multiple objects in the SELECT clause, but will never be possible in the Criteria API

Why shouldn't it be possible with the Criteria API? Once again, what good is a Criteria API if the criteria portion is largely ignored or circumvented and the final results do not in any way, shape, or form, match what was asked for in the criteria?
If I wanted all records possible I would not specify any criteria.
This is effectively the same as "select * from tableA" vs. "select * from tableA where column1 = 5 and column2 = 7". If I wanted all the records I would use the first query, if only wanted records where column1=5 and column2=7 I would use the second. Logically if I want all the records through the Criteria API I would not specify any Criterion, if, however, I want only certain records I would .add(Expression.eq(...)) until I had narrowed down the list.

For the Criteria to function properly at the highest level but be ignored throughout the hierarchy thereafter is contradictory.


Quote:
P.S. There is a good argument that in the query above, we shouldn't be even fetching the WorksheetProducts, because we still need to hit the db again in order to fetch the collection. I havn't quite decided which way to go on this. Its certainly ok to prefetch objects in a <many-to-one>, and might even make good sense for a <many-to-many>, but probably inefficient for the case of <one-to-many>.


This is only true if nobody wants to narrow down to a subset of the <one-to-many> (which I do, and which you tell me I can't).

This to me is critical functionality, I switched from Toplink to Hibernate because I read in Hibernate's quick reference that it had a Criteria API and I trusted that I could use it as such as I had used similar functionality in Toplink. I have waited patiently through weeks of debugging and struggling to get Hibernate configured and operating as needed, reading and posting to this forum, waiting for bug fixes and/or functionality to be implemented, only to ultimately be told that it isn't supposed to work in a powerful and flexible way that is useful beyond a few simple scenarios. I think you guys have done a fantastic job with what you have made and your commitment to your user base so please don't think I'm bitter, I just wish it had been stated in the documentation that this functionality was not intended to be fully fledged like it is in other products.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Sep 11, 2003 11:13 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
Quote:
But why on earth is that expected?


Because a collection has *identity*. An object has a particular state. And showing you some other state is Evil and Wrong.

What if you loaded the object once earlier in the session. Then you queried it by criteria. Should I suddenly "trim down" the existing collection to match the Criteria query??!

Vomit!

I'm sorry, but you are just flat out off target here and I will _never_ change Hibernate to support such evil behaviour. Its just awful. It is completely non-object-oriented, since it treats objects as pain _values_ with no _identity_.

Quote:
What good is a Criteria API if the Criteria portion is circumvented further down the tree?


It is NOT circumvented. It is used correctly to select matching instances of the top queried class.

Quote:
Why shouldn't it be possible with the Criteria API?


At present the Criteria API returns single objects as each row of results. In HQL, an array is possible.


Quote:
This is effectively the same as "select * from tableA" vs. "select * from tableA where column1 = 5 and column2 = 7". If I wanted all the records I would use the first query, if only wanted records where column1=5 and column2=7 I would use the second.



Umm. No. It is not. I think you need to think about this for a few minutes. The Criteria placed on the children constrains which instances of the parent are returned.


Quote:
This to me is critical functionality, I switched from Toplink to Hibernate because I read in Hibernate's quick reference that it had a Criteria API and I trusted that I could use it as such as I had used similar functionality in Toplink.



If you check the javadoc, it says "this is an experimental API".

If you check the version number of Hibernate that you are using, it says "beta". The features you are using were introduced in this current beta cycle.

Now you are pissing me off.




Now, I think what you need to do here is forget all your assumptions about how you think things "should" work, and instead learn about how Hibernate actually works. The current behaviour is much more correct and much more object oriented than what you are proposing.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Sep 11, 2003 11:50 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
To be clear, what you are asking for is that Hibernate behaves like this:

Code:
Foo foo = (Foo) session.get(Foo.class, "foo");

assertTrue( foo.getBars().size()==10 );

session.createCriteria(Foo.class)
    .add( Expression.eq("id", "foo")
    .createCriteria("bars")
        .add( Expression.like("name", "b%") )
    .list();

assertTrue( foo.getBars().size()==5 );

session.createCriteria(Foo.class)
    .add( Expression.eq("id", "foo")
    .createCriteria("bars")
        .add( Expression.like("name", "ba%") )
    .list();


assertTrue( foo.getBars().size()==3 );



I think its quite clear why this is evil.


Now, what I can do for you is provide the following feature.


Code:
List list = session.createCriteria(Foo.class)
    .add( Expression.eq("id", "foo")
    .createCriteria("bars", "bar")
        .add( Expression.like("name", "b%") )
    .setResultsAsMap()
    .list();

Map firstResult = (Map) list.get(0);

Foo foo = firstResult.get("this");
Bar bar = firstResult.get("bar");


Are you happy with that?


Top
 Profile  
 
 Post subject:
PostPosted: Fri Sep 12, 2003 1:18 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
Anyway, I implemented this. Its in CVS.


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