You are viewing a plain text version of this content. The canonical link for it is here.
Posted to user-java@ibatis.apache.org by Amin Ahmad <am...@gmail.com> on 2007/08/08 20:05:45 UTC

Splitting one table row into a master object with sub-objects

Hello,

I am working with a 103-column table that tracks a variety of information
for a provider. Rather than modeling this as a single class with 103
attributes, I have defined a top-level class that contains a few core
provider attributes, and "subject-area" classes that group disjoint subsets
of attributes by domain ( e.g. Financial, Metrics, Demographics). The top
level bean contains an attribute for each of these "subject-area" classes.

My problem lies in effectively using iBatis to populate this structure from
a table row. My current approach is to use a custom type handler to populate
the subject area bean. The example below shows how I am populating the
"metrics" attribute in the resultMap of the top-level provider class.

   <result property="metrics"
           column="QT_ETHR_AVLB_RSTR"
           typeHandler="
com.cgi.wasacwis.typehandlers.ProviderMetricsTypeHandler"/>

The metrics attribute is actually a "subject-area" class that holds ~20
attributes from the row. The custom type handler, ProviderMetricsTypeHandler ,
contains code for creating and populating the fields. To illustrate, I have
included the method "getResult" below:

    private ProviderMetrics getResult(ResultSet r) throws SQLException {
        ProviderMetrics result =
WasacwisFactory.eINSTANCE.createProviderMetrics();

        result.setEitherAvailableRestricted        (r.getLong
("QT_ETHR_AVLB_RSTR"));
        result.setEitherProviderRestricted         (r.getLong
("QT_ETHR_PRVD_RSTR"));
        result.setFemaleAvailableRestricted        (r.getLong
("QT_FEML_AVLB_RSTR"));
        result.setFemaleProviderRestricted
(r.getLong("QT_FEML_PRVD_RSTR"));

        result.setMaleAvailableRestricted        (r.getLong
("QT_MALE_AVLB_RSTR"));
        result.setMaleProviderRestricted        (r.getLong
("QT_MALE_PRVD_RSTR"));
        result.setProviderParticipantsUnder18     (r.getLong
("QT_PRVD_PART_UND18"));
        result.setProviderParticipantsUnder2    (r.getLong
("QT_PRVD_PART_UNDR2"));
        result.setTotalAvailableBedCapacity
(r.getLong("QT_TOT_AVLB_BEDCAP"));

        result.setTotalAvailableRestricted        (r.getLong
("QT_TOT_AVLB_RSTR"));
        result.setTotalAvailableUnder18            (r.getLong
("QT_TOT_AVLB_UNDR18"));
        result.setTotalAvailableUnder2             (r.getLong
("QT_TOT_AVLB_UNDR2"));
        result.setTotalEitherPlaced                (r.getLong
("QT_TOT_ETHR_PLCD"));
        result.setTotalEitherReserved
(r.getLong("QT_TOT_ETHR_RSVD"));

        result.setTotalFemalePlaced                (r.getLong
("QT_TOT_FEML_PLCD"));
        result.setTotalFemaleReserved            (r.getLong
("QT_TOT_FEML_RSVD"));
        result.setTotalMalePlaced                 (r.getLong
("QT_TOT_MALE_PLCD"));
        result.setTotalMaleReserved                (r.getLong
("QT_TOT_MALE_RSVD"));
        result.setTotalPlaced                    (r.getLong("QT_TOT_PLCD"));

        result.setTotalPlacedUnder18            (r.getLong
("QT_TOT_PLCD_UNDR18"));
        result.setTotalPlacedUnder2                (r.getLong
("QT_TOT_PLCD_UNDR2"));
        result.setTotalProviderBedCapacity         (r.getLong
("QT_TOT_PRVD_BEDCAP"));
        result.setTotalProviderRestricted        (r.getLong
("QT_TOT_PRVD_RSTR"));
        result.setTotalReserved                    (r.getLong("QT_TOT_RSVD"));

        result.setTotalReservedUnder18            (r.getLong
("QT_TOT_RSVD_UNDR18"));
        result.setTotalReservedUnder2            (r.getLong
("QT_TOT_RSVD_UNDR2"));

        return result;
    }

