-->
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: Trying to Avoid Subselects in Filters
PostPosted: Thu Dec 21, 2006 4:25 pm 
Expert
Expert

Joined: Sat Jan 17, 2004 2:57 pm
Posts: 329
Location: In the basement in my underwear
Need help with Hibernate? Read this first:
http://www.hibernate.org/ForumMailingli ... AskForHelp

Hibernate version:
3.2.1 GA

Mapping documents:
N/A

Code between sessionFactory.openSession() and session.close():
N/A

Full stack trace of any exception that occurs:
No Exception

Name and version of the database you are using:
Oracle 10g

The generated SQL (show_sql=true):
See below

Debug level Hibernate log excerpt:
N/A


I have been racking my brain over a particular filter issue I have been trying to work through for a while now. We're extensively using filters within our application to address requirements around tracking temporal data. I'll try to keep our overall arch short, see the following link for a more detailed description http://forum.hibernate.org/viewtopic.ph ... t=temporal

Essentially for any of our data that is temporal we have a header table with a version table hanging off of it that contains one or more versions throughout time. In our case if we look for a record in a time period that doesn't have a version then for all intents and purposes that record 'doesn't exist' and will return null.

What we are doing right now is using a Class Level filter with a subselect in order to filter out records that don't have a corresponding version record for the query in question.

However, as we are continuing our usage of filters we are beginning to find cases where 2 individual subselects would need to know about each other.

I'll give an example:

Code:
from
        AGMT_CNTRCT_LINE this_
    where
        this_.AGMT_CNTRCT_LINE_ID IN (
            SELECT
                version.AGMT_CNTRCT_LINE_ID
            FROM
                AGMT_CNTRCT_LINE_V version
            WHERE
                this_.AGMT_CNTRCT_LINE_ID = version.AGMT_CNTRCT_LINE_ID
                AND version.CREATE_DATE = (
                    SELECT
                        MAX(b.CREATE_DATE)
                    from
                        AGMT_CNTRCT_LINE_V b
                    WHERE
                        version.AGMT_CNTRCT_LINE_ID = b.AGMT_CNTRCT_LINE_ID 
                        AND ? < b.VERSION_END_DATE
                        AND ? > b.VERSION_START_DATE
                )
                AND version.ACTIVE_FLG=?
            )
            and this_.AGMT_CNTRCT_LINE_ID IN (
                SELECT
                    version.AGMT_CNTRCT_LINE_ID
                FROM
                    AGMT_CNTRCT_LINE_V version
                WHERE
                    this_.AGMT_CNTRCT_LINE_ID = version.AGMT_CNTRCT_LINE_ID
                    AND ? < version.INACTIVE_DATE
                    AND ? > version.EFFECTIVE_DATE
            )


In this case I am looking for the most recent version that is active and has a business effectiveness between 2 dates. However, this gives me the incorrect answer since the 2nd subselect isn't filtered down to the most recent version.

So I have a few options that I can think of:
-Add another permutation for the 1st subselect that contains the criteria for the effective, inactive date. However, our filters are dynamic based on the type of query we're doing so I end up having to specify a filter with and without the 2nd subselect incorporated. This is less than ideal. And in fact, already done with the active_flg. We'll be adding more scenarios like this so I can see this quickly spiraling out of control.
-Force an Inner Join to the Collection for the AGMT_CNTRCT_LINE_V table in the initial search. This actually removes the need to do the class level filter as we also have a collection level filter that gives us the right result. However, this quickly falls apart when you start loading the root level objects as collections off of other Entities.
Is there some way to tell the hibernate mapping to ALWAYS apply the inner join so that the filtering would apply automatically? This would be ideal as I could get rid of the class level filter altogether. Guess would also require the ability to give the join a specified alias so that it could be used in the query itself.
-Possibly change my mapping so that instead of a one to many collection I map it some other way that would give me the desired result. Although, I have no idea if possible or what it would be?

Does anyone have any insight to offer?


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jan 08, 2007 4:35 pm 
Expert
Expert

Joined: Sat Jan 17, 2004 2:57 pm
Posts: 329
Location: In the basement in my underwear
Added a plea for help in

http://opensource.atlassian.com/project ... se/HHH-298

This actually was a 4th possibility:
-Add the ability to have a join in the filter itself.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Feb 13, 2007 6:28 pm 
Expert
Expert

Joined: Sat Jan 17, 2004 2:57 pm
Posts: 329
Location: In the basement in my underwear
So, in case any is following and/or cares this is what we ended up doing.

First tried forcing the inner join each time on the root object but ended up with duplicate records coming back which made complete sense although part of me was hoping that since I wasn't actually retrieving anything from the joined table that all would be good....not the case so I abandoned that idea.

I dispelled the multiple permutations of the filters as it quickly spirals out of maintainability.

What we ended up with is using a Custom Class and Collection persister to handle our specific cases and overrode the

public String filterFragment(String alias, Map enabledFilters) method to essentially tack the subselect on the filters that have been enabled like this...


Code:
    public String filterFragment(String alias, Map enabledFilters) throws MappingException {
        final StringBuffer sessionFilterFragment = new StringBuffer();
        sessionFilterFragment.append(super.filterFragment(alias, enabledFilters));
        if (versionable && !enabledFilters.isEmpty()) {
            //inject a subselect to the filter clause of abstractVersionable filters
            StringBuffer subselect = new StringBuffer();
            subselect.append(alias).append(".").append(versionTableKeyName).append(" IN (SELECT ").append(alias).append(".").append(versionTableKeyName).append(" FROM ").append(versionTableName).append(" ").append(versionTableName).append(" WHERE ").append(alias).append(".").append(versionTableKeyName).append(" = ").append(versionTableName).append(".").append(versionTableKeyName);
            sessionFilterFragment.insert(0, subselect);
            sessionFilterFragment.append(")");
        }
        return sessionFilterFragment.toString();
    }


Since we're using the same type of structure for our version data we can get away with something like this. Alternatively I was trying to play around with the concept of filter 'groups' that you could group filters together and specify something like a join or subselect that you could reuse in your filters.

However, using the custom persisters will solve our issue for now and drastically reduces the complexity of each individual filter. Using the Custom Persister classes should also allow us to overcome the snarl we were trying to sort out from moving to Xdoclet to Annotations as our template filters are all the same with the table and key names changed and we didn't want to have to specify the same filter over and over with just those things changed.


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.