パラメータコメント

外だしSQLの2Way-SQLを実現させるパラメータコメントについてのページです。

パラメータコメントとは?

外だしSQLの実行時に指定されるパラメータを動的に処理して、バインド変数の構築や分岐などを実現するSQLコメント をパラメータコメントと言います。SQLコメント形式になっているのは、2Way-SQLを実現するためです。

基本仕様

コメントの構文
コメントの構文として基本的にブロックコメント "/**/" を利用する。但し、一部機能(ELSE)で行コメント "--" も利用する。
パラメータコメントの判断
"/*" の後に空白や改行を入れるとパラメータコメントと認識されない。例えば、"/* IF ...*/" と記述すると、パラメータコメントとしては処理されないの注意。 逆に、通常のSQLコメントを記述する場合は、パラメータコメントとして誤動作しないために、 このように空白を一つ空けるか、改行を入れて記述することが推奨される。
大文字小文字
パラメータコメントのコメント名(IFやBEGINなど)やプロパティ名("pmb.memberName" など)は、大文字小文字を区別する。

バインド変数コメント

アプリケーションから渡されるパラメータをバインド変数として扱うパラメータコメントです。主に where 句にて利用します。

"/**/" 形式のコメントの中で、"pmb.[property-name]" という形式で記述します。

e.g. バインド変数コメントの利用 @OutsideSql
  and MEMBER_ID = /*pmb.memberId*/3
  and MEMBER_NAME like /*pmb.memberName*/'S'
  and BIRTHDATE <= /*pmb.birthdate*/'2010-06-06'
  and MEMBER_STATUS_CODE in /*pmb.memberStatusCodeList*/('FML', 'WDL')

テスト値の扱い

コメント直後の値は、テスト値と認識され、アプリケーション実行時には無視されます。 2Way-SQLを実現するためのもので、実際にデータがHITする値である必要はありません。 省略することもできますが、2Way-SQLが成り立たなくなってしまうので無意味です。

数値の場合は数字をそのまま記述し、文字列や日付の場合はシングルクォーテーション "'" で囲います。日付の場合に、日付リテラルを利用することができます(サポートしているDBMSにおいてのみ)。 文字列の日付フォーマットが解析されないDBMSの場合に(2Way-SQLを実現するために)有効です。

e.g. バインド変数コメントのテスト値で日付リテラル @OutsideSql
  and BIRTHDATE = /*pmb.birthdate*/date '2010-06-06'

テスト値が "(1, 2)" という形式の場合は、バインド変数の値は、リスト型(java.util.List)、もしくは、配列型である必要があります。 括弧で括られたテスト値から、"in" (InScope) のためのバインド変数であると判断されます。

null値の扱い

該当の値が null の場合、検索処理であれば設定ミスとして例外、更新系処理であれば null でバインドされます。検索処理では null でのバインドは不要なためです。(null かどうかの絞り込み条件は、SQLの構文である "is null"、"is not null" を利用します)

"in" (InScope) の場合に、リストの要素で null の値は無視されます。また、必ず null 以外の要素が一つ以上存在する必要があります。(空リストで条件を無効にしたい場合は、IFコメントを利用)

ネストしたプロパティの扱い

"/*pmb.member.memberName*/" という形式で、ネストしたプロパティを無限階層に利用することができます。 但し、コメント上の表現が複雑になりやすいため、基本的には推奨されません。ParameterBean のExクラスを利用するなどして、極力コメント上はネストのない表現で記述できるように工夫すると良いでしょう。

java.util.Map型の扱い

パラメータの型が java.util.Map の場合は、"/*pmb.memberMap.key1*/" という形式で、map内のキー値を指定して該当する値を利用することができます。 指定されたキー要素が存在しない場合は、その値は null として扱われます。

java.util.List型の扱い

パラメータの型が java.util.List の場合は、"/*pmb.memberMap.get(2)*/" という形式で、list内のインデックスを指定して該当する値を利用することができます。

