-->
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.  [ 3 posts ] 
Author Message
 Post subject: Criteria - setFetchMode() - addOrder() - join
PostPosted: Thu Jun 02, 2005 12:03 pm 
Regular
Regular

Joined: Thu May 26, 2005 2:08 pm
Posts: 99
I am confused about how Criteria.setFetchMode() is supposed to be used. I've setup three simple classes: A, B, and C.

Class A contains an instance of Class B.
Class B contains an instance of Class C.

Each class has one other property and an ID.

A has a name (String).
B has a color (String).
C has a size (Integer).

My data looks like the following:

A:
Code:
id    name     b_id
1     foo     1
2     bar     2


B:
Code:
id    color   c_id
1     red     1
2     green   <null>
3     blue     3


C:
Code:
id   size
1   10
2   20
3   30
4   40
5   50



The problem that I'm having is that if I try to use Criteria to fetch a list of "A" objects, sorted by the size field from C, Hibernate is always using inner joins. I understand why it would want to do that by default, but I'm trying to override that behavior with Criteria.setFetchMode().

Using a Criteria that was built for A.class, I've tried both of the following (among dozens of other failed iterations):
Code:
criteria.setFetchMode("b.c", FetchMode.JOIN);

Code:
criteria.setFetchMode("a.b.c", FetchMode.JOIN);


