ハンズオンセクション 5
- 概要
- 事前準備
- 業務的one-to-oneとは?
- 業務的one-to-oneの関連付け
- 業務的one-to-oneを利用した実装
- 導出的one-to-oneを利用した実装
- テストデータの登録時チェック
- SQLのエリアス名のこだわり
- 次のセクション
概要
さて、ハンズオンの続きです。
"業務的one-to-oneとデータチェック" を学んでいきましょう。
事前準備
org.docksidestage.handson.exercise.HandsOn05Test クラスを作成してください。 このクラスは UnitContainerTestCase を継承します。また、ERDを開いておくと良いでしょう。
【事務連絡】org.dbflute.handson から、org.docksidestage.handson に変わりました。 org.dbfluteで開始した人は、そのまま org.dbflute で続けてOKです。もし、移行するなら log4j.properties と basicInfoMap.dfprop の該当箇所を修正してください。
業務的one-to-oneとは?
まずは、以下のテストを作成してください。
- 会員住所情報を検索
-
- 会員名称、有効開始日、有効終了日、住所、地域名称をログに出して確認する
- 会員IDの昇順、有効開始日の降順で並べる
会員住所情報は、会員から見て "業務的one-to-one" と言えます。
業務的one-to-oneの関連付け
additionalForeignKeyMap.dfprop に、以下の定義を設定してください。
e.g. 会員と会員住所情報に業務的one-to-oneを定義 @additionalForeignKeyMap.dfprop
...
; FK_MEMBER_MEMBER_ADDRESS_VALID = map:{
; localTableName = MEMBER ; foreignTableName = MEMBER_ADDRESS
; localColumnName = MEMBER_ID ; foreignColumnName = MEMBER_ID
; fixedCondition =
$$foreignAlias$$.VALID_BEGIN_DATE <= /*targetDate(Date)*/null
and $$foreignAlias$$.VALID_END_DATE >= /*targetDate(Date)*/null
; fixedSuffix = AsValid
; comment = 有効な会員住所 (現在日時を入れれば現在住所)
}
...
そして、再自動生成(DocとGenerate)をし、SchemaHTMLにて関連付いていることを確認してください。 AdditionalForeignKeyの場合は、普通のFKとはちょっとだけ表示が異なっています。
業務的one-to-oneを利用した実装
それでは、業務的one-to-oneを利用して、以下のテストを作成してください。
- 会員と共に現在の住所を取得して検索
-
- SetupSelectのJavaDocにcommentがあることを確認すること
- 会員名称と住所をログに出して確認すること
- 現在日付はスーパークラスのメソッドを利用 ("c" 始まりのメソッド)
- 会員住所情報が取得できていることをアサート
- 千葉に住んでいる会員の支払済み購入を検索
-
- 会員ステータス名称と住所をログに出して確認すること
- 購入に紐づいている会員の住所の地域が千葉であることをアサート
導出的one-to-oneを利用した実装
それでは、以下の導出的one-to-oneの関連を設定して、エクササイズをやってみましょう。
e.g. 会員と会員ログイン情報に導出的one-to-oneを定義 @additionalForeignKeyMap.dfprop
...
; FK_MEMBER_MEMBER_LOGIN_LATEST = map:{
; localTableName = MEMBER ; foreignTableName = MEMBER_LOGIN
; localColumnName = MEMBER_ID ; foreignColumnName = MEMBER_ID
; fixedCondition =
$$foreignAlias$$.LOGIN_DATETIME = ($$sqbegin$$
select max(login.LOGIN_DATETIME)
from MEMBER_LOGIN login
where login.MEMBER_ID = $$foreignAlias$$.MEMBER_ID
)$$sqend$$
; fixedSuffix = AsLatest
}
...
commentは、いい感じになんか入れてみましょう。
- 最終ログイン時の会員ステータスを取得して会員を検索
-
- SetupSelectのJavaDocに自分で設定したcommentが表示されることを目視確認
- 会員名称と最終ログイン日時と最終ログイン時の会員ステータス名称をログに出す
- 最終ログイン日時が取得できてることをアサート
テストデータの登録時チェック
このような業務的な制約はどうしても存在してしまいます。 プログラム上では、不整合なデータが発生しないように気をつけて実装テストされますが、テストデータ自体の作成でのチェックは意外に忘れられがちです。
DBFluteクライアント/playsql配下の 20-member.xls にて、とある有効終了日が一つ後の有効期間の有効開始日を超える日 を指定して、ReplaceSchemaを実行してみましょう。 当然のことながら、正常に登録されてしまうはずです。
今度は、以下のSQLをDBFluteクライアント/playsql/take-finally.sql に記述して再実行してみましょう。ログに注目です。じっくり読んでみましょう。
e.g. 会員住所情報で同時点で住所が重複しないようにチェック @take-finally.sql
-- #df:assertListZero#
-- /- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-- Member addresses should be only one at any time.
-- - - - - - - - - - -/
select adr.MEMBER_ADDRESS_ID, adr.MEMBER_ID
, adr.VALID_BEGIN_DATE, adr.VALID_END_DATE
, adr.ADDRESS
from MEMBER_ADDRESS adr
where exists (select subadr.MEMBER_ADDRESS_ID
from MEMBER_ADDRESS subadr
where subadr.MEMBER_ID = adr.MEMBER_ID
and subadr.VALID_BEGIN_DATE > adr.VALID_BEGIN_DATE
and subadr.VALID_BEGIN_DATE < adr.VALID_END_DATE
)
;
さらにチェック
それでは、チェックするSQLを作成して、実際にチェックしてみましょう。
まず、そのチェックに引っ掛かるようなデータに修正してください。 そして、以下をアサートするSQLを書いて、take-finally.sql に登録し、ReplaceSchemaでチェックされることを確認しましょう。データを元に戻し、再度 ReplaceSchema を実行して正常なデータが入ったことを確信しましょう。
- 正式会員日時を持ってる仮会員がいないこと
- まだ生まれていない会員がいないこと
- 退会会員が退会情報を持っていることをアサート
ConditionBeanスタイルで
もし、よければ、(強いこだわりがなければ) ConditionBeanスタイルで書いてみましょう。 DBFluteユーザーが見慣れているフォーマットで書くことで、全体の可読性向上が期待できます。
e.g. "|" が左端と想定して、ConditionBeanスタイルの法則 @SQL
|select ...
| , ...
| from MEMBER
| left outer join ...
| inner join ...
| where ...
| and ...
| order by ...
おまけチェック
実は、先ほどの会員住所情報の重複チェックのSQLは、有効期間が一日だけ重複しているパターンを検出することはできません。 時間が余った場合は、このSQLをどのように直せばそのパターンが検出できるのか試してみましょう。 また、様々なパターンに挑戦してみるのも良いでしょう。
SQLのエリアス名のこだわり
なかなか議論が尽きないところではありますが、SQLのエリアス名の付け方、こういった考えをもっています。 これといった正解をだしづらいものですが、ぜひいちどお読みくださいませ。
次のセクション
さて、次のセクションへ