[Seasar-user:8936] Re: [DBFlute] 動的にSQLを生成する方法

kubo [E-MAIL ADDRESS DELETED]
2007年 7月 4日 (水) 13:05:25 JST


久保です。

丸岡さんへ

> >> 例えば XXXXDaoクラスに
> >>     public Entity[] dynamicXxxSql(String sql, List condition);
> >> 
> >> のようなメソッドを追加すれば、引数の1つ目がプログラムで生成したSQL
> >> 2つ目の引数をリストで渡せば、PreparedStatementのパラメタにセットしてくれる
> >> ようなことはできませんか?
> >> 
> >> 既にこのようなことが可能でしたらその方法を教えていただけませんでしょうか?
> >> また、もし、現在そのような仕組みが無いようでしたら
> >> 要望として挙げさせていただきたいです。
> >
> >も、今検討中です。
> >
> >「ConditionBeanでできないことは外だしSQLにすれば良い」というのが通じない
> >パターンがまさしく今回で(不定数に関しては逆にConditionBeanでないと実現もできない)、
> >自分も実業務で困ったことがあるためどうにかしたいのですが、
> >あまりやり方を増やして利用方法が煩雑になるのも避けたいとも考えています。
> >(ちょっと考えさせてください)
> 
> はい。
> もし、上記のような仕組みがあれば、とりあえず何でもできそうですので
> 私としては実装されると面白いかなと思います。

public Entity[] dynamicXxxSql(String sql, List condition);

に関しては、

A. Entityと指定されたSelect句のMappingがTypeSafeでない。
   (Sql2Entityが利用できない)
B. そもそも内部的に実現がしづらい。(すいません...S2Daoとの連携が...)

という理由から、以下の方法での実現します。

「埋め込み変数コメントで指定する文字列にBind変数コメントを記載」
現状S2Daoではこれが出来ないのですが(静的解析なのでそれは当然)、
DBFluteでこれをできるようにします。

以下

/--------------------------------------------------------【Daoの宣言】
@OutsideSql(dynamicAnalysis=true) ★動的解析true
public java.util.List<DynamicAnalysisBook> selectDynamicAnalysisBook(DynamicAnalysisPmb pmb);
----------/

/--------------------------------------------------------【外だしSQL】
--#DynamicAnalysisBook#

--!DynamicAnalysisPmb!
--!!String firstOutsideSqlPiece!! ★埋め込み変数コメントと同じProperty名
--!!String secondOutsideSqlPiece!!★埋め込み変数コメントと同じProperty名
--!!java.math.BigDecimal bookId!!
--!!java.util.Map<String, java.math.BigDecimal> bookIdMap!!

select book.BOOK_ID, book.BOOK_NAME
  from BOOK book
/*$pmb.firstOutsideSqlPiece*/   ★埋め込み変数コメント
/*$pmb.secondOutsideSqlPiece*/  ★埋め込み変数コメント
 order by BOOK_ID asc
;
----------/

/--------------------------------------------------------【クライアント】
final DynamicAnalysisPmb pmb = new DynamicAnalysisPmb();

// First!
pmb.setFirstOutsideSqlPiece(" where book.BOOK_ID = /*pmb.bookId*/null");

// Second!
final Map<String, BigDecimal> parameterMap = new LinkedHashMap<String, BigDecimal>();
final List<BigDecimal> bookIdList = new ArrayList<BigDecimal>();
bookIdList.add(new BigDecimal(2));
bookIdList.add(new BigDecimal(4));
bookIdList.add(new BigDecimal(5));
int index = 0;
final StringBuilder sb = new StringBuilder();
for (BigDecimal parameter : bookIdList) { ★不定数条件の生成(値はMapに積む)
    parameterMap.put("index" + index, parameter);
    sb.append(" union select book.BOOK_ID, book.BOOK_NAME from BOOK where book.BOOK_ID = ");
    sb.append("/*pmb.bookIdMap.index").append(index).append("*/null ").append(getLineSeparator());
    ++index;
}
pmb.setSecondOutsideSqlPiece(sb.toString());

// Set parameters
pmb.setBookId(new BigDecimal(1));
pmb.setBookIdMap(parameterMap);

// ## Act ##
final List<DynamicAnalysisBook> bookList = getMyDao().selectDynamicAnalysisBook(pmb);
----------/

【補足】
@OutsideSqlは元々、「外だしSQLを利用すること」を明示的に宣言する
Annotationです。これを付けておくと、いざSQLファイルが存在しない場合に、
SQLファイルが無いことを示す専用の例外が発生します。
そのOptionとして「dynamicAnalysis」というPropertyを追加しました。
これはDefaultはfalseです。

通常S2Daoでは、「pmb.bookIdMap.index...」というように、
Mapのキー値でPropertyを取得することはできないのですが、
DBFluteではS2Daoを拡張してこれができるようになっています。
なので、不定数のBind値が表現可能です。
(java.util.Listには対応してないので、MapのKey値をIndexにします)

【制限】
埋め込み変数コメントの「/*$pmb.secondOutsideSqlPiece*/」は、
“pmb”であることが前提です。(pb とか bookPmb とかはNG)
(Javaで変数名を取得できないのが痛いと心痛。取り急ぎ現状は固定)

第1引数のPmbのPropertyだけが対象です。
Pmbで指定したPropertyと同名の埋め込み変数コメントをご利用下さい。

動的解析ですので、通常の外だしSQLよりかは若干遅くなります。
(但し妥協はできる程度ではないかと)

【メリット】
A. 普通の外だしSQLのようにSql2Entityが利用できる。(MappingがSafetyである)
B. Bind変数を利用した動的なSQL文の一部文字列をプログラムから指定できる。



この方法にて、不定数条件のSQLを表現できるかと思います。
こちら、今週末くらいに公開するDBFlute-0.5.3-RC2に反映させます。

無論、できるのであれば、ConditionBeanで実現してしまう方が良いですが、
どうしても外だしSQLにせざるを得なくて、かつ、不定数条件の場合に
上記の方法を利用するという感じになります。





Seasar-user メーリングリストの案内