Needless to say, I am writing the entire mapping for the provider metrics
class in Java, and this isn't making good use of iBatis. Can anyone suggest
a better strategy?

thank you,
Amin

Re: Splitting one table row into a master object with sub-objects

Posted by Ole Trenner <ol...@gmx.de>.
Hi,

Amin Ahmad wrote:
> Thanks very much for your help, Ole. This solved my problems, though I
> would like to add a few of my own observations:
> 
> (1) Is it necessary to create the composite result map
> (id="CompositeTopResult")? I added the lines below:
>       <result property="subClassProperty1" resultMap="SubResult1"/>
>       <result property="subClassProperty2" resultMap="SubResult2"/>
>     Directly into the "TopResult" resultMap and had no problems.


Both approaches should be equivalent. I simply prefer the one with the
extended "base" resultMap because we often start with abator generated
"flat" result maps. Since we don't want to change generated code/config,
we implement our own functionality by extending those artifacts.


> (2) The resultMap/result syntax diagram on page 31 of the manual seems
> to suggest that column is a mandatory attribute of result when in fact
> it is not. This contributed to my initial confusion.


The pdf doc says on page 35:

"In iBATIS versions 2.2.0 and above, you can also reuse a result map in
a 1:1 query instead of repeating the columns."


> (3) One oddity I encountered was that the resultMap reference needs to
> be fully qualified with the name-space in front. I don't know if this is
> a bug of feature, but simply using resultMap="Subresult1" resulted in an
> error saying the resultMap is not defined. When I qualified it with the
> namespace, everything worked, even though both result maps (TopResult
> and SubResult1) are defined in the same namespace.


I'm not quite sure, but you should be able to configure the behaviour
with the useStatementNamespaces attribute of the settings element in
your sqlMapConfig:


<sqlMapConfig>
    <settings useStatementNamespaces="false"/>
	...
</sqlMapConfig>


> Thanks again for you help.
> amin

Best regards,
Ole.

Re: Splitting one table row into a master object with sub-objects

Posted by Amin Ahmad <am...@gmail.com>.
Thanks very much for your help, Ole. This solved my problems, though I would
like to add a few of my own observations:

(1) Is it necessary to create the composite result map
(id="CompositeTopResult")? I added the lines below:
      <result property="subClassProperty1" resultMap="SubResult1"/>
      <result property="subClassProperty2" resultMap="SubResult2"/>
    Directly into the "TopResult" resultMap and had no problems.
(2) The resultMap/result syntax diagram on page 31 of the manual seems to
suggest that column is a mandatory attribute of result when in fact it is
not. This contributed to my initial confusion.
(3) One oddity I encountered was that the resultMap reference needs to be
fully qualified with the name-space in front. I don't know if this is a bug
of feature, but simply using resultMap="Subresult1" resulted in an error
saying the resultMap is not defined. When I qualified it with the namespace,
everything worked, even though both result maps (TopResult and SubResult1)
are defined in the same namespace.

Thanks again for you help.
amin

