-->
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.  [ 2 posts ] 
Author Message
 Post subject: Criteria API lack of control over entity hydration
PostPosted: Tue Aug 29, 2006 2:51 pm 
Newbie

Joined: Tue Aug 29, 2006 2:10 pm
Posts: 11
Consider the following examples:

I have a test project with 2 entities, Parent and Child, with the following mapping files:

Parent.hbm.xml:
Code:
<?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="com.foo.persist">

   <class name="Parent" table="PAR_PARENT" lazy="true">
      <id column="PAR_UID" name="internalId" type="long">
         <generator class="native" />
      </id>


      <property column="PAR_ID" name="parentId" type="string"
         length="32">
      </property>
      <many-to-one column="PAR_CHI_ID" name="child" class="Child" />


   </class>

</hibernate-mapping>



Child.hbm.xml:
Code:
<?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="com.foo.persist">

   <class name="Child" table="CHI_CHILD" lazy="true">
      <id column="CHI_UID" name="internalId" type="long">
         <generator class="native" />
      </id>
      <property column="CHI_ID" name="childId" type="string"
         length="32">
      </property>
   </class>

</hibernate-mapping>



given the following code:
Code:
Session lSession = HibernateSessionFactory.getSession();
Transaction t = lSession.beginTransaction();
Criteria lCrit = lSession.createCriteria(Parent.class)
   .createAlias("child", "chi")
   .add(Restrictions.eq("chi.childId", "CHILD"))
   ;
      
List<Parent> lResults = lCrit.list();
      
t.commit();

HibernateSessionFactory.closeSession();

for (Parent lResultParent : lResults) {
   System.out.println("parentId : " + lResultParent.getParentId());
   System.out.println("child isInitialized : " + org.hibernate.Hibernate.isInitialized(lResultParent.getChild()));
}
      

I get the following results:
Quote:
Hibernate: select this_.PAR_UID as PAR1_1_1_, this_.PAR_ID as PAR2_1_1_, this_.PAR_CHI_ID as PAR3_1_1_, chi1_.CHI_UID as CHI1_0_0_, chi1_.CHI_ID as CHI2_0_0_ from PAR_PARENT this_ inner join CHI_CHILD chi1_ on this_.PAR_CHI_ID=chi1_.CHI_UID where chi1_.CHI_ID=?
parentId : PARENT1
child isInitialized : true
parentId : PARENT2
child isInitialized : true


Columns from CHI_CHILD show up in the select clause of the SQL statement generated by Hibernate, despite the fact that I did not specify that child should be fetched eagerly, so it should use the default of lazy fetching, right? I've tried manually setting the fetchmode to FetchMode.SELECT, but that has no effect when using a Restriction on the association.

Now, if I screw around with my object model a bit and make the children a set inside of Parent (one-to-many, instead of many-to-one):

Parent.hbm.xml:
Code:
<?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="com.foo.persist">

   <class name="Parent" table="PAR_PARENT" lazy="true">
      <id column="PAR_UID" name="internalId" type="long">
         <generator class="native" />
      </id>


      <property column="PAR_ID" name="parentId" type="string"
         length="32">
      </property>

      <set name="childSet" inverse="true">
         <key column="CHI_PAR_UID" />
         <one-to-many class="Child" />
      </set>


   </class>

</hibernate-mapping>



Child.hbm.xml:
Code:
<?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="com.foo.persist">

   <class name="Child" table="CHI_CHILD" lazy="true">
      <id column="CHI_UID" name="internalId" type="long">
         <generator class="native" />
      </id>
      <property column="CHI_ID" name="childId" type="string"
         length="32">
      </property>

      <many-to-one column="CHI_PAR_UID" name="parent" class="Parent"/>
   </class>

</hibernate-mapping>



and use the following code:

Code:
Session lSession = HibernateSessionFactory.getSession();
Transaction t = lSession.beginTransaction();
Criteria lCrit = lSession.createCriteria(Parent.class)      
   .createAlias("childSet", "chi")
   .add(Restrictions.eq("chi.childId", "CHILD1"))
   ;
      
List<Parent> lResults = lCrit.list();
      
t.commit();

HibernateSessionFactory.closeSession();

for (Parent lResultParent : lResults) {
   System.out.println("parentId : " + lResultParent.getParentId());
   System.out.println("childSet isInitialized : " + org.hibernate.Hibernate.isInitialized(lResultParent.getChildSet()));
}


I get the following output:
Quote:
Hibernate: select this_.PAR_UID as PAR1_1_1_, this_.PAR_ID as PAR2_1_1_, chi1_.CHI_UID as CHI1_0_0_, chi1_.CHI_ID as CHI2_0_0_, chi1_.CHI_PAR_UID as CHI3_0_0_ from PAR_PARENT this_ inner join CHI_CHILD chi1_ on this_.PAR_UID=chi1_.CHI_PAR_UID where chi1_.CHI_ID=?
parentId : PARENT1
childSet isInitialized : false



Columns from CHI_CHILD still show up in the generated SQL statement, but the childSet in the Parent is not initialized. Why is Hibernate selecting the data, then ignoring it? I've tried using setFetchMode(FetchMode.JOIN) to get Hibernate to initialize the childSet, but it makes no difference. I've also tried using createCriteria... instead of createAlias, and that also doesn't make any difference.

It appears that when traversing a one-to-many, there is no way for the developer to specify that Hibernate should initialize the association, and when traversing a many-to-one association, there is no way for the developer to specify that Hibernate should not initialize the association. Also, there seems to be no way to prevent Hibernate from selecting the joined columns in the select clause in either case, despite the fact that for a one-to-many, the data is ignored. Actually, I have come across one way, Projections, but using those is a daunting task since they return Object[] instead of the entity desired, and AliasToBeanResultTransformer doesn't appear to have any way to handle associations.

Shouldn't there be a little more flexibility in the Criteria API in this respect? I understand the argument that hydrating associations with a Restriction may be a "Bad Thing", but it doesn't appear that this is being consistently applied (i.e. in the many-to-one case). It seems to me that it would be beneficial to match the behavior of HQL in this case. If the developer doesn't specify "join fetch" in HQL, Hibernate obeys the fetching strategy from the mapping file. Similarly, if the developer doesn't specify setFetchMode(FetchMode.???) in the Criteria API, it should obey the fetching strategy in the the mapping file.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Dec 06, 2006 3:18 pm 
Newbie

Joined: Tue Aug 29, 2006 2:10 pm
Posts: 11
Is there some information missing from this post that would make it more answerable? I've asked quite a few questions in this forum and have never received a response to any of them.


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