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 Jerome Jacobsen <je...@gentootech.com> on 2004/12/10 16:03:16 UTC

Avoiding N+1 with complex HashMap properties.

Hello,

I'm using SQL Maps 2.08.  I was hoping I could do the following
but I get a ProbeException.  Should this be supported?

<resultMap id="get-customer-period-result"
           class="java.util.HashMap">
  <result property="customerNum"
          column="CUSTOMER_NUMBER"
          columnIndex="1"/>
  <result property="category.id"
          column="CATEGORY_ID"
          columnIndex="2"/>
  <result property="category.parentId"
          column="PARENT_CATEGORY_ID"
          columnIndex="3"/>
  <result property="category.description"
          column="CATEGORY_DESC"
          columnIndex="4"/>
  ...
</resultMap>

<statement id="getCustomerPeriodSummary"
           resultMap="get-customer-period-result">
  SELECT CPS.CUSTOMER_NUMBER,
         PC.CATEGORY_ID,
         PC.PARENT_CATEGORY_ID,
         PC.DESCRIPTION AS CATEGORY_DESC,
         PC.SEQUENCE AS CATEGORY_SEQUENCE,
         ...
    FROM CUSTOMER_PERIOD_SUMMARY CPS, PRODUCT_CATEGORY PC, SALES_PERIOD SP
   WHERE CPS.CUSTOMER_NUMBER = #value#
     AND CPS.CATEGORY_ID = PC.CATEGORY_ID
     AND CPS.PERIOD_ID = SP.PERIOD_ID
ORDER BY CATEGORY_SEQUENCE
</statement>

I was hoping that sqlmaps would store the 'category.id',
'category.parentId', 'category.description' either in a nested
HashMap (key of 'category') or store the values in the top
level HashMap with those keys ('category.id', etc.)

However neither case appears to happen and I get the
exception below.  I realize that I can map the complex
properties with separate selects but I want to avoid the
N+1 query problem.  So I do the join instead.

com.ibatis.common.beans.ProbeException: There is no WRITEABLE property named
'id' in class 'java.lang.Object'
	at com.ibatis.common.beans.ClassInfo.getSetter(ClassInfo.java:146)
	at
com.ibatis.common.beans.ComplexBeanProbe.setProperty(ComplexBeanProbe.java:3
34)
	at
com.ibatis.common.beans.ComplexBeanProbe.setObject(ComplexBeanProbe.java:231
)
	at com.ibatis.common.beans.GenericProbe.setObject(GenericProbe.java:69)
	at
com.ibatis.sqlmap.engine.exchange.ComplexDataExchange.setData(ComplexDataExc
hange.java:87)
	at
com.ibatis.sqlmap.engine.mapping.result.BasicResultMap.setResultObjectValues
(BasicResultMap.java:231)
	at
com.ibatis.sqlmap.engine.mapping.statement.RowHandlerCallback.handleResultOb
ject(RowHandlerCallback.java:63)
	at
com.ibatis.sqlmap.engine.execution.SqlExecutor.handleResults(SqlExecutor.jav
a:350)
	at
com.ibatis.sqlmap.engine.execution.SqlExecutor.executeQuery(SqlExecutor.java
:179)
	at
com.ibatis.sqlmap.engine.mapping.statement.GeneralStatement.sqlExecuteQuery(
GeneralStatement.java:200)
	at
com.ibatis.sqlmap.engine.mapping.statement.GeneralStatement.executeQueryWith
Callback(GeneralStatement.java:168)
	at
com.ibatis.sqlmap.engine.mapping.statement.GeneralStatement.executeQueryForL
ist(GeneralStatement.java:118)
	at
com.ibatis.sqlmap.engine.impl.SqlMapExecutorDelegate.queryForList(SqlMapExec
utorDelegate.java:626)
	at
com.ibatis.sqlmap.engine.impl.SqlMapExecutorDelegate.queryForList(SqlMapExec
utorDelegate.java:598)
	at
com.ibatis.sqlmap.engine.impl.SqlMapSessionImpl.queryForList(SqlMapSessionIm
pl.java:107)
	at
org.springframework.orm.ibatis.SqlMapClientTemplate$3.doInSqlMapClient(SqlMa
pClientTemplate.java:202)
	at
org.springframework.orm.ibatis.SqlMapClientTemplate.execute(SqlMapClientTemp
late.java:142)
	at
org.springframework.orm.ibatis.SqlMapClientTemplate.executeWithListResult(Sq
lMapClientTemplate.java:164)
	at
org.springframework.orm.ibatis.SqlMapClientTemplate.queryForList(SqlMapClien
tTemplate.java:200)
	at
com.giv.dashboard.dao.db.ibatis.SqlMapSalesStatisticsDAO.getCategoryStatsFor
Customer(SqlMapSalesStatisticsDAO.java:28)
	at