On 8/9/07, Ole Trenner <ol...@gmx.de> wrote:
>
> If I get you right you could use a resultMap that itself uses other
> resultMap instances to populate its subclass attributes.
>
> Lets say, you have got a select like "SELECT * FROM mytable" that
> returns a resultSet of 103 columns, several of which get mapped to the
> top-level class with a resultMap like so:
>
> <resultMap class="package.TopClass" id="TopResult">
>     <result column="id" property="id"/>
>     <result column="property1" property="property1"/>
>     <result column="property2" property="property2"/>
>         ...
>     <result column="propertyN" property="propertyN"/>
> </resultMap>
>
> Now you create a resultMap for every subset class like so:
>
> <resultMap class="package.SubClass1" id="SubResult1">
>     <result column="subProperty1" property="subProperty1"/>
>     <result column="subProperty2" property="subProperty2"/>
>         ...
> </resultMap>
>
> <resultMap class="package.SubClass2" id="SubResult2">
>     <result column="subProperty3" property="subProperty3"/>
>     <result column="subProperty4" property="subProperty4"/>
>         ...
> </resultMap>
>
> Now you can create a "composite" resultMap that uses all those
> resultMaps to create a TopClass instance filled with each of its
> properties and its SubClass properties:
>
> <resultMap class="package.TopClass" extends="TopResult"
>         id="CompositeTopResult">
>     <result property="subClassProperty1" resultMap="SubResult1"/>
>     <result property="subClassProperty2" resultMap="SubResult2"/>
> </resultMap>
>
> Your select now can use this resultMap to populate a complete TopClass
> instance with both its flat fields and its subclass instances.
>
> Hope that helps :)
> Best regards -
> Ole.
>
>
> Amin Ahmad wrote:
> > Hello,
> >
> > I am working with a 103-column table that tracks a variety of
> > information for a provider. Rather than modeling this as a single class
> > with 103 attributes, I have defined a top-level class that contains a
> > few core provider attributes, and "subject-area" classes that group
> > disjoint subsets of attributes by domain ( e.g. Financial, Metrics,
> > Demographics). The top level bean contains an attribute for each of
> > these "subject-area" classes.
> >
> > My problem lies in effectively using iBatis to populate this structure
> > from a table row. My current approach is to use a custom type handler to
> > populate the subject area bean. The example below shows how I am
> > populating the "metrics" attribute in the resultMap of the top-level
> > provider class.
> >
> >    <result property="metrics"
> >            column="QT_ETHR_AVLB_RSTR"
> >
> > typeHandler="com.cgi.wasacwis.typehandlers.ProviderMetricsTypeHandler"/>
> >
> > The metrics attribute is actually a "subject-area" class that holds ~20
> > attributes from the row. The custom type handler,
> > ProviderMetricsTypeHandler , contains code for creating and populating
> > the fields. To illustrate, I have included the method "getResult" below:
> >
> >     private ProviderMetrics getResult(ResultSet r) throws SQLException {
> >         ProviderMetrics result =
> > WasacwisFactory.eINSTANCE.createProviderMetrics();
> >
> >         result.setEitherAvailableRestricted
> > (r.getLong("QT_ETHR_AVLB_RSTR"));
> >         result.setEitherProviderRestricted
> > (r.getLong("QT_ETHR_PRVD_RSTR"));
> >         result.setFemaleAvailableRestricted
> > (r.getLong("QT_FEML_AVLB_RSTR"));
> >         result.setFemaleProviderRestricted
> > (r.getLong("QT_FEML_PRVD_RSTR"));
> >         result.setMaleAvailableRestricted
> > (r.getLong("QT_MALE_AVLB_RSTR"));
> >         result.setMaleProviderRestricted
> > (r.getLong("QT_MALE_PRVD_RSTR"));
> >         result.setProviderParticipantsUnder18
> > (r.getLong("QT_PRVD_PART_UND18"));
> >         result.setProviderParticipantsUnder2
> > (r.getLong("QT_PRVD_PART_UNDR2"));
> >         result.setTotalAvailableBedCapacity
> > (r.getLong("QT_TOT_AVLB_BEDCAP"));
> >         result.setTotalAvailableRestricted
> > (r.getLong("QT_TOT_AVLB_RSTR"));
> >         result.setTotalAvailableUnder18
> > (r.getLong("QT_TOT_AVLB_UNDR18"));
> >         result.setTotalAvailableUnder2
> > (r.getLong("QT_TOT_AVLB_UNDR2"));
> >         result.setTotalEitherPlaced
> > (r.getLong("QT_TOT_ETHR_PLCD"));
> >         result.setTotalEitherReserved
> > (r.getLong("QT_TOT_ETHR_RSVD"));
> >         result.setTotalFemalePlaced
> > (r.getLong("QT_TOT_FEML_PLCD"));
> >         result.setTotalFemaleReserved
> > (r.getLong("QT_TOT_FEML_RSVD"));
> >         result.setTotalMalePlaced
> > (r.getLong("QT_TOT_MALE_PLCD"));
> >         result.setTotalMaleReserved
> > (r.getLong("QT_TOT_MALE_RSVD"));
> >         result.setTotalPlaced
> > (r.getLong("QT_TOT_PLCD"));
> >         result.setTotalPlacedUnder18
> > (r.getLong("QT_TOT_PLCD_UNDR18"));
> >         result.setTotalPlacedUnder2
> > (r.getLong("QT_TOT_PLCD_UNDR2"));
> >         result.setTotalProviderBedCapacity
> > (r.getLong("QT_TOT_PRVD_BEDCAP"));
> >         result.setTotalProviderRestricted
> > (r.getLong("QT_TOT_PRVD_RSTR"));
> >         result.setTotalReserved
> > (r.getLong("QT_TOT_RSVD"));
> >         result.setTotalReservedUnder18
> > (r.getLong("QT_TOT_RSVD_UNDR18"));
> >         result.setTotalReservedUnder2
> > (r.getLong("QT_TOT_RSVD_UNDR2"));
> >
> >         return result;
> >     }
> >
> > Needless to say, I am writing the entire mapping for the provider
> > metrics class in Java, and this isn't making good use of iBatis. Can
> > anyone suggest a better strategy?
> >
> > thank you,
> > Amin
>
>

