帳モメのotanuft

Stay hungry, stay foolish.

N+1 Selects問題

iBATISに限らず、O/Rマッピングでの問題点。

ネストした、子レコードの自動取得時に、親レコードが複数検索される場合、

ヒットした親レコードの数だけ、子レコードに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して取得する。

iBATISは、SQLを直に書いて実行するので、

他のマッピングツールよりはやり方が楽のような気がする。

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>