LikeSearch条件のオプション

ParameterBeanのプロパティのオプションで、バインド変数の値に対して、LikeSearch条件の処理を自動化することができます。

ParameterBean - LikeSearch条件のオプション

ネストしたプロパティの場合でも、ネストの基点となっているプロパティに対するオプション指定が反映されます。 逆に言うと、ネストしたプロパティの文字列型の全てのバインド変数コメントにオプションが反映されます(likeを利用しているかどうかに関わらず)。 ただ、"ネストしたプロパティの扱い" で説明の通り、ネストしたプロパティの利用は推奨されません。 この挙動は、ネストが必ず必要になるFORコメントにおいて主に活用されます。

e.g. ネストしたプロパティで、LikeSearch条件のオプション利用 @OutsideSql
-- !df:pmb!
-- !!$$Domain$$.Member member:likeContain!!
...
  and MEMBER_NAME = /*pmb.member.memberName*/'%foo%'
  and MEMBER_ACCOUNT = /*pmb.member.memberAccount*/'%bar%'

ネストの基点となるプロパティの型がParameterBeanで、 ネストしたプロパティ自体にLikeSearch条件のオプションが指定されていれば、それが有効になります。@since 0.9.7.1

基本的な適用法則として、バインド変数として利用する値(のプロパティ)に近いオプションが利用されます。 (ループ内の場合は、InLoopオプションが一番近いオプションとして扱われます) @since 0.9.7.1

ループ内で利用できるオプション

FORコメントのループ内でのみ利用できるLikeSearch条件関連のオプションがあります。@since 0.9.7.1

埋め込み変数コメント

アプリケーションから渡されるパラメータを実行されるSQL上に埋め込むパラメータコメントです。 バインド変数が利用できない条件値の設定やSQLの一部分をプログラム上で組み立てるのに利用します。

基本的には、ページングの条件などでバインド変数が利用できない場合 に利用します(DBMSの仕様次第)。それ以外の状況での利用は想定されていません。 画面ユーザ入力のパラメータに対してこのコメントを利用する場合は、SQLインジェクションのリスクが発生しますので厳重に注意 して下さい。

"/**/" 形式のコメントの中で、"$pmb.[property-name]" という形式で記述します。パラメータの中の文字列がそのままSQL上に展開されます。

e.g. 埋め込み変数コメントの利用 {memberId=123} @OutsideSql
  and MEMBER_ID = /*$pmb.memberId*/3
  -- and MEMBER_ID = 123

クォーテーションの調整

テスト値がクォートされている場合は、展開された値がクォートされます。@since 0.9.7.1

e.g. 埋め込み変数コメントでクォートされる場合 {memberName=bar} @OutsideSql
  and MEMBER_NAME = /*$pmb.memberName*/'foo'
  -- and MEMBER_NAME = 'bar'
e.g. 埋め込み変数コメントでクォートされる場合 (InScope) {statusList=bar,baz} @OutsideSql
  and MEMBER_STATUS_CODE in /*$pmb.statusList*/('foo')
  -- and MEMBER_STATUS_CODE in ('bar', 'baz')

テスト値の扱い

基本仕様は、バインド変数コメントと同じです。 ただ、埋め込み変数コメント特有のSQLの一部分を展開させる用途に関しては、テスト値を省略した利用が想定されるため、テスト値の取扱いを調整するオプションが用意されています。

/*$$pmb.foo*/
テスト値がSQL上にそのまま展開される @since 0.9.9.0C
"from /*$$pmb.foo*/SEA.MEMBER mb" なら "from [fooの値]SEA.MEMBER mb" と展開されます。つまり、埋め込み変数コメントだけが単に置換されるだけとなります。 (通常の埋め込み変数コメントであれば、"from [fooの値] mb" となる)
/*$.pmb.foo*/
テスト値はドット "." (の前の文字列)までと認識 @since 0.9.9.0C
"from /*$$pmb.foo*/SEA.MEMBER mb" なら "from [fooの値].MEMBER mb" と展開されます。テーブル名に付与するスキーマ名を切り替えるような場合に有効です。

