ibatis-user-java mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Rick <ric...@gmail.com>
Subject Re: Laziness, downcasting and <discriminator,id> associations
Date Mon, 01 Dec 2008 21:42:54 GMT
I haven't read the entire post, but when I saw all your comments on "lazy,"
I started to wonder what you are needing it for.

I'm not an expert in iBATIS by any stretch but I typically find I don't need
the lazy features and I have some pretty complex relationships.  (Side note,
I'm also coverting an existing Hibernate application to iBATIS.)

If I need all the data up front, yes, I will create one query to get it all
and iBATIS handles it just fine. You mentioned a huge cartesian result set -
if that were the case, I wouldn't think you would need it all up front in
which case I still don't rely on the lazy approach.

A DAO method is so small and simple, why not just call a dao with what you
need if there is some sort of drill down? I guess I'd have to see more of
your exact user experience of what you are doing since that does make a
difference. Can you explain more what you are doing with your collection
that you return? Are you going over every element and then calling a
property which triggers the lazy load?  If that were the case, I don't think
a lazy solution is good either, since lazy only makes sense (imo) if you
aren't sure you need all the data (and even then I don't get what the big
deal is.)

In my case, my dao methods are vey lightweight...

public NPPSystem findBySystemName(String nppSystemName) {
        return (NPPSystem)
getSqlMapClientTemplate().queryForObject("NPPSystem.find", nppSystemName );
    }

If I wanted I could always just call that dao to get what I need.
In other words, doing ..

Foo foo = ContainingObject.getFoo();   //lazy load Foo

isn't much less typing than

Foo foo = fooDao.findFoo(ContaingObject.getFooID()); //get Foo from DAO call

