Seasar Conference 2009 White The End

@DBFlute
ご来場頂きました皆様、本当にありがとうございます。
なんとか無事カンファレンスを終えることができました。

セッションは、今までにないひたすらデモとしゃべりのみということで、
なかなかドキドキでしたが、伝えたいことはかなり伝えられたのでは
と思っております。ただもちろんのこと資料がないので、
復習のために記事を書こうかと思います。
(動きとか細かい部分は記事では表現できないので、
 この記事を読んでセッションの内容を頭の中で思い出して下さい)
1. ConditionBeanの紹介・基本

DBアクセス実装する前に目的を明確にすることが大事です。
これはDBFluteに限らず全てのDBアクセスにおいて同様です。
その目的さえはっきりしていれば、ConditionBeanの場合は
その目的に沿って指定することで実装を進めることが可能です。
(CBは目的指向のインターフェースを提供している)

ConditionBeanの実装手順(思考フロー)は、
DBアクセスを構造的に理解することにつながると考えています。
特に若い人には「DBアクセスを構造的に理解する」というのを
身につけて頂きたいと考えております。
ConditionBean的なの実装手順(思考フロー)を身につけることが、
通常のSQLを実装や別のO/Rマッパのインターフェースを
使うときにも役に立つと考えています。
http://d.hatena.ne.jp/r_ikeda/20090315/dbflute_demo
早速こちらで紹介して頂いております。
ありがとうございます!

セッションでは紹介していませんが、目的と手段の関係で
一番わかりやすい例を「gihyp.jp:ConditionBeanにおける結合やソート」
にて書かれていますので、こちらも読まれると良いかと思います。

そして、大事なポイントが以下になります。

o new XxxCB();と書いて「Shift + Alt + L」で左辺補完
o 「cb.se」まで書いたら補完で関連テーブル選択「cb.setupSelect_xxx()」
o 「cb.q」まで書いたら補完で「cb.query()」
o 「cb.query().set」の後はキャメルケースによる補完奥義

「Shift + Alt + L」は、言葉で覚えるよりキーボードの場所で
覚えるのが良いでしょう。10回ほど反復練習すれば手が覚えます。
但し、その練習風景を他人に見られると変な人だと思われます。

キャメルケースによる補完については、具体的な例としては、
setMemberName_PrefixSearch()なら「setMNPS」と打ってEnterでOK!
これはどのメソッドでも利用可能です。先頭の大文字をリズミカルに
「パン!パン!パン!」と押していくことが大事です。
大文字部分を口に出しても良いですが変な人だと思われます。

ログに出力されるSQL(というか生成されるSQL)が、改行含みで
しっかりフォーマットされているところもポイントです。
地味ながらとってもおしゃれなことなのです。
ログについては例と一緒に詳しく説明がこちらにありますのでぜひご覧下さい。
DBFluteのロギング
1.5. テストケースの実装

ちょっと脱線ネタでしたが、同じ部屋の前のセッションから参加されて
いる人のために、「JUnitを使ったテストケースの実装の仕方」を簡単に
説明させて頂きました。主にはAssertの実装をデモ致しました。
(無茶苦茶アドリブです...)

テストが成り立っていることをAssertするって概念を強く
主張させて頂きました。リストをAssertするときなど、
for文の中でしかAssertしてないとリストが0件のときは
テストが通ってしまいます。そのリストが少なくとも
「0件以上である(そもそもテストが成り立っていること)」
ということをAssertするのも大事です。
2. 関連テーブルの取得「setupSelect」

関連テーブルのカラムをSelect句にSetupするという意味。
基本的に親テーブル(many-to-one)を指定できます。
但し、1:1テーブル(one-to-one)も指定できます。
cb.setupSelect_Xxx().withYyy().with...
と無限階層に指定することが可能です。
3. 絞り込み・ソート「query」

絞り込み・ソートはとりあえず「cb.query()」。
「cb.query().setXxx_...()」で絞り込み。
「cb.query().addOrderBy_Xxx_...()」でソート。
(addOrderBy_MemberName_Desc()ならaddOBMNDで補完可能)
4. 区分値「Classification」

// 検索条件:会員ステータスが「正式会員」であること
cb.query().setMemberStatusCode_Formalized();
というように区分値メソッドがあればそれを使いましょう。
なければ、アーキテクトの人に言って設定してもらいましょう。

セッションでは紹介できませんでしたが、
Entityにも区分値メソッドが存在します。

// 会員ステータスが「正式会員」か否か
entity.isMemberStatusCodeFormalized();

// 会員ステータスを「正式会員」に設定
entity.classifyMemberStatusCodeFormalized();
5. 曖昧検索「LikeSearch」

cb.query().setMemberName_LikeSearch();
第二引数の「new LikeSearchOption();」で色々指定。
new LikeSearchOption().likeContain() → 中間一致
new LikeSearchOption().likePrefix() → 前方一致
new LikeSearchOption().likeSuffix() → 後方一致

escapeByXxx()でエスケープ処理が可能。
エスケープ処理をしてないと、ユーザが入力した「%」が
そのまま曖昧検索のワイルドカードになってしまう可能性があり、
前方一致でインデックス利用を前提とした検索が中間一致に
なってしまい、検索処理が帰ってこないというトラブルもあります。
この機能を覚えることで「エスケープ処理」という概念を
ディベロッパーの人にはぜひ覚えて欲しいです。

