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 Daniel Carleton <da...@pobox.com> on 2007/04/13 12:18:12 UTC

GroupBy + Discriminator + Polymorphism

Hello,

I'm attempting to map an ArrayList<SuperClass> typed property and
populate it with instances of SubClassFoo and SubClassBar from the
database.

In my query I LEFT JOIN, and then check for NULL primary key values to
determine which type of SubClass each row represents.  Then I use a
<discriminator> and <subMaps> to delegate each row type to the
appropriate mapping.  I also groupBy my containing object's ID to get
all SubClassFoo and SubClassBar objects into the ArrayList<SuperClass>
together.

Only problem is, it's not working as expected.  iBATIS returns an
ArrayList containing two instances of my containing class.  One instance
contains all the SubClassFoo instances, and the other the SubClassBar
instances.

Anyone know how I can achieve a heterogeneous list?  I'm trying to
leverage the sorting capabilities of the database, otherwise I'd just
use two SELECTs, one for each type.

Simplified code below, where Foo and Bar are subclasses of Super.

<sqlMap namespace="Example">
	<resultMap class="Container" id="root" groupBy="id">
		<result property="id" column="id"/>
		<discriminator javaType="String" column="entity_type">
			<subMap value="foo" resultMap="fooSub"/>
			<subMap value="bar" resultMap="barSub"/>
		</discriminator>
	</resultMap>
	<resultMap id="fooSub" class="Container" extends="root">
		<result property="arrayListOfSuper" resultMap="fooMap"/>
	</resultMap>
	<resultMap id="barSub" class="Container" extends="root">
		<result property="arrayListOfSuper" resultMap="barMap"/>
	</resultMap>
	<resultMap id="fooMap" class="Foo">
		<result property="id" column="id">
		<result property="name" column="name">
	</resultMap>
	<resultMap id="barMap" class="Bar">
		<result property="id" column="id">
		<result property="name" column="name">
	</resultMap>
	<statement id="getContainer" resultMap="root">
		SELECT containers.id_container,
			foos.id, foos.name, bars.id, bars.name,
			CASE
			WHEN foos.id IS NULL THEN 'foo'
			WHEN bars.id IS NULL THEN 'bar'
			END AS entity_type

		FROM containers
			LEFT JOIN foos USING(id_container)
			LEFT JOIN bars USING(id_container)
		WHERE containers.id_container = 1
			AND ( foos.id IS NOT NULL OR bars.id IS NOT NULL)
	</statement>	
</sqlMap>

Thoughts?

Thanks,

- Daniel



Re: GroupBy + Discriminator + Polymorphism

Posted by Daniel Carleton <da...@pobox.com>.
Mikael,

Ok, here's what I've come up with so far.

On Fri, 2007-04-13 at 13:52 -0700, Daniel Carleton wrote:
> I'll play around with putting the groupBy in different places, and
> maybe try tracing into the iBATIS source to see what's going on.

Moving the groupBys around didn't have any effect.

I traced into the code, and I think I have the general idea of what's
happening.  It looks like groupBys are per-resultMap, and so the
aggregation is being reset for each subMap.

I haven't quite discovered the spot where this behavior could be cleanly
modified, but here is the one location I have pinned down.

com.ibatis.sqlmap.engine.mapping.result.BasicResultMap.setResultObjectValues

This is where the uniqueKeys are managed per BasicResultMap in the
RequestScope.  Once the mapping moves on to a new instance of
BasicResultMap, it no longer sees the key, and starts a new List.

I don't see any way around this issue for my application, and so I guess
I'll keep working on it.  If anyone has any ideas, I'd be grateful if
you chimed in.

Cheers,

- Daniel



Re: GroupBy + Discriminator + Polymorphism

Posted by Daniel Carleton <da...@pobox.com>.
Hey Mikael,

>        <resultMap id="polyMap" extends="superMap">
>                <discriminator javaType="String" column="entity_type">
>                        <subMap value="foo" resultMap="fooMap"/>
>                        <subMap value="bar" resultMap="barMap"/>
>                </discriminator>
>        </resultMap> 

Yeah, I've tried things like this, but apparently <resultMap>s must
contain at least one <result>.

