石丸です。
MyBatisで連番のカラム名を持つテーブルに対してのUPDATE文を書く際に知ったテクニックを紹介します。
- 連番のカラム名を動的に生成
- if で N より小さいと記述(== か != しかかけないと思っていた)
- パラメータとして渡されたオブジェクトのメソッドを呼ぶ
MyBatisのバージョンは3.4(SpringBootプロジェクトでmybatis-spring-boot-starter:1.3.1)になります。
対象のテーブル
話を簡単にするため極限までカラム数を減らしていますが、正規化されていないし、COLUMN_Bは1、2しか存在しないし、COLUMN_AとCOLUMN_Bでは連番のフォーマットが違うという3重苦です。
ID | COLUMN_A01 | COLUMN_B1 | COLUMN_A02 | COLUMN_B2 | COLUMN_A03 |
---|---|---|---|---|---|
1 | 1-A01 | 1-B1 | 1-A02 | 1-B2 | 1-A03 |
2 | 2-A01 | 2-B1 | 2-A02 | 2-B2 | 2-A03 |
3 | 3-A01 | 3-B1 | 3-A02 | 3-B2 | 3-A03 |
mapper
mapper.javaはこんな感じ
1
|
int updateTableX(@Param("ID") int id, @Param("rows") List<Row> rows, @Param("formatter") Formatter formatter);
|
テーブルは正規化されていませんが、それでは扱いにくいのでプログラム上ではCOLUMN_AとCOLUMN_Bをフィールドに持つRowクラスのArrayListにしています。
mapper.xmlは次のようになりました。
1
2
3
4
5
6
7
8
9
10
11
12
|
<update id="updateTableX">
UPDATE TABLE_X SET
<foreach item="item" index="index" collection="rows">
<bind name="suffix" value="formatter.formatIndex(index)" />
,COLUMN_A${suffix} = #{item.COLUMN_A}
<if test="index lt 2">
,COLUMN_B${index+1} = #{item.COLUMN_B}
</if>
</foreach>
WHERE
ID = #{ID}
</update>
|
まず連番のカラム名生成ですが、collectionを回すforeachのindexを利用します。
COLUMN_Bは0から始まるindex+1を末尾にくっつけるだけなので7行目のようになります。
COLUMN_B${index+1}
が COLUMN_B1
になります。
COLUMN_Aの0埋めされた2桁の連番の生成にはformatterを用意しました。
4行目のbind要素でformatter.formatIndex(index)を実行し、suffix変数に格納します。
bind 要素を使うと、OGNL 式の結果を変数に格納し、その変数を SQL 文中で使用することができます。
http://www.mybatis.org/mybatis-3/ja/dynamic-sql.html#bind
これで COLUMN_A${suffix}
が COLUMN_A01
になります。
最後にif要素ですが index < 2
と書くとエラーになります。
これは index lt 2
と書くことでうまくいきます。
Mybatis Dynamic SQL StatementにOGNL式で使える演算子の一覧がありました。