ハンズオンセクション 3

概要

さて、ハンズオンの続きです。

"ConditionBeanの筋トレ" をしていきましょう。

事前準備

org.docksidestage.handson.exercise.HandsOn03Test クラスを作成してください。 このクラスは UnitContainerTestCase を継承します。また、ERDを開いておくと良いでしょう。

ConditionBeanのストレッチ

ConditionBeanの使い方、機能のページを参考に。

コーディングポリシー

ハンズオンのコーディングポリシーを用意していますので、意識しながら実装しましょう。(特にjfluteのレビューを受ける予定の方は、こちらをよろしくおねがいします)

テストの前提もアサートしよう

テストが成り立っていることをアサートすることも大事です。 また、明示的に指定されていなくても、ログ出力でテストが成り立っていることを目でも確認できるように必要に応じて臨機で入れていくと良いでしょう。

コラムの記事も読みましょう

ところどころに登場するコラムの記事 (jflute's Column) も、ぜひ読んでいきましょう。 これもハンズオンの一環です。

Silverストレッチ

[1] 会員名称がSで始まる1968年1月1日以前に生まれた会員を検索
  • 会員ステータスも取得する
  • 生年月日の昇順で並べる
  • 会員が1968/01/01以前であることをアサート
※"以前" の解釈は、"その日ぴったりも含む" で。
[2] 会員ステータスと会員セキュリティ情報も取得して会員を検索
  • 若い順で並べる。生年月日がない人は会員IDの昇順で並ぶようにする
  • 会員ステータスと会員セキュリティ情報が存在することをアサート
※カージナリティを意識しましょう
[3] 会員セキュリティ情報のリマインダ質問で2という文字が含まれている会員を検索
  • 会員セキュリティ情報のデータ自体は要らない
  • (Actでの検索は本番でも実行されることを想定し、テスト都合でパフォーマンス劣化させないこと)
  • リマインダ質問に2が含まれていることをアサート
  • アサートするために別途検索処理を入れても誰も文句は言わない
※修行++: 実装できたら、(もし複数回検索していたら) Assert内の検索が一回になるようにしてみましょう。 その際、Act内で検索しているデータを、Assert内でもう一度検索することなく実現してみましょう。
※修行#: さらに実装できたら、会員名称とリマインダ質問を会員ごとに一行のログに出力するようにしてみましょう。 その際、ArrangeとActには手を入れず、Assert内だけの修正で実現してみましょう。

他にも似たところないか探す習慣を

また、何かひとつレビューで指摘されたら、他のところにも似たようなものがないか確認する習慣を付けましょう。 例えば本番運用でバグが見つかって、また別の場所で同じバグが発生すると効率が悪いです。

間違いはあっても、一回で過去も未来も直せるプログラマーは信頼できるものです。 同じ指摘を受けないように意識してみましょう。

Goldストレッチ

[4] 会員ステータスの表示順カラムで会員を並べて検索
  • 会員ステータスの "表示順" カラムの昇順で並べる
  • 会員ステータスのデータ自体は要らない
  • その次には、会員の会員IDの降順で並べる
  • 会員ステータスのデータが取れていないことをアサート
  • 会員が会員ステータスごとに固まって並んでいることをアサート (順序は問わない)
[5] 生年月日が存在する会員の購入を検索
  • 会員名称と会員ステータス名称と商品名を取得する(ログ出力)
  • 購入日時の降順、購入価格の降順、商品IDの昇順、会員IDの昇順で並べる
  • OrderBy がたくさん追加されていることをログで目視確認すること
  • 購入に紐づく会員の生年月日が存在することをアサート
[6] 2005年10月の1日から3日までに正式会員になった会員を検索
  • 画面からの検索条件で2005年10月1日と2005年10月3日がリクエストされたと想定して...
  • Arrange で String の "2005/10/01", "2005/10/03" を一度宣言してから日時クラスに変換し...
  • 自分で日付移動などはせず、DBFluteの機能を使って、そのままの日付(日時)を使って条件を設定
  • 会員ステータスも一緒に取得
  • ただし、会員ステータス名称だけ取得できればいい (説明や表示順カラムは不要)
  • 会員名称に "vi" を含む会員を検索
  • 会員名称と正式会員日時と会員ステータス名称をログに出力
  • 会員ステータスがコードと名称だけが取得されていることをアサート
  • 会員の正式会員日時が指定された条件の範囲内であることをアサート
※Java8 (DBFlute-1.1) なら、assertException(...)を使うとよいでしょう
※修行++: 実装できたら、こんどはスーパークラスのメソッド adjustMember_FormalizedDatetime_...() を使って、10月1日ジャスト(時分秒なし)の正式会員日時を持つ会員データを作成してテスト実行してみましょう。 もともと一件しかなかった検索結果が「二件」になるはずです。

コメントを意識してみよう

ハンズオンでは、ソースコードのコメントも意識してみましょう。

ぜひ、こちらのブログもお読みください。

書いておくと嬉しいコメントってどんなコメント? を考え続けることが大切です。

Platinumストレッチ

[7] 正式会員になってから一週間以内の購入を検索
  • 会員と会員ステータス、会員セキュリティ情報も一緒に取得
  • 商品と商品ステータス、商品カテゴリ、さらに上位の商品カテゴリも一緒に取得
  • 上位の商品カテゴリ名が取得できていることをアサート
  • 購入日時が正式会員になってから一週間以内であることをアサート
※修行++: 実装できたら、こんどはスーパークラスのメソッド adjustPurchase_PurchaseDatetime_...() を呼び出し、調整されたデータによって検索結果が一件増えるかどうか確認してみましょう。 もし増えないなら、なぜ増えないのか?しっかり分析して、コード上のコメントで分析結果を書き出してみましょう。 そして、一週間以内という解釈を無理のない程度に変えて、増えるようにしてみましょう。 もともと増えたのであれば、なぜ増えたのか?が把握できていたらOKです。
[8] 1974年までに生まれた、もしくは不明の会員を検索
  • 画面からの検索条件で1974年がリクエストされたと想定
  • Arrange で String の "1974/01/01" を一度宣言してから日付クラスに変換
  • その日付クラスの値を、(日付移動などせず)そのまま使って検索条件を実現
  • 会員ステータス名称、リマインダ質問と回答、退会理由入力テキストを取得する(ログ出力) ※1
  • 若い順だが生年月日が null のデータを最初に並べる
  • 生年月日が指定された条件に合致することをアサート (1975年1月1日なら落ちるように)
  • Arrangeで "きわどいデータ" ※2 を作ってみましょう (Behavior の updateNonstrict() ※3 を使って)
  • 検索で含まれるはずの "きわどいデータ" が検索されてることをアサート (アサート自体の保証のため)
  • 生まれが不明の会員が先頭になっていることをアサート
※1: ログについて、値がない項目は "none" を出力。if文使わないように。ヒント: Java8なら map()
※2: 1974年12月31日生まれの人、1975年1月1日生まれの人。前者は検索に含まれて、後者は含まれない。 テストデータに存在しない、もしくは、存在に依存するのがためらうほどのピンポイントのデータは、自分で作っちゃうというのも一つの手。 (エクササイズ 6 や 7 でやっていた adjust がまさしくそれ: 同じように adjustXxx() という感じでprivateメソッドにしましょう)
※3: 1 から 9 までの任意の会員IDを選び updateNonstrict() してみましょう。 まあ、一桁代の会員IDが存在すること自体への依存は割り切りで。最低限それだけのテストデータで用意されていないとお話にならないってことで。
※今後、"きわどいデータ" を作ってアサートを確かなものにするかどうかは自分の判断で。
[9] 2005年6月に正式会員になった会員を先に並べて生年月日のない会員を検索
  • 画面からの検索条件で2005年6月がリクエストされたと想定
  • Arrange で String の "2005/06/01" を一度宣言してから日付クラスに変換
  • その日付クラスの値を、(日付移動などせず)そのまま使って検索条件を実現
  • 第二ソートキーは会員IDの降順
  • 検索された会員の生年月日が存在しないことをアサート
  • 2005年6月に正式会員になった会員が先に並んでいることをアサート (先頭だけじゃなく全体をチェック)

true/falseだけだからこそ変数名に迷う

プログラムでは、booleanを使って何か制御することも多いです。

ぜひ、こちらのブログもお読みください。

booleanは処理を分岐させる力があるので、実は影響力が強い変数なのですよね。

CBでページングしてみよう

まずは、ページング検索という概念を学びましょう。

そして、ConditionBeanを使ったページング検索のエクササイズをやってみましょう。

全ての会員をページング検索
  • 会員ステータス名称も取得
  • 会員IDの昇順で並べる
  • ページサイズは 3、ページ番号は 1 で検索すること
  • 会員ID、会員名称、会員ステータス名称をログに出力
  • SQLのログでカウント検索時と実データ検索時の違いを確認
  • 総レコード件数が会員テーブルの全件であることをアサート
  • 総ページ数が期待通りのページ数(計算で導出)であることをアサート
  • 検索結果のページサイズ、ページ番号が指定されたものであることをアサート
  • 検索結果が指定されたページサイズ分のデータだけであることをアサート
  • PageRangeを 3 にして PageNumberList を取得し、[1, 2, 3, 4]であることをアサート
  • 前のページが存在しないことをアサート
  • 次のページが存在することをアサート

CBでカーソル検索ってみよう

まずは、カーソル検索という概念を学びましょう。

そして、ConditionBeanを使ったカーソル検索のエクササイズをやってみましょう。

会員ステータスの表示順カラムで会員を並べてカーソル検索
  • 会員ステータスの "表示順" カラムの昇順で並べる
  • 会員ステータスのデータも取得
  • その次には、会員の会員IDの降順で並べる
  • 会員ステータスが取れていることをアサート
  • 会員が会員ステータスごとに固まって並んでいることをアサート
  • 検索したデータをまるごとメモリ上に持ってはいけない
  • (要は、検索結果レコード件数と同サイズのリストや配列の作成はダメ)

InnerJoinAutoDetectってみよう

ConditionBeanには、InnerJoinAutoDetectという機能があります。

この機能は既に有効になっています(@since 1.0.3)。 ここではその効用を学びつつ、そもそも結合の特性や、バインド変数の概念を掴んでみましょう。

いままで書いたエクササイズでもいいですし、新たに適当なテストメソッドを作ってもいいので、ログのSQLを目視で確認して InnerJoinAutoDetect を実感してみるとよいでしょう。

模範解答的な

模範解答的な実装を紹介しています。(参考までに)

次のセクション

さて、次のセクションへ