com.giv.dashboard.DashboardImpl.getCategorySummaries(DashboardImpl.java:42)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39
)
	at
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl
.java:25)
	at java.lang.reflect.Method.invoke(Method.java:324)
	at
org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopU
tils.java:295)
	at
org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint
(ReflectiveMethodInvocation.java:154)
	at
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(Reflect
iveMethodInvocation.java:121)
	at
org.springframework.transaction.interceptor.TransactionInterceptor.invoke(Tr
ansactionInterceptor.java:56)
	at
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(Reflect
iveMethodInvocation.java:143)
	at
org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopPro
xy.java:174)
	at $Proxy0.getCategorySummaries(Unknown Source)
	at
com.giv.dashboard.web.spring.ViewCategoriesController.handleRequestInternal(
ViewCategoriesController.java:62)
	at
org.springframework.web.servlet.mvc.AbstractController.handleRequest(Abstrac
tController.java:128)
	at
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter.handle(Si
mpleControllerHandlerAdapter.java:44)
	at
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServle
t.java:532)
	at
org.springframework.web.servlet.FrameworkServlet.serviceWrapper(FrameworkSer
vlet.java:366)
	at
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java
:317)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:740)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:853)
	at
com.evermind.server.http.ResourceFilterChain.doFilter(ResourceFilterChain.ja
va:65)
	at oracle.security.jazn.oc4j.JAZNFilter.doFilter(Unknown Source)
	at
com.evermind.server.http.ServletRequestDispatcher.invoke(ServletRequestDispa
tcher.java:604)
	at
com.evermind.server.http.ServletRequestDispatcher.forwardInternal(ServletReq
uestDispatcher.java:317)
	at
com.evermind.server.http.HttpRequestHandler.processRequest(HttpRequestHandle
r.java:790)
	at
com.evermind.server.http.HttpRequestHandler.run(HttpRequestHandler.java:270)
	at
com.evermind.server.http.HttpRequestHandler.run(HttpRequestHandler.java:112)
	at
com.evermind.util.ReleasableResourcePooledExecutor$MyWorker.run(ReleasableRe
sourcePooledExecutor.java:192)
	at java.lang.Thread.run(Thread.java:534)


RE: Avoiding N+1 with complex HashMap properties.

Posted by Jerome Jacobsen <je...@gentootech.com>.
Darn typo!  Option 2 should say:

HashMap customer = customerDao.getCustomer(id);
HashMap address = customer.get("address");
out.println("customer state = " + address.get("state"));

customer.get("address") instead of customerDao.get("address")

We're getting the 'address' HashMap from the 'customer'
HashMap.

