ibatis-user-java mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Carlos Pita" <carlosjosep...@gmail.com>
Subject Laziness, downcasting and <discriminator,id> associations
Date Mon, 01 Dec 2008 15:51:31 GMT
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

Mime
View raw message