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.  [ 1 post ] 
Author Message
 Post subject: HQL feature suggestion
PostPosted: Sun Dec 05, 2004 6:39 am 
Newbie

Joined: Sat Dec 04, 2004 7:35 am
Posts: 1
Location: Sweden
Hi, I'm in the middle of my first Hibernate project, so I might have missed how to do certain things.

However, here is a simplified view of my problem (and solution):

Lets say I have the following pojos: A, B, C, D
A has one-to-many relationships to B, C and D, i.e. A has three methods getAs() : Set, getBs() : Set and getDs() : Set

In my webapp I render a long list of A objects, that I fetch from a query. In each row I have to render all elements of B, C and D as well.
In HQL I have the possibility to use join fetch for one collection, (e.g. B) in order to speed things up. But on each row I render, there will be two new querys for the C and D collections.
If there are N elements of type A in the list, there will be 1 + 2N queries against the database for my list. This will be a huge performance hit for long lists of A objects.



My solution (or hack) to the problem is to run just 3 querys to load all the objects like this:

Code:
public static List getAList(Date from, Date to) throws HibernateException {       
    // The where clause has to be the same for all 3 queries
    String WHERE =
        "where b.date >= :from" +
        "and b.date <= :to ";

    Session sess = HibernateProvider.getSession();
    Query query = sess.createQuery("select a " +
        "from A a " +
        "join fetch a.bs b " +
        WHERE);
    query.setParameter("from", from);
    query.setParameter("to", to);
    List aList = query.list();

    // Init C collections for all A objects in aList (no more, no less)
    query = sess.createQuery("select a.id, c " +
        "from A a " +
        "join a.bs b " +
        "join a.cs c " +
        WHERE);
    query.setParameter("from", from);
    query.setParameter("to", to);
    Id2LazySet id2CSet = new Id2LazySet(query.list());
    // Init all a->c sets
    for (int i = 0, size = aList.size(); i < size; i++) {
        A a = (A) aList.get(i);
        // Update C collection for this A object
        id2CsSet.updateLazySet(sess, a, a.getCs());
    }

    // Init D collections for all A objects in aList (no more, no less)
    query = sess.createQuery("select a.id, d " +
        "from A a " +
        "join a.bs b " +
        "join a.ds d " +
        WHERE);
    query.setParameter("from", from);
    query.setParameter("to", to);
    Id2LazySet id2DSet = new Id2LazySet(query.list());
    // Init all a->d sets
    for (int i = 0, size = aList.size(); i < size; i++) {
        A a = (A) aList.get(i);
        // Update D collection for this A object
        id2DsSet.updateLazySet(sess, a, a.getDs());
    }

    return aList;
}

public class Id2LazySet {
    private Map map = new HashMap();

    public Id2LazySet(List id_objs) {
        // Build a map with id->a list with collection elements
        for (int i = 0, size = id_objs.size(); i < size; i++) {
            Object[] id_obj = (Object[]) id_objs.get(i);
            ArrayList list = (ArrayList) map.get(id_obj[0]);
            if (null == list) {
                list = new ArrayList();
                map.put(id_obj[0], list);
            }
            list.add(id_obj[1]);
        }
    }

    public void updateLazySet(Session sess, Object owner, java.util.Set lazySet) throws SysException {
        try {
            SessionImpl sessImpl = (SessionImpl) sess;
            Set set = (Set) lazySet;
            CollectionSnapshot collectionSnapshot = set.getCollectionSnapshot();

            ArrayList contentList = (ArrayList) map.get(collectionSnapshot.getKey());
            int size = (null != contentList ? contentList.size() : 0);
            Serializable[] idArray = new Serializable[size];
            for (int i = 0; i < size; i++) {
                idArray[i] = sess.getIdentifier(contentList.get(i));
            }
            SessionFactoryImplementor sfi = (SessionFactoryImplementor) sess.getSessionFactory();

            set.initializeFromCache(sfi.getCollectionPersister(collectionSnapshot.getRole()), idArray, owner);
        } catch (HibernateException x) {
            throw new SysException(x);
        }
    }
}


It works very well, but it feels like an ugly hack. Imagine if HQL could do this for you with something like this:

Code:
select a
from A a
join fetch a.bs b
query-fetch a.cs c
query-fetch a.ds d
where b.date >= :from
and b.date <= :to


This query would generate 3 sql queries against the DB, and all our objects and collections would be initiated...

What do you think? Am I alone with this problem?

Please let me know if it was hard to understand my point of view!
Dave


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 1 post ] 

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.