I'll play around with putting the groupBy in different places, and maybe
try tracing into the iBATIS source to see what's going on.

Thanks for the input.

Cheers,

- Daniel



Re: GroupBy + Discriminator + Polymorphism

Posted by Mikael Andersson <ma...@gmail.com>.
Hmm, my suggestion wasn't very good.
Perhaps groupBy needs to be applied to one of the lower level resultMaps?
Never tried to tackle this usecase myself and would like to know how you
solve it.

- Mike


On 13/04/07, Mikael Andersson <ma...@gmail.com> wrote:
>
> Perhaps something like this (wild guess work :) ) :
>
>      <resultMap class="Container" id="root" groupBy="id">
>                <result property="id" column="id_container"/>
>                <result property="arrayListOfSuper" resultMap="polyMap"/>
>        </resultMap>
>
>        <resultMap id="polyMap" extends="superMap">
>                <discriminator javaType="String" column="entity_type">
>                        <subMap value="foo" resultMap="fooMap"/>
>                        <subMap value="bar" resultMap="barMap"/>
>                </discriminator>
>        </resultMap>
>
>        <resultMap id="superMap" class="Super">
>               <result property="id" column="id"/>
>        </resultMap>
>
>        <resultMap id="fooMap" class="Foo" extends="superMap">
>                <result property="name" column="name">
>        </resultMap>
>        <resultMap id="barMap" class="Bar" extends="superMap">
>                <result property="name" column="name">
>        </resultMap>
>
>
> - Mike
>
> On 13/04/07, Daniel Carleton <da...@pobox.com> wrote:
> >
> > Hello,
> >
> > I'm attempting to map an ArrayList<SuperClass> typed property and
> > populate it with instances of SubClassFoo and SubClassBar from the
> > database.
> >
> > In my query I LEFT JOIN, and then check for NULL primary key values to
> > determine which type of SubClass each row represents.  Then I use a
> > <discriminator> and <subMaps> to delegate each row type to the
> > appropriate mapping.  I also groupBy my containing object's ID to get
> > all SubClassFoo and SubClassBar objects into the ArrayList<SuperClass>
> > together.
> >
> > Only problem is, it's not working as expected.  iBATIS returns an
> > ArrayList containing two instances of my containing class.  One instance
> >
> > contains all the SubClassFoo instances, and the other the SubClassBar
> > instances.
> >
> > Anyone know how I can achieve a heterogeneous list?  I'm trying to
> > leverage the sorting capabilities of the database, otherwise I'd just
> > use two SELECTs, one for each type.
> >
> > Simplified code below, where Foo and Bar are subclasses of Super.
> >
> > <sqlMap namespace="Example">
> >         <resultMap class="Container" id="root" groupBy="id">
> >                 <result property="id" column="id"/>
> >                 <discriminator javaType="String" column="entity_type">
> >                         <subMap value="foo" resultMap="fooSub"/>
> >                         <subMap value="bar" resultMap="barSub"/>
> >                 </discriminator>
> >         </resultMap>
> >         <resultMap id="fooSub" class="Container" extends="root">
> >                 <result property="arrayListOfSuper" resultMap="fooMap"/>
> >         </resultMap>
> >         <resultMap id="barSub" class="Container" extends="root">
> >                 <result property="arrayListOfSuper" resultMap="barMap"/>
> >         </resultMap>
> >         <resultMap id="fooMap" class="Foo">
> >                 <result property="id" column="id">
> >                 <result property="name" column="name">
> >         </resultMap>
> >         <resultMap id="barMap" class="Bar">
> >                 <result property="id" column="id">
> >                 <result property="name" column="name">
> >         </resultMap>
> >         <statement id="getContainer" resultMap="root">
> >                 SELECT containers.id_container,
> >                         foos.id, foos.name, bars.id, bars.name,
> >                         CASE
> >                         WHEN foos.id IS NULL THEN 'foo'
> >                         WHEN bars.id IS NULL THEN 'bar'
> >                         END AS entity_type
> >
> >                 FROM containers
> >                         LEFT JOIN foos USING(id_container)
> >                         LEFT JOIN bars USING(id_container)
> >                 WHERE containers.id_container = 1
> >                         AND ( foos.id IS NOT NULL OR bars.id IS NOT
> > NULL)
> >         </statement>
> > </sqlMap>
> >
> > Thoughts?
> >
> > Thanks,
> >
> > - Daniel
> >
> >
> >
>