> -----Original Message-----
> From: Jerome Jacobsen [mailto:jerome.jacobsen@gentootech.com]
> Sent: Friday, December 10, 2004 12:49 PM
> To: ibatis-user-java@incubator.apache.org
> Subject: RE: Avoiding N+1 with complex HashMap properties.
>
>
> Just to be clear, here are examples of what I'm talking about.
>
> Option 1:  Single hashmap with indexed property style keys.
>
> HashMap customer = customerDao.getCustomer(id);
> out.println("customer state = " + customer.get("address.state"));
>
> Option 2:  Nested hashmaps.
>
> HashMap customer = customerDao.getCustomer(id);
> HashMap address = customerDao.get("address");
> out.println("customer state = " + address.get("state"));
>
>
> I have no preference as to which way is supported, as long as:
>   - The exception doesn't occur.
>   - I can access the properties using the 'indexed property'
>     style in JSP.
>
> I'm not sure how JSTL/JSP would handle the nested HashMap versus
> handling the single HashMap.
>
> In JSP not sure if I can do this for both implementations
> above:
>
> Customer State = <c:out value="${customer.address.state}"/>
>
> If JSP/JSTL only supports one of these implementation options
> then that would be my preference for ibatis too.
>
> Regards,
>
> Jerome
>
> > -----Original Message-----
> > From: Clinton Begin [mailto:clinton.begin@gmail.com]
> > Sent: Friday, December 10, 2004 10:09 AM
> > To: jerome.jacobsen@gentootech.com
> > Cc: ibatis-user-java@incubator.apache.org
> > Subject: Re: Avoiding N+1 with complex HashMap properties.
> >
> >
> > Tough call.  Some people want it stored as a single property (e.g.
> > imagine the key is a classname), and others want it stored nested.
> >
> > Currently, as you've discovered, it's not nested.
> >
> > I would suggest that if you have a complex object model, then you
> > should model it using JavaBeans.  Maps are very loose, unpredictable
> > and limited.
> >
> > Cheers,
> > Clinton
> >
> >
> > On Fri, 10 Dec 2004 10:03:16 -0500, Jerome Jacobsen
> > <je...@gentootech.com> wrote:
> > > Hello,
> > >
> > > I'm using SQL Maps 2.08.  I was hoping I could do the following
> > > but I get a ProbeException.  Should this be supported?
> > >
> > > <resultMap id="get-customer-period-result"
> > >            class="java.util.HashMap">
> > >   <result property="customerNum"
> > >           column="CUSTOMER_NUMBER"
> > >           columnIndex="1"/>
> > >   <result property="category.id"
> > >           column="CATEGORY_ID"
> > >           columnIndex="2"/>
> > >   <result property="category.parentId"
> > >           column="PARENT_CATEGORY_ID"
> > >           columnIndex="3"/>
> > >   <result property="category.description"
> > >           column="CATEGORY_DESC"
> > >           columnIndex="4"/>
> > >   ...
> > > </resultMap>
> > >
> > > <statement id="getCustomerPeriodSummary"
> > >            resultMap="get-customer-period-result">
> > >   SELECT CPS.CUSTOMER_NUMBER,
> > >          PC.CATEGORY_ID,
> > >          PC.PARENT_CATEGORY_ID,
> > >          PC.DESCRIPTION AS CATEGORY_DESC,
> > >          PC.SEQUENCE AS CATEGORY_SEQUENCE,
> > >          ...
> > >     FROM CUSTOMER_PERIOD_SUMMARY CPS, PRODUCT_CATEGORY PC,
> > SALES_PERIOD SP
> > >    WHERE CPS.CUSTOMER_NUMBER = #value#
> > >      AND CPS.CATEGORY_ID = PC.CATEGORY_ID
> > >      AND CPS.PERIOD_ID = SP.PERIOD_ID
> > > ORDER BY CATEGORY_SEQUENCE
> > > </statement>
> > >
> > > I was hoping that sqlmaps would store the 'category.id',
> > > 'category.parentId', 'category.description' either in a nested
> > > HashMap (key of 'category') or store the values in the top
> > > level HashMap with those keys ('category.id', etc.)
> > >
> > > However neither case appears to happen and I get the
> > > exception below.  I realize that I can map the complex
> > > properties with separate selects but I want to avoid the
> > > N+1 query problem.  So I do the join instead.
> > >
> > > com.ibatis.common.beans.ProbeException: There is no WRITEABLE
> > property named
> > > 'id' in class 'java.lang.Object'
> > >         at
> > com.ibatis.common.beans.ClassInfo.getSetter(ClassInfo.java:146)
> > >         at
> > >
> > com.ibatis.common.beans.ComplexBeanProbe.setProperty(ComplexBeanPr
> > obe.java:3
> > > 34)
> > >         at
> > >
> > com.ibatis.common.beans.ComplexBeanProbe.setObject(ComplexBeanProb
> > e.java:231
> > > )
> > >         at
> > com.ibatis.common.beans.GenericProbe.setObject(GenericProbe.java:69)
> > >         at
> > >
> > com.ibatis.sqlmap.engine.exchange.ComplexDataExchange.setData(Comp
> > lexDataExc
> > > hange.java:87)
> > >         at
> > >
> > com.ibatis.sqlmap.engine.mapping.result.BasicResultMap.setResultOb
> > jectValues
> > > (BasicResultMap.java:231)
> > >         at
> > >
> > com.ibatis.sqlmap.engine.mapping.statement.RowHandlerCallback.hand
> > leResultOb
> > > ject(RowHandlerCallback.java:63)
> > >         at
> > >
> > com.ibatis.sqlmap.engine.execution.SqlExecutor.handleResults(SqlEx
> > ecutor.jav
> > > a:350)
> > >         at
> > >
> > com.ibatis.sqlmap.engine.execution.SqlExecutor.executeQuery(SqlExe
> > cutor.java
> > > :179)
> > >         at
> > >
> > com.ibatis.sqlmap.engine.mapping.statement.GeneralStatement.sqlExe
> > cuteQuery(
> > > GeneralStatement.java:200)
> > >         at
> > >
> > com.ibatis.sqlmap.engine.mapping.statement.GeneralStatement.execut
> > eQueryWith
> > > Callback(GeneralStatement.java:168)
> > >         at
> > >
> > com.ibatis.sqlmap.engine.mapping.statement.GeneralStatement.execut
> > eQueryForL
> > > ist(GeneralStatement.java:118)
> > >         at
> > >
> > com.ibatis.sqlmap.engine.impl.SqlMapExecutorDelegate.queryForList(
> > SqlMapExec
> > > utorDelegate.java:626)
> > >         at
> > >
> > com.ibatis.sqlmap.engine.impl.SqlMapExecutorDelegate.queryForList(
> > SqlMapExec
> > > utorDelegate.java:598)
> > >         at
> > >
> > com.ibatis.sqlmap.engine.impl.SqlMapSessionImpl.queryForList(SqlMa
> > pSessionIm
> > > pl.java:107)
> > >         at
> > >
> > org.springframework.orm.ibatis.SqlMapClientTemplate$3.doInSqlMapCl
> > ient(SqlMa
> > > pClientTemplate.java:202)
> > >         at
> > >
> > org.springframework.orm.ibatis.SqlMapClientTemplate.execute(SqlMap
> > ClientTemp
> > > late.java:142)
> > >         at
> > >
> > org.springframework.orm.ibatis.SqlMapClientTemplate.executeWithLis
> > tResult(Sq
> > > lMapClientTemplate.java:164)
> > >         at
> > >
> > org.springframework.orm.ibatis.SqlMapClientTemplate.queryForList(S
> > qlMapClien
> > > tTemplate.java:200)
> > >         at
> > >
> > com.giv.dashboard.dao.db.ibatis.SqlMapSalesStatisticsDAO.getCatego
> > ryStatsFor
> > > Customer(SqlMapSalesStatisticsDAO.java:28)
> > >         at
> > >
> > com.giv.dashboard.DashboardImpl.getCategorySummaries(DashboardImpl
> > .java:42)
> > >         at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
> > >         at
> > >
> > sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorIm
> > pl.java:39
> > > )
> > >         at
> > >
> > sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAc
> > cessorImpl
> > > .java:25)
> > >         at java.lang.reflect.Method.invoke(Method.java:324)
> > >         at
> > >
> > org.springframework.aop.support.AopUtils.invokeJoinpointUsingRefle
> > ction(AopU
> > > tils.java:295)
> > >         at
> > >
> > org.springframework.aop.framework.ReflectiveMethodInvocation.invok
> > eJoinpoint
> > > (ReflectiveMethodInvocation.java:154)
> > >         at
> > >
> > org.springframework.aop.framework.ReflectiveMethodInvocation.proce
> > ed(Reflect
> > > iveMethodInvocation.java:121)
> > >         at
> > >
> > org.springframework.transaction.interceptor.TransactionInterceptor
> > .invoke(Tr
> > > ansactionInterceptor.java:56)
> > >         at
> > >
> > org.springframework.aop.framework.ReflectiveMethodInvocation.proce
> > ed(Reflect
> > > iveMethodInvocation.java:143)
> > >         at
> > >
> > org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDyn
> > amicAopPro
> > > xy.java:174)
> > >         at $Proxy0.getCategorySummaries(Unknown Source)
> > >         at
> > >
> > com.giv.dashboard.web.spring.ViewCategoriesController.handleReques
> > tInternal(
> > > ViewCategoriesController.java:62)
> > >         at
> > >
> > org.springframework.web.servlet.mvc.AbstractController.handleReque
> > st(Abstrac
> > > tController.java:128)
> > >         at
> > >
> > org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter
> > .handle(Si
> > > mpleControllerHandlerAdapter.java:44)
> > >         at
> > >
> > org.springframework.web.servlet.DispatcherServlet.doService(Dispat
> > cherServle
> > > t.java:532)
> > >         at
> > >
> > org.springframework.web.servlet.FrameworkServlet.serviceWrapper(Fr
> > ameworkSer
> > > vlet.java:366)
> > >         at
> > >
> > org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkSe
> > rvlet.java
> > > :317)
> > >         at
> javax.servlet.http.HttpServlet.service(HttpServlet.java:740)
> > >         at
> javax.servlet.http.HttpServlet.service(HttpServlet.java:853)
> > >         at
> > >
> > com.evermind.server.http.ResourceFilterChain.doFilter(ResourceFilt
> > erChain.ja
> > > va:65)
> > >         at
> oracle.security.jazn.oc4j.JAZNFilter.doFilter(Unknown Source)
> > >         at
> > >
> > com.evermind.server.http.ServletRequestDispatcher.invoke(ServletRe
> > questDispa
> > > tcher.java:604)
> > >         at
> > >
> > com.evermind.server.http.ServletRequestDispatcher.forwardInternal(
> > ServletReq
> > > uestDispatcher.java:317)
> > >         at
> > >
> > com.evermind.server.http.HttpRequestHandler.processRequest(HttpReq
> > uestHandle
> > > r.java:790)
> > >         at
> > >
> > com.evermind.server.http.HttpRequestHandler.run(HttpRequestHandler
> > .java:270)
> > >         at
> > >
> > com.evermind.server.http.HttpRequestHandler.run(HttpRequestHandler
> > .java:112)
> > >         at
> > >
> > com.evermind.util.ReleasableResourcePooledExecutor$MyWorker.run(Re
> > leasableRe
> > > sourcePooledExecutor.java:192)
> > >         at java.lang.Thread.run(Thread.java:534)
> > >
> > >
> >