null値の扱い

基本仕様は、バインド変数コメントと同じです。null を許容する状況で、(InScopeでない場合に)実際に値が null だった場合は、"null" という文字が展開されます。@since 0.9.7.1

埋め込み変数コメント内のバインド

埋め込み変数の値の中に、バインド変数コメントやIFコメントなどを含めることができます(動的に解析されます)。 但し、不定数の条件はFORコメントを利用することで実現できるため、あまり利用が想定されていません。@since 0.9.7.0

埋め込み変数コメント内のNG文字

埋め込み変数の値の中に、JDBCのバインド変数を示す記号 "?" (はてな)を含めることはできません。

その他仕様

その他仕様に関してはバインド変数コメントと同じ仕様です。

IFコメント

アプリケーションから渡されるパラメータを判定して条件分岐するパラメータコメントです。where 句条件の有無判定や、結合の有無判定など様々な用途で利用します。

"/**/" 形式のコメントの中で、"IF [condition-expression]" という形式で記述し、ENDコメントでスコープを定義します。

e.g. IFコメントの利用 @OutsideSql
 where
   /*IF pmb.memberId != null*/
   MEMBER_ID = /*pmb.memberId*/3
   /*END*/
   /*IF pmb.memberName != null*/
   and MEMBER_NAME like /*pmb.memberName*/'S'
   /*END*/
 /*IF pmb.validOrderBy*/
 order by MEMBER_NAME desc
 /*END*/

IFコメントの条件表現

条件表現として、以下のポイントがあります。

  • 文字列リテラルが利用できる e.g. /*IF pmb.memberName > 'Pixy'*/
  • 数値リテラルが利用できる e.g. /*IF pmb.memberId >= 6*/
  • 日付リテラルが利用できる e.g. /*IF pmb.birthdate > date '2000/03/28'*/
  • nullリテラルが利用できる e.g. /*IF pmb.memberName != null*/
  • 等値条件で判定できる e.g. /*IF pmb.memberName == 'Pixy'*/
  • 非等値条件で判定できる e.g. /*IF pmb.memberId != 3*/
  • 大なり小なり条件で判定できる e.g. /*IF pmb.memberId < 3*/
  • boolean型を利用できる e.g. /*IF pmb.memberFlg*/
  • (引数なしの)メソッド呼び出しができる e.g. /*IF pmb.isPaging()*/
  • boolean型で否定判定できる e.g. /*IF !pmb.memberFlg*/
  • AND条件で条件連結できる e.g. /*IF pmb.isPaging() && pmb.memberId != null*/
  • OR条件で条件連結できる e.g. /*IF pmb.isPaging() || pmb.memberId != null*/
  • AND条件とOR条件の(一つのIFコメント内での)併用はできない

複雑な条件はメソッド化

複雑な条件を記述する場合は、ParameterBeanのExクラスにメソッドを定義して、そのメソッドをIFコメントで呼び出すようにするのが推奨されます。 これのメソッドを、(複雑な条件の) 代理判定メソッド と呼びます。

このようにすることで、プログラム言語の文法を利用して柔軟に条件を実装でき、また、条件の妥当性を単体テストなどでチェックしやすくなります。 そういったポリシーから、IFコメントではそもそも複雑な条件を書けないように制限している、という面もあります(AND条件とOR条件の併用禁止など)。