Re: GroupBy + Discriminator + Polymorphism

Posted by Daniel Carleton <da...@pobox.com>.
List,

Ok, I created a JIRA ticket for this issue.  If anyone else has run into
it, you can vote below.

https://issues.apache.org/jira/browse/IBATIS-420

- Daniel



Re: GroupBy + Discriminator + Polymorphism

Posted by Mikael Andersson <ma...@gmail.com>.
Perhaps something like this (wild guess work :) ) :

     <resultMap class="Container" id="root" groupBy="id">
               <result property="id" column="id_container"/>
               <result property="arrayListOfSuper" resultMap="polyMap"/>
       </resultMap>

       <resultMap id="polyMap" extends="superMap">
                <discriminator javaType="String" column="entity_type">
                       <subMap value="foo" resultMap="fooMap"/>
                       <subMap value="bar" resultMap="barMap"/>
               </discriminator>
       </resultMap>

       <resultMap id="superMap" class="Super">
              <result property="id" column="id"/>
       </resultMap>

       <resultMap id="fooMap" class="Foo" extends="superMap">
               <result property="name" column="name">
       </resultMap>
       <resultMap id="barMap" class="Bar" extends="superMap">
               <result property="name" column="name">
       </resultMap>


- Mike

On 13/04/07, Daniel Carleton <da...@pobox.com> wrote:
>
> Hello,
>
> I'm attempting to map an ArrayList<SuperClass> typed property and
> populate it with instances of SubClassFoo and SubClassBar from the
> database.
>
> In my query I LEFT JOIN, and then check for NULL primary key values to
> determine which type of SubClass each row represents.  Then I use a
> <discriminator> and <subMaps> to delegate each row type to the
> appropriate mapping.  I also groupBy my containing object's ID to get
> all SubClassFoo and SubClassBar objects into the ArrayList<SuperClass>
> together.
>
> Only problem is, it's not working as expected.  iBATIS returns an
> ArrayList containing two instances of my containing class.  One instance
> contains all the SubClassFoo instances, and the other the SubClassBar
> instances.
>
> Anyone know how I can achieve a heterogeneous list?  I'm trying to
> leverage the sorting capabilities of the database, otherwise I'd just
> use two SELECTs, one for each type.
>
> Simplified code below, where Foo and Bar are subclasses of Super.
>
> <sqlMap namespace="Example">
>         <resultMap class="Container" id="root" groupBy="id">
>                 <result property="id" column="id"/>
>                 <discriminator javaType="String" column="entity_type">
>                         <subMap value="foo" resultMap="fooSub"/>
>                         <subMap value="bar" resultMap="barSub"/>
>                 </discriminator>
>         </resultMap>
>         <resultMap id="fooSub" class="Container" extends="root">
>                 <result property="arrayListOfSuper" resultMap="fooMap"/>
>         </resultMap>
>         <resultMap id="barSub" class="Container" extends="root">
>                 <result property="arrayListOfSuper" resultMap="barMap"/>
>         </resultMap>
>         <resultMap id="fooMap" class="Foo">
>                 <result property="id" column="id">
>                 <result property="name" column="name">
>         </resultMap>
>         <resultMap id="barMap" class="Bar">
>                 <result property="id" column="id">
>                 <result property="name" column="name">
>         </resultMap>
>         <statement id="getContainer" resultMap="root">
>                 SELECT containers.id_container,
>                         foos.id, foos.name, bars.id, bars.name,
>                         CASE
>                         WHEN foos.id IS NULL THEN 'foo'
>                         WHEN bars.id IS NULL THEN 'bar'
>                         END AS entity_type
>
>                 FROM containers
>                         LEFT JOIN foos USING(id_container)
>                         LEFT JOIN bars USING(id_container)
>                 WHERE containers.id_container = 1
>                         AND ( foos.id IS NOT NULL OR bars.id IS NOT NULL)
>         </statement>
> </sqlMap>
>
> Thoughts?
>
> Thanks,
>
> - Daniel
>
>
>