セッション後に質問がありましたが、
プロジェクト全体でエスケープ処理が必須なので強制させたい
というような場合は、includeQueryMap.dfpropの機能を使って、
PrefixSearchを削除して、LikeSearchOptionを継承した
プロジェクト独自のLikeSearchOptionを作って、
エスケープ処理が必須になるように仕向けるのが良いでしょう。

※ご質問ありがとうございました!
6. 子テーブルの条件で絞り込み「ExistsSubQuery」

まずは「子テーブルの条件で絞り込み」をするっていう概念を
ディベロッパーの方には覚えて頂きたいです。
会員検索の条件で「2000円以上の購入をしたことのある会員」
というような場合です。

ポイントは...ちょっと言葉で表せませんね...
ここはセッションに来た人だけの特典ということで。
(動きのあるドキュメントも将来的には作りたいですね...)
(追記:あ、なにげにこんなところにあった...)

セッション後に質問がありましたが、
「exists(相関サブクエリ)」だけでなく「in(相関じゃない)」も可能です。
http://d.hatena.ne.jp/jflute/20081224/1230052469
但し、子テーブルに対するSubQueryの場合は、
ほとんどの場合においてexistsの方が速いでしょう。
会員テーブルが1000000件で購入テーブルが30件くらいしかない
というような極端な場合は「in」の方が圧倒的に速いですが、
通常「one-to-many」の関連でそういう状況は少ないでしょう。
会員テーブルが1000000件で購入テーブルが10000000件という
ような状況であれば「exists」の方が向いています。
(当然、検索条件にも依存しますが...)

※ご質問ありがとうございました!
7. Nullをどっち側に並べるか「Nulls First/Last」

これもNullableなカラムでソートするような場合は、
「Nullをどっち側に並べるか」という概念を気にする必要が
あることを覚えて頂きたいです。
会員検索のソート条件で「Nullの入る可能性のある生年月日の降順」
で並べるような場合です。

cb.query().addOrderBy_Birthdate_Desc().withNullsFirst()
cb.query().addOrderBy_Birthdate_Desc().withNullsLast()

と、OrderByの後に続けて指定することが可能です。
詳しくは
http://d.hatena.ne.jp/jflute/20080504/1209880427
をご覧下さい。
8. 取得するカラムを指定する「SpecifyColumn」

ConditionBeanは基本的に指定されたテーブルのカラムを全て
取得します。不要なカラムも取得してしまうのですが、
通常は割り切っても特に問題は発生しません。
が、テーブルのカラム数が100個あるとか、CLOBはBLOBが
たくさん存在するとか、そういうようなテーブルの場合は
結構痛いコストになる可能性があります。
(基本的にはDB設計的に「one-to-one」を活用して、
 カラム数の調整やCLOBやBLOBを外だしテーブルにしたり
 することがお奨めですが、そうできないような場合)

ConditionBeanは取得するカラムを指定することができます。
詳しくは
http://d.hatena.ne.jp/jflute/20080625/1214329516
をご覧下さい。
9. 子テーブルの導出カラム「DerivedReferrer」

セッションでは時間の都合上紹介できませんでした。
正確には:

「子テーブルの導出カラムを取得(+ ソート)」
(Specify)DerivedReferrer

「子テーブルの導出カラムで絞り込み」
(Query)DerivedReferrer

と分類できます。
詳しくは
http://d.hatena.ne.jp/jflute/20080625/1214329516
http://d.hatena.ne.jp/jflute/20090109/1231481996
をご覧下さい。
10. 最大値レコードを検索「ScalarSubQuery」

セッションでは時間の都合上紹介できませんでした。
詳しくは
http://d.hatena.ne.jp/jflute/20090101/1230736087
をご覧下さい。
11. その他Eclipseのショートカット

一行削除:Ctrl + D
for文の補完:リスト宣言の直下で「fore + Enter!」
選択行移動:Alt + 上矢印(or 下矢印)
選択行コピー:Ctrl + Alt + 上矢印(or 下矢印)
12. まとめ

こういう動きのある勉強会を各現場で開いてディベロッパー同士で
情報共有することが大事だと思います。動きのある実装を目で見て、
機能名を口に出してディベロッパー同士で会話すること、これらが
実は大事で、ただサイトみたりExampleみたりするだけよりも格段に
勉強効率が上がります。ぜひ開催を検討して見て下さい。
(もし、よければ自分にご相談して頂いても...)

単純に実装のためにConditionBeanを覚えるというのもありますが、
主張したかったのは、ConditionBeanは「DBアクセスのエッセンス」を
構造的に理解・覚えるために非常に役に立つということです。
DBアクセスを実装するときの思考フローもさることながら、
「Nullをどっち側に並べる」とか「子テーブルの条件で絞り込む」とか
「子テーブルの導出カラム」とか「エスケープ処理」とか、
こういった概念をしっかり理解することがとても大事だと考えています。

でも、一番主張したかったのはEclipseのショートカットですかね...
皆様ありがとうございました!