e.g. ParameterBeanのExクラスにて複雑な条件のメソッド @Java
public boolean existsPurchase() {
    return (_purchaseCount != null || _purchasePrice != null) && ...;
}
e.g. ParameterBeanのExクラスのメソッドをIFコメントで利用 @OutsideSql
 /*IF pmb.existsPurchase()*/
 and exists (select PURCHASE_ID
             from PURCHASE
             ...
 /*END*/

IFコメント上に書かれた代理判定メソッドを自動判別して、Exクラスでのメソッドの定義漏れを防ぐ支援機能(AutoDetect)も存在します。

ParameterBean - 代理判定メソッド

IFコメントのネスト

IFコメントは、ネストさせて利用することができます。但し、単なる AND 条件などは、一つのIFコメントで演算子を利用するか、ParameterBeanでメソッド化するのが良いでしょう。 色々できるとは言っても、あくまでタイプセーフでないパラメータコメント上なので、できる限りシンプルにすることを心がけましょう。

BEGINコメントとの併用

where 句における条件有無の判定は、全ての条件が無効だった場合の "where" の除去、"and" や "or" の調整などをするために、基本的にBEGINコメントと併用するのが基本です。

ELSEコメント

IFコメントと併用して、IFコメントの判定が false だったときに有効になるパラメータコメントです。主にページングなどにおいて select 句を差し替えるのに利用します。

IFコメントのスコープ内において、"--" 形式のコメントの中で、"ELSE [SQL-expression]" という形式で記述します。

e.g. ELSEコメントの利用 @OutsideSql
/*IF pmb.isPaging()*/
select member.MEMBER_ID
     , member.MEMBER_NAME
     , ...
-- ELSE select count(*)
/*END*/
  from ...

一行制約 ありません

行コメントを利用しているため、else 部分は一行表現に限られます。

複数行で書くこともできます。(長らくできないと思われてましたが、できました: 2015/11/10)

e.g. ELSEコメントの利用 @OutsideSql
/*IF pmb.isPaging()*/
select member.MEMBER_ID
     , member.MEMBER_NAME
     , ...
-- ELSE
-- select
-- count(*)
/*END*/
  from ...

ただ、コメントの中はアプリから実行するまで動くかどうかわかりませんので、使わないに越したことはありません。

コメント制約

DBFluteの古いバージョンでは、IFコメントとELSEコメントの間には、行コメント("--")を含めることができません。 @until 0.9.9.0D

e.g. ELSEコメントの利用 @OutsideSql
/*IF pmb.isPaging()*/
select member.MEMBER_ID
     , member.MEMBER_NAME -- NG comment 
     , ...
-- ELSE select count(*)
/*END*/
  from ...

BEGINコメント

スコープ内の判定対象のコメントが全て無効の場合に、スコープ内をまるごと削除するパラメータコメントです。 条件がなかった場合に where だけ取り残されるのを防ぐことができます。判定対象のコメントは以下の通りです。

  • IFコメント
  • ELSEコメント
  • FORコメント @since 0.9.7.0

"/*BEGIN*/" という形式で記述し、ENDコメントでスコープを定義します。where 句をまるごと囲って利用するのが最も想定される使い方です。

e.g. BEGINコメントで where 句をまるごと囲う @OutsideSql
select * from MEMBER
 /*BEGIN*/
 where
   /*IF pmb.memberId != null*/
   MEMBER_ID = /*pmb.memberId*/3
   /*END*/
   /*IF pmb.memberName != null*/
   and MEMBER_NAME like /*pmb.memberName*/'S%'
   /*END*/
   /*FOR pmb.memberAccountList*/
   and MEMBER_ACCOUNT = /*#current*/'foo'/*END*/
   /*END*/
 /*END*/
 order by MEMBER_ID
e.g. 実行して囲った中の判定対象のコメントが全て無効だった場合 @DisplaySql
select * from MEMBER
 order by MEMBER_ID

連結文字の調整

また、連結文字の調整として、スコープ内で最初に有効になった調整対象のコメントの直後(空白や改行は含まれていてもよい)の {and, or, ","} が除去されます。こうすることで例えば、調整なしだと "where and ..." となってしまうような場合に、"where ..." というように調整することができます。調整対象のコメントは以下の通りです。

  • IFコメント
  • ELSEコメント
  • FORコメント @since 0.9.7.0
  • FIRSTコメント @since 0.9.7.0
  • ネストしたBEGINコメント(後述) @since 0.9.7.0
e.g. BEGINコメントで where 句をまるごと囲う @OutsideSql
select * from MEMBER
 /*BEGIN*/
 where
   /*IF pmb.memberId != null*/
   MEMBER_ID = /*pmb.memberId*/3
   /*END*/
   /*IF pmb.memberName != null*/
   and MEMBER_NAME like /*pmb.memberName*/'S%'
   /*END*/
   /*FOR pmb.memberAccountList*/
   and MEMBER_ACCOUNT = /*#current*/'foo'/*END*/
   /*END*/
 /*END*/
 order by MEMBER_ID
e.g. 実行して二番目のmemberNameだけが有効な場合 (and が消える) @DisplaySql
select * from MEMBER
 where
   MEMBER_NAME like 'M%' escape '|'
 order by MEMBER_ID

0.9.9.1Aより前のバージョン

カンマの除去に関して、調整対象のコメントの直後に改行や空白を含めると除去されませんので注意が必要です。 (and や or はこのバージョン以後も以前も変わらず改行や空白が入っていても特に問題なし)

BEGINコメントのネスト

BEGINコメントをネストさせることができます。"and" や "or" などの連結文字の調整は、ネストしたBEGINコメントごとに独立して処理されながらも、 (上位の)BEGINコメントの判定対象として、ネストしたBEGINコメントのスコープ内のIFコメントやFORコメントなどもしっかり含まれるため、サブクエリの where 句などに利用することができます。@since 0.9.7.0

また、ネストしたBEGINコメント自体の直後の "and" や "or" も調整されます。以下の例のようにサブクエリ内の条件が全て無効の場合に、サブクエリ自体を削除するというような記述が可能です。 ネストしたBEGINの直後の and も調整され、かつ、ネストしたBEGIN内の(最初に有効になった)IFコメントの直後の and も同時に調整されます。 (memberId が null の場合、ネストしたBEGIN直後の and が削除されます) @since 0.9.7.0

e.g. サブクエリの条件でネストしたBEGINコメントを利用 @OutsideSql
 /*BEGIN*/
 where
   /*IF pmb.memberId != null*/
   MEMBER_ID = /*pmb.memberId*/3
   /*END*/
   /*BEGIN*/
   and in (select PURCHASE_ID
             from PURCHASE
            where
              /*IF pmb.purchaseCount != null*/
              PURCHASE_COUNT >= /*pmb.purchaseCount*/2
              /*END*/
              /*IF pmb.purchasePrice != null*/
              and PURCHASE_PRICE >= /*pmb.purchasePrice*/2000
              /*END*/
   /*END*/
 /*END*/

0.9.7.0より前のバージョン

上位のBEGINコメントが考慮するIFコメントには、ネストしたBEGINコメント内のIFコメントが含まれません。 よって、ネストしたBEGINコメント内のIFコメントが true であっても、上位のBEGINコメント直下のIFコメントが全て false の場合は、(ネストされたスコープも含めて)上位スコープから丸ごと削除されます。例えば、サブクエリの where 句において、ネストしたBEGINコメントを利用する場合は、以下のようにサブクエリ自体を丸ごと分岐させるIFコメントを活用すると良いでしょう。

e.g. サブクエリの条件を分岐 @OutsideSql
 /*BEGIN*/
 where
   /*IF pmb.memberId != null*/
   MEMBER_ID = /*pmb.memberId*/3
   /*END*/
   /*IF pmb.purchaseCount != null || pmb.purchasePrice != null*/
   and in (select PURCHASE_ID
             from PURCHASE
            /*BEGIN*/
            where
              /*IF pmb.purchaseCount != null*/
              PURCHASE_COUNT >= /*pmb.purchaseCount*/2
              /*END*/
              /*IF pmb.purchasePrice != null*/
              and PURCHASE_PRICE >= /*pmb.purchasePrice*/2000
              /*END*/
            /*END*/
   /*END*/
 /*END*/

ENDコメント

スコープを定義するための終端を表すパラメータコメントです。/*END*/ という形式で記述します。ENDコメントを忘れると、スコープが定義できないため、明示的な例外が発生します。

FORコメント

アプリケーションから渡されるパラメータをもとにループして可変なSQLを展開するパラメータコメントです。主に、where 句における不定数の条件に利用します。@since 0.9.7.0

"/**/" 形式のコメントの中で、"FOR [list-expression]" という形式で記述し、ENDコメントでスコープを定義します。

e.g. FORコメントの利用 @OutsideSql
/*FOR pmb.memberNameList*/
and member.MEMBER_NAME = ...
/*END*/
e.g. FORコメントで展開された条件 {listSize=3} @ExecutedSql
and member.MEMBER_NAME = ...
and member.MEMBER_NAME = ...
and member.MEMBER_NAME = ...

細かい仕様

  • 指定するリストは、java.util.List の実装クラスである必要がある
  • FORコメントの中に、別のコメント(IFコメントなど)を定義することができる
  • リストが null や空リストの場合は何も展開されない(スコープがまるごと削除される)
  • リストの要素に null を含めることはできる @since 0.9.7.1

カレント変数

FORコメント内に、バインド変数コメントを含めることができます。 その際、バインド変数コメントでループの現在要素を利用するための カレント変数 を利用することができます。FORコメント内で "/*#current*/" と記述すると、そのバインド変数コメントは、現在要素の値を利用します。"/*#current.memberName*/" と続けてプロパティを辿ることもできます。また、IFコメントや(ネストした)FORコメント、埋め込み変数コメントでも同様に利用できます。

e.g. FORコメントの利用 @OutsideSql
/*FOR pmb.memberNameList*/
and member.MEMBER_NAME = /*#current*/'foo%'
/*END*/

ループ変数コメント

また、細かい制御をするための ループ変数コメント が用意されています。これらを利用して、実行時のSQLと2Way-SQLとの整合性と保つように調整します。

FIRSTコメント
最初のループで有効になる
NEXTコメント
二回目以降のループで有効になる
LASTコメント
最後のループで有効になる
e.g. where句の最初の条件としてFORコメントを利用 (NEXTコメント) @OutsideSql
/*BEGIN*/
where
  /*FOR pmb.memberNameList*/
  /*NEXT 'and '*/member.MEMBER_NAME like /*#current*/'S%'
  /*END*/
  /*IF pmb.memberStatusCode != null*/
  and member.MEMBER_STATUS_CODE = /*pmb.memberStatusCode*/'FML'
  /*END*/
/*END*/
e.g. FORコメントを利用して展開された表示用SQL(where句の最初の条件) @DisplaySql
where
  member.MEMBER_NAME like 'foo%'
  and member.MEMBER_NAME like 'bar%'
  and member.MEMBER_NAME like 'baz%'
e.g. where句の二番目以降の条件としてFORコメントを利用(FIRST, NEXT, LASTコメント) @OutsideSql
/*BEGIN*/
where
  /*IF pmb.memberId != null*/
  member.MEMBER_ID = /*pmb.memberId*/3
  /*END*/
  /*FOR pmb.memberNameList*//*FIRST*/and (/*END*/
    /*NEXT 'or '*/member.MEMBER_NAME like /*#current*/'S%'
  /*LAST*/)/*END*//*END*/
/*END*/
e.g. FORコメントを利用して展開された表示用SQL(where句の二番目以降の条件) @DisplaySql
where
  member.MEMBER_ID = 3
  and (
    member.MEMBER_NAME like 'foo%'
    or member.MEMBER_NAME like 'bar%'
    or member.MEMBER_NAME like 'baz%'
  )

ENDコメントで囲ったSQL文字を展開

ENDコメントで囲ったSQL文字を、有効となるループタイミングで展開することができます。 囲われた文字は、Sql2Entityタスクなどで実行される 2Way-SQL としてのSQLの一部として評価されます。

e.g. "and (" が最初のループのときにだけ有効になる @OutsideSql
/*FIRST*/and (/*END FIRST*/

コメント上で定義した値を展開

ENDコメントを省略して、コメントを定義した場所に、コメント上で定義した値を展開することができます。"[comment-name] '[value]'" という形式で記述します。(この場合、ENDコメントを定義してはいけません)

e.g. "or " が二回目以降のループのときにコメントと置き換わる @OutsideSql
/*NEXT 'or '*/

BEGINコメント内ではIFコメントと同じ扱い

BEGINコメント内ではIFコメントと同じように扱われます。BEGINで定義したスコープの判定要素として、FORコメントも考慮されます。 また、FORコメント内の先頭の "and" や "or" などの連結文字は、BEGINコメント内であればIFコメントと同じように調整されます。例えば、"where and ..." となってしまう場合は、"where ..." と調整されます。ゆえに、ループ内で単純に and 条件で連結するだけのような場合は、ループ変数コメントを利用しなくても解決が可能です。

e.g. ループ変数コメントを利用しなくてもいいような場合 @OutsideSql
/*BEGIN*/
where
  /*IF pmb.memberId != null*/
  member.MEMBER_ID = /*pmb.memberId*/3
  /*END*/
  /*FOR pmb.memberNameList*/
  and member.MEMBER_NAME = ...
  /*END*/
/*END*/
e.g. where句の先頭の条件になっても and が調整される @DisplaySql
where
  member.MEMBER_NAME = ...
  and member.MEMBER_NAME = ...
  and member.MEMBER_NAME = ...

LikeSearch条件のオプション

FORコメントで利用するリスト型のプロパティに対してLikeSearch条件のオプションを指定すると、 そのFORコメントの中で定義されているバインド変数コメントの中で、文字列型、かつ、カレント変数経由のもの全てに適用されます。

ParameterBean - LikeSearch条件のオプション
e.g. MEMBER_NAMEはLikeSearchOptionの処理対象となる @OutsideSql
-- !df:pmb!
-- !!List<String> memberNameList:likeContain!!
select ...
  from MEMBER
/*BEGIN*/
 where
  /*FOR pmb.memberNameList*/
  /*NEXT 'and '*/member.MEMBER_NAME like /*#current*/'S%'
  /*END*/
/*END*/
e.g. LikeSearchOptionが処理されたループ内の条件 @DisplaySql
where
  member.MEMBER_NAME = '%foo%' escape '|'
  and member.MEMBER_NAME = '%bar%' escape '|'
  and member.MEMBER_NAME = '%baz%' escape '|'

バインド変数コメントのLikeSearch条件の適用法則に当てはめると、 カレント変数で経由したものは繋がった一続きのプロパティ指定として扱われるため、ParameterBean のプロパティで指定したLikeSearch条件のオプションがFORコメント内のバインド変数コメントに引き継がれます。 (ネストしたFORコメントを途中でさらに挟んでも、カレント変数経由で繋がっていれば引き継がれます)

InLoopオプション

FORコメントの中で条件が複数あって、LikeSearch条件でない文字列の条件が含まれる場合や、それぞれLikeSearch条件を細かく設定したい場合に、 バインド変数コメントや埋め込み変数コメントのループ内限定のInLoopオプションを利用して調整することができます。"/*pmb.foo:[in-loop-option]*/" という形式で指定します。@since 0.9.7.1

notLike
LikeSearchではない
likePrefix
前方一致
likeContain
部分一致
likeSuffix
後方一致
e.g. MEMBER_NAMEは部分一致、MEMBER_ACCOUNTは単なる等値 @OutsideSql
-- !df:pmb!
-- !!List<$$Domain$$.Member> memberList:likeContain!!
select ...
  from MEMBER
/*BEGIN*/
 where
  /*IF pmb.member.memberId != null*/
  member.MEMBER_ID = /*pmb.member.memberId*/3
  /*END*/
  /*FOR pmb.memberList*//*FIRST*/and (/*END*/
    /*NEXT 'or '*/member.MEMBER_NAME like /*#current.memberName*/'%S%'
    or member.MEMBER_ACCOUNT = /*#current.memberAccount:notLike*/'Pixy'
  /*LAST*/)/*END*//*END*/
/*END*/

ParameterBeanのプロパティのオプションではLikeSearch条件のオプションを指定せずに、 それぞれのバインド変数コメントに付与するやり方もできます。あまりに一つのループ内に条件が多い場合に有効です。 ParameterBeanでのFORコメントのリスト型に対するプロパティのLikeSearch条件のオプションは、"デフォルト" のLikeSearch条件という位置付けと考えて良いでしょう。

InLoopオプションの指定は、一番強制力の強いオプション指定と言えます。

InLoopオプションで、動的に LikeSearchOption を受け取るオプション (likeオプション) は、通常の方法ではサポートされていません。 非常に回避的で内部構造依存な方法ではありますが、対象のBeanクラスに "[対象カラムのプロパティ名]InternalLikeSearchOption" というプロパティ名のgetter/setterを作成して、動的な LikeSearchOption を指定することができます。 (互換性は保たれますが、将来、代替するやり方が出てくるかもしれません)

FORコメントのネスト

FORコメントをネストさせることができます。但し、あまり利用ケースは想定されていません。 パラメータコメント上であまりに複雑になるようであれば、プログラム側でパラメータコメント上で扱いやすい形に変換する方が良いでしょう。

また、上位のFORコメントのカレント変数は、ネストしたFORコメント内では利用できません。 ネストしたFORコメント内でのカレント変数は、ネストしたFORコメントのためのカレント変数となります。

e.g. purchaseListの中のカレント変数は、purchaseListのカレント @OutsideSql
-- !df:pmb!
-- !!List<$$Domain$$.Member> memberList:likeContain!!
...
  /*FOR pmb.memberList*/
    /*FOR #current.purchaseList*/
  and PURCHASE_PRICE like /*#current.purchasePrice*/'%S%'
    /*END*/
  /*END*/

どうしても上位のカレント変数をネストしたFORコメント内で利用したい場合は、ネストしたFORコメントのカレント変数のオブジェクトに、 上位のカレント変数への参照を保持させることで実現できます。(オブジェクトの構造で解決します)

e.g. purchaseListの中のカレント変数から、memberListのカレントを参照 @OutsideSql
-- !df:pmb!
-- !!List<$$Domain$$.Member> memberList:likeContain!!
...
  /*FOR pmb.memberList*/
    /*FOR #current.purchaseList*/
  and MEMBER_ID = /*#current.member.memberId*/3
  and PURCHASE_PRICE like /*#current.purchasePrice*/'%S%'
    /*END*/
  /*END*/

FORコメントのExampleのススメ

dbflute-basic-example の MemberBhv_selectPurchaseMaxPriceMember.sql および MemberBhv_selectPurchaseSummaryMember.sql にて、このFORコメントを利用したExample実装があります。 また、それら外だしSQLを実行するテストケースもありますので合わせてご覧下さい。

FORコメントの存在価値

DBFluteでは、実はそんなにFORコメントが利用されることは多くはないと想定されます。なぜなら、ConditionBean があるからです。不定数の条件を扱うときはそれ以外の条件があまり存在しないことも多く、それであればなおさら ConditionBean で済みます。さらに、不定数の条件が LikeSearch 条件(曖昧検索の条件)であれば、ConditionBean で LikeSearchOption の SplitBy 機能を使うことで、非常に簡単に安全に実装できます。

もちろん、FORコメントが要らない訳ではなく、DBFluteでの外だしSQLの役割を考えるときに、 "いざとなれば何でもできる" という安心感が保たれていることが重要です。