Re: Splitting one table row into a master object with sub-objects

Posted by Ole Trenner <ol...@gmx.de>.
If I get you right you could use a resultMap that itself uses other
resultMap instances to populate its subclass attributes.

Lets say, you have got a select like "SELECT * FROM mytable" that
returns a resultSet of 103 columns, several of which get mapped to the
top-level class with a resultMap like so:

<resultMap class="package.TopClass" id="TopResult">
    <result column="id" property="id"/>
    <result column="property1" property="property1"/>
    <result column="property2" property="property2"/>
	...
    <result column="propertyN" property="propertyN"/>
</resultMap>

Now you create a resultMap for every subset class like so:

<resultMap class="package.SubClass1" id="SubResult1">
    <result column="subProperty1" property="subProperty1"/>
    <result column="subProperty2" property="subProperty2"/>
	...
</resultMap>

<resultMap class="package.SubClass2" id="SubResult2">
    <result column="subProperty3" property="subProperty3"/>
    <result column="subProperty4" property="subProperty4"/>
	...
</resultMap>

Now you can create a "composite" resultMap that uses all those
resultMaps to create a TopClass instance filled with each of its
properties and its SubClass properties:

<resultMap class="package.TopClass" extends="TopResult"
	id="CompositeTopResult">
    <result property="subClassProperty1" resultMap="SubResult1"/>
    <result property="subClassProperty2" resultMap="SubResult2"/>
</resultMap>

Your select now can use this resultMap to populate a complete TopClass
instance with both its flat fields and its subclass instances.

Hope that helps :)
Best regards -
Ole.


