N+1 Selects問題
ネストした、子レコードの自動取得時に、親レコードが複数検索される場合、
ヒットした親レコードの数だけ、子レコードにSELECT文が発行されることが有る。
親レコードが、キー検索のような場合は問題が少ないが、
親レコードが数万件ヒットするような場合、子レコードへのクエリーが数万件発行される。
iBATISでも、同じ問題を抱えている。
たとえば、
<!--N+1 Selects問題を含んだResultMap
リストを検索した際に、件数の数だけ、ギターテーブルにクエリーを投げる
-->
<resultMap id="playerResult" class="player">
<result property="id" column="ID" />
<result property="name" column="NAME" />
<result property="guitarId" column="GUITAR_ID" />
<result property="guitar" column="GUITAR_ID"
select="Guitar.findByPk"/>
</resultMap>
という場合、上記のSqlMapの定義だと、
Playerを取得する際に自動で子レコードのGuitarを取得する。
それを、解決する方法のひとつに、子レコードをJoinして取得する。
他のマッピングツールよりはやり方が楽のような気がする。
Hibernateなんかは、LazyLoadingなんかを使って、
目的の子レコードが必要になったときに、
SQLを発行するような仕組みが入ってるみたい。
改良したもの、
<!--N+1 Selects問題を回避したResultMap
-->
<resultMap id="playerResultWithGuitar" class="player">
<result property="id" column="ID" />
<result property="name" column="NAME" />
<result property="guitarId" column="GUITAR_ID" />
<result property="guitar.id" column="G_ID"/>
<result property="guitar.name" column="G_NAME"/>
<result property="guitar.makerName" column="G_MAKER_NAME"/>
</resultMap>
実際のSelect文。
<select id="findByPk" resultMap="playerResult" parameterClass="string">select ID, NAME,GUITAR_ID from PLAYER where ID = #value#
</select>
<select id="getPlayerList" resultMap="playerResultWithGuitar">
select P.ID, P.NAME, P.GUITAR_ID, G.ID as G_ID, G.NAME as G_NAME,
G.MAKER_ID as G_MAKER_NAME from PLAYER P, GUITAR G WHERE P.GUITAR_ID=G.ID
</select>
<select id="test" parameterClass="hashmap" resultClass="hashmap">
select ID, NAME,GUITAR_ID from PLAYER
where ID = #value0# and id = #value1# order by id
</select>
iBATISでもLazyLoadingさせることができるが、
確認が難しい・・・
<sqlMapConfig><settings
cacheModelsEnabled="true"
enhancementEnabled="true"
lazyLoadingEnabled="true"
maxRequests="32"
maxSessions="10"
maxTransactions="5"
useStatementNamespaces="true"
/>
<!--properties resource="properties/database.properties"/-->
<sqlMap resource="ibatis/dao/maps/Player.xml" />
<sqlMap resource="ibatis/dao/maps/Guitar.xml" />
</sqlMapConfig>