先日の例、
PostgreSQL の再帰SQL で要素の連結結果を求める。 - Oboe吹きプログラマの黙示録
テーブル名:division
id | parent_id | name |
1 | null | A |
2 | 1 | B |
3 | 2 | C |
4 | 2 | D |
ここから、連結文字列の結果、id と name
"1" "1,2" "1,2,3" "1,2,4"
"A" "A,B" "A,B,C" "A,B,D"
と、配列としての結果も求めるSQLにします。
[1] [1,2] [1,2,3] [1,2,4]
["A"] ["A", "B"] ["A", "B", "C"] ["A", "B", "D"]
再帰SQL
WITH RECURSIVE r (depth, id, parent_id, chain_id, chain_name, aryid, aryname) AS ( SELECT 0, id, parent_id, ''||id AS chain_id, name AS chain_name, ARRAY[id] AS aryid, ARRAY[name] AS aryname FROM division UNION ALL SELECT r.depth + 1, d.id, d.parent_id, concat(d.id , ',', ''||r.chain_id), concat(d.name, ',', r.chain_name), array_cat(ARRAY[d.id] , r.aryid), array_cat(ARRAY[d.name] , r.aryname) FROM r, division d WHERE r.parent_id = d.id ) SELECT r.chain_id, r.chain_name, r.aryid, r.aryname FROM r WHERE parent_id is null
↑ PostreSQL 独特で嫌なところは、数値型→文字列型変換している ''||r.chain_id この書き方です。
このクエリ結果を mybatis の SELECT で求めるオブジェクトのクラス
public class DivisionRecusive implements Serializable{ public String chain_id; public String chain_name; public Integer[] aryid; public String[] aryname; public DivisionRecusive(){} }
aryid を int にしたいところですが、ArrayTypeHandler を書かなくてはならないので、
このまま Integer で済ませます。必ず null 以外なら構わないでしょう。
mybatis の <typeHandlers> でカスタムで用意する ArrayTypeHandler を定義しなくても、
SQLMapの方で、以下のように、resultMap による JdbcType 定義を解決させれば、
配列として ARRAY を取得できます。
typeHandler="org.apache.ibatis.type.ArrayTypeHandler" を ARRAY として抽出するカラムに定義します。
<resultMap id="mapDivisionRecusive" type="test.data.DivisionRecusive"> <result column="aryid" property="aryid" typeHandler="org.apache.ibatis.type.ArrayTypeHandler" jdbcType="INTEGER" javaType="java.lang.Integer" /> <result column="aryname" property="aryname" typeHandler="org.apache.ibatis.type.ArrayTypeHandler" jdbcType="VARCHAR" javaType="java.lang.String" /> </resultMap> <select id="getDivisionRecusiveList" resultMap="mapDivisionRecusive"> WITH RECURSIVE r (depth, id, parent_id, chain_id, chain_name, aryid, aryname) AS ( SELECT 0, id, parent_id, ''||id AS chain_id, name AS chain_name, ARRAY[id] AS aryid, ARRAY[name] AS aryname FROM division UNION ALL SELECT r.depth + 1, d.id, d.parent_id, concat(d.id , ',', ''||r.chain_id), concat(d.name, ',', r.chain_name), array_cat(ARRAY[d.id] , r.aryid), array_cat(ARRAY[d.name] , r.aryname) FROM r, division d WHERE r.parent_id = d.id ) SELECT r.chain_id, r.chain_name, r.aryid, r.aryname FROM r WHERE parent_id is null </select>
resultMap を書く時、昔は SELECT 句で取得する列全てに対して、
<result column="" > を記述していたのですが、
property と columnが同じであれば、このように、typeHandler が必要なカラムに対してだけ書いても良いようです。