ハンズオンセクション 3

概要

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

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

事前準備

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

【事務連絡】org.dbflute.handson から、org.docksidestage.handson に変わりました。 org.dbfluteで開始した人は、そのまま org.dbflute で続けてOKです。もし、移行するなら log4j.properties と basicInfoMap.dfprop の該当箇所を修正してください。

ConditionBeanのストレッチ

それでは、ConditionBeanのストレッチです。 ConditionBeanの使い方、機能のページを参考にしながら実装しましょう。

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

Good Luck!

Silverストレッチ

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

Goldストレッチ

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

Platinumストレッチ

[7] 正式会員になってから一週間以内の購入を検索
  • 会員と会員ステータス、会員セキュリティ情報も一緒に取得
  • 商品と商品ステータス、商品カテゴリ、さらに上位の商品カテゴリも一緒に取得
  • 上位の商品カテゴリ名が取得できていることをアサート
  • 購入日時が正式会員になってから一週間以内であることをアサート
※ログ出力と書いてなくても、テストの動作を確認するためにも(自由に)ログ出力すると良い。
※実装できたら、こんどはスーパークラスのメソッド adjustPurchase_PurchaseDatetime_...() を呼び出し、調整されたデータによって検索結果が一件増えるかどうか確認してみましょう。 もし増えないなら、なぜ増えないのか...
[8] 1974年までに生まれた、もしくは不明の会員を検索
  • "1974/01/01" という文字列がリクエストされたと想定 (Arrangeで宣言)
  • それら日付を、ArrangeやActの中では一切動かさずに実現すること (日付型に変換するのはOK)
  • 会員ステータス名称、リマインダ質問と回答、退会理由入力テキストを取得する(ログ出力) ※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/06/01" という文字列がリクエストされたと想定 (Arrangeで宣言)
  • テストコード内で "2005/06/01" 以外の日付は作らずに条件を実現すること
  • 第二ソートキーは会員IDの降順
  • 検索された会員の生年月日が存在しないことをアサート
  • 2005年6月に正式会員になった会員が先に並んでいることをアサート (先頭だけじゃなく全体をチェック)

HandyDateが使えます

java.util.Calendarは、色々と落とし穴があります。 ハンズオンでは、ぜひ、HandyDateというクラスを使ってみてください。日付に関する色々な便利メソッドがあります。

e.g. HandyDateを使ってみる @Java
// 2006/09/26 の Date 型
Date targetDate = new HandyDate("2006/09/26").getDate();

// 2006/09/26 12:34:56 の Date 型
Timestamp targetTime
    = new HandyDate("2006/09/26 12:34:56").getTimestamp();

// 2006/09/27 の Date 型
Date targetDate
    = new HandyDate("2006/09/26").addDay(1).getDate();

// 2006/09/27 00:00:00 の Date 型
String exp = "2006/09/26 12:34:56";
Date targetDate
    = new HandyDate(exp).addDay(1).moveToDayJust().getDate();

HandyDate handyDate = new HandyDate("2006/09/26");
handyDate.addDay(1); // HandyDate自体のインスタンスの日付が変わる
Date targetDate = handyDate.getDate(); // 2006/09/27 の Date 型

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 を実感してみるとよいでしょう。

模範解答的な

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

次のセクション

さて、次のセクションへ