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:45:59 GMT
I also noticed that you mentioned a "web application."  As a web app, I
doubt you really need 'lazy loading' and my guess is you'd be just fine
without it.

On Mon, Dec 1, 2008 at 4:42 PM, Rick <rickcr@gmail.com> wrote:

> 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
>



-- 
Rick

Mime
View raw message