RE: Avoiding N+1 with complex HashMap properties.

Posted by Jerome Jacobsen <je...@gentootech.com>.
Just to be clear, here are examples of what I'm talking about.

Option 1:  Single hashmap with indexed property style keys.

HashMap customer = customerDao.getCustomer(id);
out.println("customer state = " + customer.get("address.state"));

Option 2:  Nested hashmaps.

HashMap customer = customerDao.getCustomer(id);
HashMap address = customerDao.get("address");
out.println("customer state = " + address.get("state"));


I have no preference as to which way is supported, as long as:
  - The exception doesn't occur.
  - I can access the properties using the 'indexed property'
    style in JSP.

I'm not sure how JSTL/JSP would handle the nested HashMap versus
handling the single HashMap.

In JSP not sure if I can do this for both implementations
above:

Customer State = <c:out value="${customer.address.state}"/>

If JSP/JSTL only supports one of these implementation options
then that would be my preference for ibatis too.

Regards,

Jerome

> -----Original Message-----
> From: Clinton Begin [mailto:clinton.begin@gmail.com]
> Sent: Friday, December 10, 2004 10:09 AM
> To: jerome.jacobsen@gentootech.com
> Cc: ibatis-user-java@incubator.apache.org
> Subject: Re: Avoiding N+1 with complex HashMap properties.
>
>
> Tough call.  Some people want it stored as a single property (e.g.
> imagine the key is a classname), and others want it stored nested.
>
> Currently, as you've discovered, it's not nested.
>
> I would suggest that if you have a complex object model, then you
> should model it using JavaBeans.  Maps are very loose, unpredictable
> and limited.
>
> Cheers,
> Clinton
>
>
> On Fri, 10 Dec 2004 10:03:16 -0500, Jerome Jacobsen
> <je...@gentootech.com> wrote:
> > Hello,
> >
> > I'm using SQL Maps 2.08.  I was hoping I could do the following
> > but I get a ProbeException.  Should this be supported?
> >
> > <resultMap id="get-customer-period-result"
> >            class="java.util.HashMap">
> >   <result property="customerNum"
> >           column="CUSTOMER_NUMBER"
> >           columnIndex="1"/>
> >   <result property="category.id"
> >           column="CATEGORY_ID"
> >           columnIndex="2"/>
> >   <result property="category.parentId"
> >           column="PARENT_CATEGORY_ID"
> >           columnIndex="3"/>
> >   <result property="category.description"
> >           column="CATEGORY_DESC"
> >           columnIndex="4"/>
> >   ...
> > </resultMap>
> >
> > <statement id="getCustomerPeriodSummary"
> >            resultMap="get-customer-period-result">
> >   SELECT CPS.CUSTOMER_NUMBER,
> >          PC.CATEGORY_ID,
> >          PC.PARENT_CATEGORY_ID,
> >          PC.DESCRIPTION AS CATEGORY_DESC,
> >          PC.SEQUENCE AS CATEGORY_SEQUENCE,
> >          ...
> >     FROM CUSTOMER_PERIOD_SUMMARY CPS, PRODUCT_CATEGORY PC,
> SALES_PERIOD SP
> >    WHERE CPS.CUSTOMER_NUMBER = #value#
> >      AND CPS.CATEGORY_ID = PC.CATEGORY_ID
> >      AND CPS.PERIOD_ID = SP.PERIOD_ID
> > ORDER BY CATEGORY_SEQUENCE
> > </statement>
> >
> > I was hoping that sqlmaps would store the 'category.id',
> > 'category.parentId', 'category.description' either in a nested
> > HashMap (key of 'category') or store the values in the top
> > level HashMap with those keys ('category.id', etc.)
> >
> > However neither case appears to happen and I get the
> > exception below.  I realize that I can map the complex
> > properties with separate selects but I want to avoid the
> > N+1 query problem.  So I do the join instead.
> >
> > com.ibatis.common.beans.ProbeException: There is no WRITEABLE
> property named
> > 'id' in class 'java.lang.Object'
> >         at
> com.ibatis.common.beans.ClassInfo.getSetter(ClassInfo.java:146)
> >         at
> >
> com.ibatis.common.beans.ComplexBeanProbe.setProperty(ComplexBeanPr
> obe.java:3
> > 34)
> >         at
> >
> com.ibatis.common.beans.ComplexBeanProbe.setObject(ComplexBeanProb
> e.java:231
> > )
> >         at
> com.ibatis.common.beans.GenericProbe.setObject(GenericProbe.java:69)
> >         at
> >
> com.ibatis.sqlmap.engine.exchange.ComplexDataExchange.setData(Comp
> lexDataExc
> > hange.java:87)
> >         at
> >
> com.ibatis.sqlmap.engine.mapping.result.BasicResultMap.setResultOb
> jectValues
> > (BasicResultMap.java:231)
> >         at
> >
> com.ibatis.sqlmap.engine.mapping.statement.RowHandlerCallback.hand
> leResultOb
> > ject(RowHandlerCallback.java:63)
> >         at
> >
> com.ibatis.sqlmap.engine.execution.SqlExecutor.handleResults(SqlEx
> ecutor.jav
> > a:350)
> >         at
> >
> com.ibatis.sqlmap.engine.execution.SqlExecutor.executeQuery(SqlExe
> cutor.java
> > :179)
> >         at
> >
> com.ibatis.sqlmap.engine.mapping.statement.GeneralStatement.sqlExe
> cuteQuery(
> > GeneralStatement.java:200)
> >         at
> >
> com.ibatis.sqlmap.engine.mapping.statement.GeneralStatement.execut
> eQueryWith
> > Callback(GeneralStatement.java:168)
> >         at
> >
> com.ibatis.sqlmap.engine.mapping.statement.GeneralStatement.execut
> eQueryForL
> > ist(GeneralStatement.java:118)
> >         at
> >
> com.ibatis.sqlmap.engine.impl.SqlMapExecutorDelegate.queryForList(
> SqlMapExec
> > utorDelegate.java:626)
> >         at
> >
> com.ibatis.sqlmap.engine.impl.SqlMapExecutorDelegate.queryForList(
> SqlMapExec
> > utorDelegate.java:598)
> >         at
> >
> com.ibatis.sqlmap.engine.impl.SqlMapSessionImpl.queryForList(SqlMa
> pSessionIm
> > pl.java:107)
> >         at
> >
> org.springframework.orm.ibatis.SqlMapClientTemplate$3.doInSqlMapCl
> ient(SqlMa
> > pClientTemplate.java:202)
> >         at
> >
> org.springframework.orm.ibatis.SqlMapClientTemplate.execute(SqlMap
> ClientTemp
> > late.java:142)
> >         at
> >
> org.springframework.orm.ibatis.SqlMapClientTemplate.executeWithLis
> tResult(Sq
> > lMapClientTemplate.java:164)
> >         at
> >
> org.springframework.orm.ibatis.SqlMapClientTemplate.queryForList(S
> qlMapClien
> > tTemplate.java:200)
> >         at
> >
> com.giv.dashboard.dao.db.ibatis.SqlMapSalesStatisticsDAO.getCatego
> ryStatsFor
> > Customer(SqlMapSalesStatisticsDAO.java:28)
> >         at
> >
> com.giv.dashboard.DashboardImpl.getCategorySummaries(DashboardImpl
> .java:42)
> >         at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
> >         at
> >
> sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorIm
> pl.java:39
> > )
> >         at
> >
> sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAc
> cessorImpl
> > .java:25)
> >         at java.lang.reflect.Method.invoke(Method.java:324)
> >         at
> >
> org.springframework.aop.support.AopUtils.invokeJoinpointUsingRefle
> ction(AopU
> > tils.java:295)
> >         at
> >
> org.springframework.aop.framework.ReflectiveMethodInvocation.invok
> eJoinpoint
> > (ReflectiveMethodInvocation.java:154)
> >         at
> >
> org.springframework.aop.framework.ReflectiveMethodInvocation.proce
> ed(Reflect
> > iveMethodInvocation.java:121)
> >         at
> >
> org.springframework.transaction.interceptor.TransactionInterceptor
> .invoke(Tr
> > ansactionInterceptor.java:56)
> >         at
> >
> org.springframework.aop.framework.ReflectiveMethodInvocation.proce
> ed(Reflect
> > iveMethodInvocation.java:143)
> >         at
> >
> org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDyn
> amicAopPro
> > xy.java:174)
> >         at $Proxy0.getCategorySummaries(Unknown Source)
> >         at
> >
> com.giv.dashboard.web.spring.ViewCategoriesController.handleReques
> tInternal(
> > ViewCategoriesController.java:62)
> >         at
> >
> org.springframework.web.servlet.mvc.AbstractController.handleReque
> st(Abstrac
> > tController.java:128)
> >         at
> >
> org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter
> .handle(Si
> > mpleControllerHandlerAdapter.java:44)
> >         at
> >
> org.springframework.web.servlet.DispatcherServlet.doService(Dispat
> cherServle
> > t.java:532)
> >         at
> >
> org.springframework.web.servlet.FrameworkServlet.serviceWrapper(Fr
> ameworkSer
> > vlet.java:366)
> >         at
> >
> org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkSe
> rvlet.java
> > :317)
> >         at javax.servlet.http.HttpServlet.service(HttpServlet.java:740)
> >         at javax.servlet.http.HttpServlet.service(HttpServlet.java:853)
> >         at
> >
> com.evermind.server.http.ResourceFilterChain.doFilter(ResourceFilt
> erChain.ja
> > va:65)
> >         at oracle.security.jazn.oc4j.JAZNFilter.doFilter(Unknown Source)
> >         at
> >
> com.evermind.server.http.ServletRequestDispatcher.invoke(ServletRe
> questDispa
> > tcher.java:604)
> >         at
> >
> com.evermind.server.http.ServletRequestDispatcher.forwardInternal(
> ServletReq
> > uestDispatcher.java:317)
> >         at
> >
> com.evermind.server.http.HttpRequestHandler.processRequest(HttpReq
> uestHandle
> > r.java:790)
> >         at
> >
> com.evermind.server.http.HttpRequestHandler.run(HttpRequestHandler
> .java:270)
> >         at
> >
> com.evermind.server.http.HttpRequestHandler.run(HttpRequestHandler
> .java:112)
> >         at
> >
> com.evermind.util.ReleasableResourcePooledExecutor$MyWorker.run(Re
> leasableRe
> > sourcePooledExecutor.java:192)
> >         at java.lang.Thread.run(Thread.java:534)
> >
> >
>


