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.