So I never really found a need to rely on "lazy" features. (Typically the
business requirements don't work that way either. For example you typically
need "everything" populated or your UI goes through some step where you want
to drill down more at which point I just call my service/dao and get what I
need.

I rambled on fast so sorry if I didn't make much sense. I can clarify more
if you like.


On Mon, Dec 1, 2008 at 12:22 PM, Carlos Pita <carlosjosepita@gmail.com>wrote:

>
>
>> I have to admit that I never used the lazy features of iBATIS. Lazyness by
>> Default was one of the reasons why
>> I moved over from hibernate to iBATIS. And I know a bunch of other devs
>> that are not and will not use lazy
>> loading. Imho it does not make sense for web applications or restful
>> service interfaces and introduces a lot
>> of problems (open session in view, serialization of result sets,
>> performance etc.).
>
>
> Could you please develop your position a bit more? I'm interested in any
> approach that gets rid of any amount of magic that works behind the scenes.
> If avoiding all laziness happens to be a workable approach, I'll be all for
> it.
>
> For example, to trigger the discussion:
>
> Do you work out a query per use case that eager fetches (by means of joins
> and/or immediate subselects) the exact graph needed for that use case?
>
> There are obvious problems with join fetches for entities that have more
> than one 1-N relationships because of the potentially huge cartesian product
> that they generate; I can figure out more or less laboriuos solutions to
> them using ibatis to fetch and map one or more of the associated collections
> by separate queries and then merging the results. And anyway lazy fetching
> would avoid the cartesian product to incur in the even worse n+1 problem.
> How do you approach this kind of situation?
>
> Then, there are bidirectional associations, which I currently implement
> keeping a memory cache with weak references to get the N-1 side
> (child.getParent() should be a cache hit). Eagerness would imply an extra
> query here, for a parent that more often than not has been already fetched.
>
> That are some of the many issues that came to my mind.
>
> > Nevertheless: feel free to create a jira issue with your proposed
> patches. And it would be great if you could
> > link to all the other lazy issues that are related. Maybe we should start
> a vote about the direction of lazy
> > loading with iBATIS in the future...
>
> Sure.
>
>
> Best regards
> -Carlos
>
>
>>
>>
>> I'm not sure what the plans are for iBATIS 3.0. I've seen quite different
>> opinions regarding lazyness on the
>> whiteboard (
>> http://opensource.atlassian.com/confluence/oss/display/IBATIS/iBATIS+3.0+Whiteboard)
>> but I don't
>> know the status. @Clinton: any news about that?
>>
>>
>>
>>
>> Regards
>>
>> Kai
>>
>>
>> --- Original Nachricht ---
>> Absender: Carlos Pita
>> Datum: 01.12.2008 16:51
>> > Hi,
>> >
>> > I'm evaluating the possibility to migrate a web application that is
>> already
>> > in production from hibernate to ibatis. As a part of this I'm
>> implementing
>> > skeletal versions of our most complex mappings, as a proof of concept.
>> The
>> > application contains some overlapping hierarchies rooted at interfaces
>> that
>> > are certainly hard to map into hibernate entities. For example, many
>> > different things can be commentable, rateable, abusable (meaning that
>> abuses
>> > can be reported on them), etc. These concepts are naturally modelled as
>> > interfaces. Bidirectional associations exist between, say, commentables
>> and
>> > comments:
>> >
>> > commentable <-----------*> comment
>> >
>> > Following the example, a product and a user profile are commentables:
>> >
>> >     commentable <---------*> comment
>> >          ^   ^
>> >         |   |
>> >     product  profile
>> >
>> > Of course, both product and profile belong to different class
>> hierarchies
>> > besides being commentable. The link from comment to commentable could be
>> > implemented as pointing to an union select of product and profile, or
>> using
>> > a target table discriminator column as in:
>> >
>> > comment {
>> >   text
>> >    commentable_kind : { product, profile }
>> >   commentable_id
>> > }
>> >
>> > I've found at least two ways to satisfactorily map the above using
>> ibatis,
>> > which encourages me to continue my evaluation and eventually get into
>> the
>> > (considerable) work of migrating the entire app to ibatis.
>> >
>> > But then I've to say that I bumped my head against issue #1 here
>> >
>> >
>> http://opensource.atlassian.com/confluence/oss/display/IBATIS/Lazy+loading+issues
>> >
>> > That happened when I expected to get the commentable product out of a
>> > comment: (Product)(comment.getCommentable()).
>> >
>> > Nevertheless, I found out the following patch
>> >
>> > https://issues.apache.org/jira/browse/IBATIS-463
>> >
>> > which is fine but only a kludge for the very specific situation. Why not
>> > allow laziness to be specified on a per subselect basis (of course,
>> taking
>> > into account the default app-wide setting also)? Required changes to the
>> > source are minimal (the ResultMapping could be passed to loadResult in
>> order
>> > it to check for the lazy attribute), and that solves the lazy downcast
>> > problem but also provides a finer control over laziness/eagerness of the
>> > association. The logic to enable laziness would then be:
>> > global-laziness-enabled && local-laziness-enabled -> enable laziness
for
>> > this subselect.
>> >
>> > That said, for associations specified by a pair <discriminator,id> as
>> above,
>> > the discriminator is available *before* fetching the associated entity.
>> One
>> > way of mapping the aforementioned association is:
>> >
>> > <resultMap id="commentMap" class="comment">
>> >     ....
>> >    <discriminator column="commentable_kind" javaType="int">
>> >         <subMap value="1" resultMap="productCommentableMap"/>
>> >        <subMap value="2" resultMap="profileCommentableMap"/>
>> >     </discriminator>
>> > </resultMap>
>> >
>> > <resultMap id="productCommentMap" class="comment" extends="commentMap">
>> >      ....
>> >     <result property="commentable" column="commentable_id"
>> > select="selectProductById" />
>> > </resultMap>
>> >
>> > <resultMap id="profileCommentMap" class="comment" extends="commentMap">
>> >      ....
>> >     <result property="commentable" column="commentable_id"
>> > select="selectProfileById" />
>> > </resultMap>
>> >
>> > Obviously selectProductById would be mapped by a productMap which builds
>> a
>> > product and selectProfileById by a profileMap which builds a profile:
>> >
>> > <select id="selectProductById" resultMap="productMap" (or
>> > resultClass="product")>
>> >     ....
>> > </select>
>> >
>> > <select id="selectProfileById" resultMap="profileMap" (or
>> > resultClass="profile")>
>> >     ....
>> > </select>
>> >
>> > But despite the concrete class info is available, as far as the
>> association
>> > is lazy ibatis will generate a commentable proxy, not a product or
>> profile
>> > one.
>> >
>> > I think it would be an improvement if instead of just considering the
>> class
>> > in the bean signature ibatis also took into account the class of the
>> > resultmap involved, and favoured the "more concrete" one, because
>> sometimes
>> > the mapping containes more precise information about the specific use
>> case
>> > than the general interface. In this case, product and profile would be
>> > picked instead of just commentable. This way, we have a lazy and
>> > downcasteable polymorphic association (for this kind of mapping).
>> >
>> > Briefly, I'm proposing two minor changes for the java version:
>> >
>> > 1) add a lazy attribute to the result tag that takes effect only when
>> the
>> > select attribute is also given. This attribute won't activate laziness
>> if
>> > it's globally disabled, but will deactivate it when it's globally
>> enabled.
>> >
>> > 2) when creating a 1:1 proxy prefer the more concrete class between the
>> one
>> > given in the property signature and the one given in the resultMap for
>> the
>> > "delayed" query.
>> >
>> > I'm going to implement these changes anyway, because I need them. But I
>> > really would like to know what do you think about the ideas and whether
>> > you're also interested in patching the 2.x trunk or not.
>> >
>> > Before I mentioned that I found out two ways of mapping the
>> > <discriminator,id> association. The second one involves using a dynamic
>> > select instead of two submaps. This select (say
>> > selectCommentableFromKindAndId) chooses the target table (product or
>> > profile) according to the discriminator (kind) value. But I realized
>> that
>> > the resultMap for a dynamic select is itself static, somehow diminishing
>> the
>> > flexibility that dynamic tags introduce. Of course, the resultMap for
>> the
>> > select could contain a discriminator to alleviate the problem. But I
>> think
>> > that the resultMap is so closely related to the query that the
>> possibility
>> > to modify the second (by means of dynamic tags) should be accompanied by
>> the
>> > possibility to modify the first based on dynamic criteria. What do you
>> > think?
>> >
>> > Best regards
>> > -Carlos
>> >
>>
>>
>


-- 
Rick

Mime
View raw message