Re: Avoiding N+1 with complex HashMap properties.

Posted by Clinton Begin <cl...@gmail.com>.
Tough call.  Some people want it stored as a single property (e.g.
imagine the key is a classname), and others want it stored nested.

Currently, as you've discovered, it's not nested.

I would suggest that if you have a complex object model, then you
should model it using JavaBeans.  Maps are very loose, unpredictable
and limited.

Cheers,
Clinton


On Fri, 10 Dec 2004 10:03:16 -0500, Jerome Jacobsen
<je...@gentootech.com> wrote:
> Hello,
> 
> I'm using SQL Maps 2.08.  I was hoping I could do the following
> but I get a ProbeException.  Should this be supported?
> 
> <resultMap id="get-customer-period-result"
>            class="java.util.HashMap">
>   <result property="customerNum"
>           column="CUSTOMER_NUMBER"
>           columnIndex="1"/>
>   <result property="category.id"
>           column="CATEGORY_ID"
>           columnIndex="2"/>
>   <result property="category.parentId"
>           column="PARENT_CATEGORY_ID"
>           columnIndex="3"/>
>   <result property="category.description"
>           column="CATEGORY_DESC"
>           columnIndex="4"/>
>   ...
> </resultMap>
> 
> <statement id="getCustomerPeriodSummary"
>            resultMap="get-customer-period-result">
>   SELECT CPS.CUSTOMER_NUMBER,
>          PC.CATEGORY_ID,
>          PC.PARENT_CATEGORY_ID,
>          PC.DESCRIPTION AS CATEGORY_DESC,
>          PC.SEQUENCE AS CATEGORY_SEQUENCE,
>          ...
>     FROM CUSTOMER_PERIOD_SUMMARY CPS, PRODUCT_CATEGORY PC, SALES_PERIOD SP
>    WHERE CPS.CUSTOMER_NUMBER = #value#
>      AND CPS.CATEGORY_ID = PC.CATEGORY_ID
>      AND CPS.PERIOD_ID = SP.PERIOD_ID
> ORDER BY CATEGORY_SEQUENCE
> </statement>
> 
> I was hoping that sqlmaps would store the 'category.id',
> 'category.parentId', 'category.description' either in a nested
> HashMap (key of 'category') or store the values in the top
> level HashMap with those keys ('category.id', etc.)
> 
> However neither case appears to happen and I get the
> exception below.  I realize that I can map the complex
> properties with separate selects but I want to avoid the
> N+1 query problem.  So I do the join instead.
> 
> com.ibatis.common.beans.ProbeException: There is no WRITEABLE property named
> 'id' in class 'java.lang.Object'
>         at com.ibatis.common.beans.ClassInfo.getSetter(ClassInfo.java:146)
>         at
> com.ibatis.common.beans.ComplexBeanProbe.setProperty(ComplexBeanProbe.java:3
> 34)
>         at
> com.ibatis.common.beans.ComplexBeanProbe.setObject(ComplexBeanProbe.java:231
> )
>         at com.ibatis.common.beans.GenericProbe.setObject(GenericProbe.java:69)
>         at
> com.ibatis.sqlmap.engine.exchange.ComplexDataExchange.setData(ComplexDataExc
> hange.java:87)
>         at
> com.ibatis.sqlmap.engine.mapping.result.BasicResultMap.setResultObjectValues
> (BasicResultMap.java:231)
>         at
> com.ibatis.sqlmap.engine.mapping.statement.RowHandlerCallback.handleResultOb
> ject(RowHandlerCallback.java:63)
>         at
> com.ibatis.sqlmap.engine.execution.SqlExecutor.handleResults(SqlExecutor.jav
> a:350)
>         at
> com.ibatis.sqlmap.engine.execution.SqlExecutor.executeQuery(SqlExecutor.java
> :179)
>         at
> com.ibatis.sqlmap.engine.mapping.statement.GeneralStatement.sqlExecuteQuery(
> GeneralStatement.java:200)
>         at
> com.ibatis.sqlmap.engine.mapping.statement.GeneralStatement.executeQueryWith
> Callback(GeneralStatement.java:168)
>         at
> com.ibatis.sqlmap.engine.mapping.statement.GeneralStatement.executeQueryForL
> ist(GeneralStatement.java:118)
>         at
> com.ibatis.sqlmap.engine.impl.SqlMapExecutorDelegate.queryForList(SqlMapExec
> utorDelegate.java:626)
>         at
> com.ibatis.sqlmap.engine.impl.SqlMapExecutorDelegate.queryForList(SqlMapExec
> utorDelegate.java:598)
>         at
> com.ibatis.sqlmap.engine.impl.SqlMapSessionImpl.queryForList(SqlMapSessionIm
> pl.java:107)
>         at
> org.springframework.orm.ibatis.SqlMapClientTemplate$3.doInSqlMapClient(SqlMa
> pClientTemplate.java:202)
>         at
> org.springframework.orm.ibatis.SqlMapClientTemplate.execute(SqlMapClientTemp
> late.java:142)
>         at
> org.springframework.orm.ibatis.SqlMapClientTemplate.executeWithListResult(Sq
> lMapClientTemplate.java:164)
>         at
> org.springframework.orm.ibatis.SqlMapClientTemplate.queryForList(SqlMapClien
> tTemplate.java:200)
>         at
> com.giv.dashboard.dao.db.ibatis.SqlMapSalesStatisticsDAO.getCategoryStatsFor
> Customer(SqlMapSalesStatisticsDAO.java:28)
>         at
> com.giv.dashboard.DashboardImpl.getCategorySummaries(DashboardImpl.java:42)
>         at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
>         at
> sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39
> )
>         at
> sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl
> .java:25)
>         at java.lang.reflect.Method.invoke(Method.java:324)
>         at
> org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopU
> tils.java:295)
>         at
> org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint
> (ReflectiveMethodInvocation.java:154)
>         at
> org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(Reflect
> iveMethodInvocation.java:121)
>         at
> org.springframework.transaction.interceptor.TransactionInterceptor.invoke(Tr
> ansactionInterceptor.java:56)
>         at
> org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(Reflect
> iveMethodInvocation.java:143)
>         at
> org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopPro
> xy.java:174)
>         at $Proxy0.getCategorySummaries(Unknown Source)
>         at
> com.giv.dashboard.web.spring.ViewCategoriesController.handleRequestInternal(
> ViewCategoriesController.java:62)
>         at
> org.springframework.web.servlet.mvc.AbstractController.handleRequest(Abstrac
> tController.java:128)
>         at
> org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter.handle(Si
> mpleControllerHandlerAdapter.java:44)
>         at
> org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServle
> t.java:532)
>         at
> org.springframework.web.servlet.FrameworkServlet.serviceWrapper(FrameworkSer
> vlet.java:366)
>         at
> org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java
> :317)
>         at javax.servlet.http.HttpServlet.service(HttpServlet.java:740)
>         at javax.servlet.http.HttpServlet.service(HttpServlet.java:853)
>         at
> com.evermind.server.http.ResourceFilterChain.doFilter(ResourceFilterChain.ja
> va:65)
>         at oracle.security.jazn.oc4j.JAZNFilter.doFilter(Unknown Source)
>         at
> com.evermind.server.http.ServletRequestDispatcher.invoke(ServletRequestDispa
> tcher.java:604)
>         at
> com.evermind.server.http.ServletRequestDispatcher.forwardInternal(ServletReq
> uestDispatcher.java:317)
>         at
> com.evermind.server.http.HttpRequestHandler.processRequest(HttpRequestHandle
> r.java:790)
>         at
> com.evermind.server.http.HttpRequestHandler.run(HttpRequestHandler.java:270)
>         at
> com.evermind.server.http.HttpRequestHandler.run(HttpRequestHandler.java:112)
>         at
> com.evermind.util.ReleasableResourcePooledExecutor$MyWorker.run(ReleasableRe
> sourcePooledExecutor.java:192)
>         at java.lang.Thread.run(Thread.java:534)
> 
>