Amin Ahmad wrote:
> Hello,
> 
> I am working with a 103-column table that tracks a variety of
> information for a provider. Rather than modeling this as a single class
> with 103 attributes, I have defined a top-level class that contains a
> few core provider attributes, and "subject-area" classes that group
> disjoint subsets of attributes by domain ( e.g. Financial, Metrics,
> Demographics). The top level bean contains an attribute for each of
> these "subject-area" classes.
> 
> My problem lies in effectively using iBatis to populate this structure
> from a table row. My current approach is to use a custom type handler to
> populate the subject area bean. The example below shows how I am
> populating the "metrics" attribute in the resultMap of the top-level
> provider class.
> 
>    <result property="metrics"
>            column="QT_ETHR_AVLB_RSTR"
>           
> typeHandler="com.cgi.wasacwis.typehandlers.ProviderMetricsTypeHandler"/>
> 
> The metrics attribute is actually a "subject-area" class that holds ~20
> attributes from the row. The custom type handler,
> ProviderMetricsTypeHandler , contains code for creating and populating
> the fields. To illustrate, I have included the method "getResult" below:
> 
>     private ProviderMetrics getResult(ResultSet r) throws SQLException {
>         ProviderMetrics result =
> WasacwisFactory.eINSTANCE.createProviderMetrics();
> 
>         result.setEitherAvailableRestricted       
> (r.getLong("QT_ETHR_AVLB_RSTR"));
>         result.setEitherProviderRestricted        
> (r.getLong("QT_ETHR_PRVD_RSTR"));
>         result.setFemaleAvailableRestricted       
> (r.getLong("QT_FEML_AVLB_RSTR"));
>         result.setFemaleProviderRestricted       
> (r.getLong("QT_FEML_PRVD_RSTR"));
>         result.setMaleAvailableRestricted       
> (r.getLong("QT_MALE_AVLB_RSTR"));
>         result.setMaleProviderRestricted       
> (r.getLong("QT_MALE_PRVD_RSTR"));
>         result.setProviderParticipantsUnder18    
> (r.getLong("QT_PRVD_PART_UND18"));
>         result.setProviderParticipantsUnder2   
> (r.getLong("QT_PRVD_PART_UNDR2"));
>         result.setTotalAvailableBedCapacity       
> (r.getLong("QT_TOT_AVLB_BEDCAP"));
>         result.setTotalAvailableRestricted       
> (r.getLong("QT_TOT_AVLB_RSTR"));
>         result.setTotalAvailableUnder18           
> (r.getLong("QT_TOT_AVLB_UNDR18"));
>         result.setTotalAvailableUnder2            
> (r.getLong("QT_TOT_AVLB_UNDR2"));
>         result.setTotalEitherPlaced               
> (r.getLong("QT_TOT_ETHR_PLCD"));
>         result.setTotalEitherReserved           
> (r.getLong("QT_TOT_ETHR_RSVD"));
>         result.setTotalFemalePlaced               
> (r.getLong("QT_TOT_FEML_PLCD"));
>         result.setTotalFemaleReserved           
> (r.getLong("QT_TOT_FEML_RSVD"));
>         result.setTotalMalePlaced                
> (r.getLong("QT_TOT_MALE_PLCD"));
>         result.setTotalMaleReserved               
> (r.getLong("QT_TOT_MALE_RSVD"));
>         result.setTotalPlaced                   
> (r.getLong("QT_TOT_PLCD"));
>         result.setTotalPlacedUnder18           
> (r.getLong("QT_TOT_PLCD_UNDR18"));
>         result.setTotalPlacedUnder2               
> (r.getLong("QT_TOT_PLCD_UNDR2"));
>         result.setTotalProviderBedCapacity        
> (r.getLong("QT_TOT_PRVD_BEDCAP"));
>         result.setTotalProviderRestricted       
> (r.getLong("QT_TOT_PRVD_RSTR"));
>         result.setTotalReserved                   
> (r.getLong("QT_TOT_RSVD"));
>         result.setTotalReservedUnder18           
> (r.getLong("QT_TOT_RSVD_UNDR18"));
>         result.setTotalReservedUnder2           
> (r.getLong("QT_TOT_RSVD_UNDR2"));
>        
>         return result;
>     }
> 
> Needless to say, I am writing the entire mapping for the provider
> metrics class in Java, and this isn't making good use of iBatis. Can
> anyone suggest a better strategy?
> 
> thank you,
> Amin