Tracing this with a debugger, it looks like the decision is being made in CriteriaLoader.getJoinType(). Specifically this piece of code:
Code:
      if ( translator.isJoin(path) ) {
         return JoinFragment.INNER_JOIN;
      }
      else {
         FetchMode fm = translator.getRootCriteria()
            .getFetchMode(path);

I see paths of "b" and "b.c" being tested in that if statement, and both return true, which then returns JoinFragment.INNER_JOIN. With my calls to setFetchMode() above, I'm trying to override this behavior, but so far have not been able to do so.

In the information below, I've included an HQL statement that does exactly what I want. The trouble is that for the real application where I'm trying to do this, HQL would be a giant pain to use. This is the backend of a complex search screen, and I have no way of knowing until runtime which fields will be searched on, sorted by, etc. The HQL would have to be built up in pieces at runtime, which is why using a Criteria has been so nice up until I ran into this problem with joining.

If someone could point me toward what I'm doing wrong with this method call, I'd greatly appreciate it.


Hibernate version:
3.0.5

Mapping documents:
hibernate.cfg.xml
Code:
<!DOCTYPE hibernate-configuration PUBLIC
   "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
   "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
   <session-factory name="local">
        <property name="show_sql">true</property>
      <property name="auto-import">false</property>
        <property name="dialect">org.hibernate.dialect.MySQLDialect</property> 
      <property name="connection.url">jdbc:mysql://localhost:3306/criteria</property>
        <property name="connection.user">crit_test</property>
        <property name="connection.password">crit_test</property>
        <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
                   
        <mapping resource="A.hbm.xml"/>
        <mapping resource="B.hbm.xml"/>
        <mapping resource="C.hbm.xml"/>
   </session-factory>
</hibernate-configuration>


A.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>
    <class name="A" table="A" lazy="true">
        <id name="id" column="id">
            <generator class="native"/>
        </id>
        <property name="name"/>
       
        <many-to-one name="b" class="B" column="b_id"/>
    </class>
</hibernate-mapping>


B.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>
    <class name="B" table="B" lazy="true">
        <id name="id" column="id">
            <generator class="native"/>
        </id>
        <property name="color"/>
       
        <many-to-one name="c" class="C" column="c_id"/>
    </class>
</hibernate-mapping>


C.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>
    <class name="C" table="C" lazy="true">
        <id name="id" column="id">
            <generator class="native"/>
        </id>
        <property name="size"/>
    </class>
</hibernate-mapping>


Code between sessionFactory.openSession() and session.close():
Criteria query:
Code:
        Criteria criteria = session.createCriteria(A.class, "a");
        Criteria criteriaB = criteria.createCriteria("b");
        Criteria criteriaC = criteriaB.createCriteria("c");
        criteriaC.addOrder(Order.asc("size"));
       
        criteria.setFetchMode("b.c", FetchMode.JOIN);
       
       
        List list = criteria.list();
        Iterator i = list.iterator();
        while (i.hasNext()) {
            A a = (A) i.next();
            System.out.println(a);
        }


HQL query:
Code:
        Query query = session.createQuery(
                "from A as a " +
                "  left join a.b as b " +
                "  left join b.c as c " +
                "  order by c.size asc ");
       
        List list = query.list();

        Iterator i = list.iterator();
        while (i.hasNext()) {
            A a = (A) ((Object[])i.next())[0];
            System.out.println(a);
        }


Full stack trace of any exception that occurs:
No exception occurs.

Name and version of the database you are using:
MySQL 4.0.24-debug (Windows)

The generated SQL (show_sql=true):
Output from Criteria query:
Code:
Hibernate: select this_.id as id2_, this_.name as name0_2_, this_.b_id as b3_0_2_, b1_.id as id0_, b1_.color as color1_0_, b1_.c_id as c3_1_0_, c2_.id as id1_, c2_.size as size2_1_ from A this_ inner join B b1_ on this_.b_id=b1_.id inner join C c2_ on b1_.c_id=c2_.id order by c2_.size asc

A@c0c8b5[id=1,name=foo,b=B@7a9224[id=1,color=red,c=C@158015a[id=1,size=10]]]


Output from HQL query:
Code:
Hibernate: select a0_.id as id0_, b1_.id as id1_, c2_.id as id2_, a0_.name as name0_0_, a0_.b_id as b3_0_0_, b1_.color as color1_1_, b1_.c_id as c3_1_1_, c2_.size as size2_2_ from A a0_ left outer join B b1_ on a0_.b_id=b1_.id left outer join C c2_ on b1_.c_id=c2_.id order by c2_.size asc

A@1e16483[id=2,name=bar,b=B@1b82d69[id=2,color=green,c=<null>]]
A@1c6d11a[id=1,name=foo,b=B@1ca209e[id=1,color=red,c=C@123a389[id=1,size=10]]]


Supporting Java code

A.java:
Code:
import org.apache.commons.lang.builder.ToStringBuilder;

public class A {

    private Long id;
    private String name;
    private B b;
   
    public String toString () {
        return new ToStringBuilder(this)
            .append("id", id)
            .append("name", name)
            .append("b", b)
            .toString();
    }
    // ... getters and setters ...
}


B.java:
Code:
import org.apache.commons.lang.builder.ToStringBuilder;

public class B {
   
    private Long id;
    private String color;
    private C c;
   
    public String toString() {
        return new ToStringBuilder(this)
            .append("id", id)
            .append("color", color)
            .append("c", c)
            .toString();
    }
    // ... getters and setters ...
}


C.java:
Code:
import org.apache.commons.lang.builder.ToStringBuilder;

public class C {
   
    private Long id;
    private Integer size;
   
    public String toString() {
        return new ToStringBuilder(this)
            .append("id", id)
            .append("size", size)
            .toString();
    }
    // ... getters and setters ...
}


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jun 02, 2005 3:24 pm 
Regular
Regular

Joined: Thu May 26, 2005 2:08 pm
Posts: 99
Note: I don't even pretend to fully understand the consequences of the code change I'm about to show or any horrors that are caused by its use. However, I changed some of the code in CriterialLoader.getJoinType(), because I'm trying to better understand how it works.

Current CVS code:
Code:
      if ( translator.isJoin(path) ) {
         return JoinFragment.INNER_JOIN;
      }
      else {
         FetchMode fm = translator.getRootCriteria()
            .getFetchMode(path);



My local change:
Code:
      FetchMode fm = translator.getRootCriteria().getFetchMode(path);
      if (fm == null && translator.isJoin(path) ) {
         return JoinFragment.INNER_JOIN;
      }
      else {
         if ( isDefaultFetchMode(fm) ) {



My output when using Criteria now looks like this:
Code:
Hibernate: select this_.id as id2_, this_.name as name0_2_, this_.b_id as b3_0_2_, b1_.id as id0_, b1_.color as color1_0_, b1_.c_id as c3_1_0_, c2_.id as id1_, c2_.size as size2_1_ from A this_ inner join B b1_ on this_.b_id=b1_.id left outer join C c2_ on b1_.c_id=c2_.id order by c2_.size asc

A@1a0b53e[id=2,name=bar,b=B@1389b3f[id=2,color=green,c=<null>]]
A@10f0625[id=1,name=foo,b=B@1dafb4e[id=1,color=red,c=C@1a8d460[id=1,size=10]]]


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jun 02, 2005 3:38 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
There is a deep limitation in OuterJoinLoader that prevents the same association being joined twice. Someday we will remove that, but it will require a lot of refactoring. For now, you cannot set a fetch mode and create a criteria on the same association path.


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