@DBFlute, Java, Buri
実務での実績もでてきたようなので、DBFluteのBuri連携について、
環境構築手順と実装方法についておさらいしておきます。
(Buri-1.0.0を基準とします)
気合いが入ってることが大前提です。
中途半端な気持ちではBuriを役立てることはできません。
必ず、DBFluteのBuri連携機能のExampleプロジェクト
「dbflute-buri-example」を参考にするようにして下さい。
#
# DBFluteのBuri連携の概要
#
Buriはステータス実装のための便利な機能を持っています。
その機能を有効利用するためにDBFluteがBuriと連携します。
ポイントは:
A. タイプセーフで安全実装
B. ディベロッパーBuriの内部構造を意識させない
「A」は言うまでもないですね。DBFluteのポリシーです。
Buri内部で例外が発生したりするととてもやっかいです。
極力トラブルが発生しないようにします。
「B」は
「ディベロッパーはBuriの内部構造を意識せずに、
純粋に業務的なステータス実装に集中する」
ということです。
便利な機能を使うためにその他の煩雑な知識が必要になっては
意味がありません。その代わりアーキテクトはできるだけトラブルが
ないように、環境構築やアプリへの微調整など頑張ることが大事です。
#
# 環境構築手順
#
前提条件:
o SeasarとBuriが動作するEclipseプロジェクトが整っていること
o XPDLができあがっていること
o DBスキーマはまだ作成されていない
1. DBユーザと空のDBスキーマを作成
2. EMechaで普通にDBFlute環境
3. buriDefinitionMap.dfpropの作成・設定
4. playsql配下にBuriの内部テーブルのDDL
5. playsql配下にDBFluteのBuri用VIEWのDDL
6. playsql配下に業務テーブルのDDL
7. ReplaceSchema実行
8. JDBC/Doc/Generate実行
9. Buri拡張の設定
<1. DBユーザと空のDBスキーマを作成>
まずはDB環境です。後の手順でReplaceSchemaを実行しますので、
とりあえずは「DBユーザ」と「空のDBスキーマ」を作成して下さい。
例えば、Oracleであれば以下のような感じで:
(dbflute-buri-exampleではOracleを利用しています)
create user buriexampledb identified by buriexampledb;
grant connect to buriexampledb;
grant resource to buriexampledb;
grant create view to buriexampledb;
grant create synonym to buriexampledb;
<2. EMechaで普通にDBFlute環境>
こちらは、DBFluteのドキュメント
「EMechaによるセットアップ」をご覧下さい。
<3. buriDefinitionMap.dfpropの作成・設定>
DBFluteクライアント/dfprop配下に「buriDefinitionMap.dfprop」
という名前のファイルを配置します。手動で作成するよりかは、
dbflute-buri-exampleのburiDefinitionMap.dfpropを
コピーして持って来るようにして下さい。
activityDefinitionMapにXPDL上のアクティビティを定義します。
「status」にはステータスになりえるアクティビティを定義します。
基本的にはXPDL上のFinishModeがManualのアクティビティがそれです。
「action」にはそのプロセスで利用する全てのアクションを定義します。
tableProcessMapに実際の業務テーブルとプロセスとの関連を定義します。
一つのテーブルに複数のプロセスが関連付けられるようになっています。
一つのテーブルに一つのプロセスであれば、
list:{}の中に一つだけプロセスを指定して下さい。
ex) DOCUMENT = list:{ 文章管理.文書公開 }
map:{
; activityDefinitionMap = map:{
; 文書管理 = map:{
; 文書公開 = map:{
; status = list:{レビュー待ち ; 差し戻し ; 公開待ち ; 公開中}
; action = list:{next ; update ; delete ; 問題なし ; 手直し必要}
}
}
}
; tableProcessMap = map:{
; DOCUMENT = list:{ 文書管理.文書公開 }
}
}
<4. playsql配下にBuriの内部テーブルのDDL>
Buriの内部テーブルのDDLを配置します。
DBFluteクライアント/playsql配下
「replace-schema-01-buri-internal.sql」
Oracleであれば、dbflute-buri-exampleの
replace-schema-01-buri-internal.sqlをそのまま
持って来てもOKです。
<5. playsql配下にDBFluteのBuri用VIEWのDDL>
DBFluteが利用するBuriの内部テーブルを参照するためのVIEWの
DDLを配置します。
DBFluteクライアント/playsql配下
「replace-schema-02-dbflute-buri.sql」
どのDBを使っていようが、dbflute-buri-exampleの
replace-schema-02-dbflute-buri.sqlをそのまま
持って来て下さい。そして、Oracleでないなら、
VIEWのSQLの「LOCALTIMESTAMP」の部分を利用するDBに
合わせて適切に調節して下さい。(INDEXの利用対象となるか確認)
この件は、こちらの記事が参考になります。
http://d.hatena.ne.jp/taktos/20090616/1245150534
<6. playsql配下に業務テーブルのDDL>
業務テーブルのDDLを配置します。
DBFluteクライアント/playsql配下
「replace-schema-03-basic.sql」
<7. ReplaceSchema実行>
Buriの内部テーブルのDDL、DBFluteのBuri用VIEWのDDL、
業務テーブルのDDLを実行するためにReplaceSchemaタスクを実行します。
業務テーブルにマスタデータやテストデータを登録する場合は、
実行前にDBFluteクライアント/playsql/data配下に準備したデータを
配置するようにして下さい。
<8. JDBC/Doc/Generate実行>
JDBCタスク、Docタスク、Generateタスクを実行します。
DBFluteのBuri連携のためのクラスが生成されます。
allcommon.plugin.buriパッケージ配下のクラスがそれです。
BasicBuriUserDataProvider:
BuriUserDataProviderの基本実装(AccessContext利用)
{buri-user.dicon定義対象}
BasicParticipantProvider:
BuriUserDataProviderの基本実装(AccessContext利用)
{buri-user.dicon定義対象}
BehaviorToDataAccessRule:
BuriとDBFluteのアダプタ
{buri-share.dicon定義対象(Buri-1.0.1よりburi-user.diconでOK)}
BuriDef:
Processの定義
(buriDefinitionMap.dfpropで定義したプロセスのENUMの集まり)
FixedBuriDataAccessScriptFactoryImpl:
Long型以外のPKを利用可能にする
{buri-extension.dicon定義対象}
FixedScriptDataAccessUtilKeyImpl:
Long型以外のPKを利用可能にする
{FixedBuriDataAccessScriptFactoryImplでnewされる}
<9. Buri拡張の設定>
Buriの設定ファイルに指定する必要のあるものは以下の四つです:
o BehaviorToDataAccessRule
o FixedBuriDataAccessScriptFactoryImpl
o BasicBuriUserDataProvider
o BasicParticipantProvider
BehaviorToDataAccessRuleは、buri-share.diconに指定します。
buri-share.diconはBuri本体からコピーしてきて修正します。
dbflute-buri-exampleのburi-share.diconを
参考に配置・修正して下さい。
(Buri-1.0.1以降の場合は、buri-user.diconで指定できるようです)
FixedBuriDataAccessScriptFactoryImplは、
buri-extension.diconに指定します。
buri-extension.diconはBuri本体からコピーしてきて修正します。
dbflute-buri-exampleのburi-extension.diconを
参考に配置・修正して下さい。
BasicBuriUserDataProviderおよび
BasicParticipantProviderは、buri-user.diconに指定します。
dbflute-buri-exampleのburi-user.diconを
参考に配置・修正して下さい。
また、DBFluteの共通カラムの自動設定で利用するAccessContextの
設定をして下さい。DBFluteの共通カラムのページを参考に。
これは、ステータス更新ユーザ(の履歴)を保持するための設定です。
AccessContextとBuriのステータス更新を関連付けて保持します。
業務的に「誰がこのステータスを変更したのか」を保持することは
ほとんどの場合において必須かと思われます。なので、ここでは
この機能を利用することを前提として説明をしています。
Buriの権限機能の仕組みを利用して実現しています。
権限チェックを行いたい場合は、このあたりを独自に拡張して下さい。
dbflute-buri-exampleが利用しているBuriのバージョンと
実際に利用しようとしているBuriのバージョンが同じであれば、
Exampleのdiconを持って来て、パッケージ名の調整などをする
だけでもOKです。
#
# 実装方法
#
DBFluteのBuri連携の大きな特徴は以下のとおりです:
A. タイプセーフなtoNextStatus()
B. 指定されたステータスによる絞り込み条件での検索
C. Buriが保持する情報へのスムーズなアクセス
<A. タイプセーフなtoNextStatus()>
Buriのプロセスに関連付けられた業務テーブルのBehaviorに
toNextStatus_[プロセス]()メソッドが生成されています。
プロセスの指定はメソッドで解決されタイプセーフです。
アクションの指定はENUMによるタイプセーフです。
_documentBhv.toNextStatus_文書管理_文書公開(document, BuriDef.文書管理_文書公開_Action.Next);
<B. 指定されたステータスによる絞り込み条件での検索>
Buriのプロセスに関連付けられた業務テーブルのBehaviorに
getEntities()メソッドが生成されています。
ステータスを指定して該当する業務テーブルを検索できます。
ステータスの指定はENUMによるタイプセーフです。
List<Document> releaseWaitingList = _documentBhv.getEntities(
new ConditionBeanSetupper<DocumentCB>() {
public void setup(DocumentCB cb) {
cb.query().addOrderBy_Id_Desc();
}
}, BuriDef.文書管理_文書公開_Status.公開待ち);
検索された業務テーブルのEntityリストは、
Buriが保持する情報を取得するためのVIEWのEntityを保持しています。
(内部的にsetupSelect_[ViewEntity]をしています)
「Buriが保持する情報へのスムーズなアクセス」にて説明します。
第二引数(可変長引数)を指定しなかった場合は、
ステータス条件無しの検索となります。
<C. Buriが保持する情報へのスムーズなアクセス>
getEntities()で検索されたEntityから以下の情報が取得できます:
o 現在ステータス(のENUM)
o Buri内部で管理している情報を取得するVIEWのEntity
List<Document> releaseWaitingList = _documentBhv.getEntities(
new ConditionBeanSetupper<DocumentCB>() {
public void setup(DocumentCB cb) {
cb.query().addOrderBy_Id_Desc();
}
}, BuriDef.文書管理_文書公開_Status.公開待ち);
for (Document document : releaseWaitingList) {
... = document.getStatus_文書管理_文書公開();
BuriAllRoundState state
= document.getBuriAllRoundState_文書管理_文書公開();
... = state.getStatusUpdateDatetime();
... = state.getStatusUpdateUser();
}
さらにステータス更新履歴のVIEWをLoadReferrerで
取得することができます。
Document document = ...;
_documentBhv.loadBuriAllRoundStateHistory_文書管理_文書公開List(
document
, new ConditionBeanSetupper<BuriAllRoundStateHistoryCB>() {
public void setup(BuriAllRoundStateHistoryCB cb) {
cb.query().addOrderBy_StatusUpdateDatetime_Asc();
}
}
);
List<BuriAllRoundStateHistory> historyList
= document.getBuriAllRoundStateHistory_文書管理_文書公開List();
for (BuriAllRoundStateHistory history : historyList) {
String statusPathName = history.getStatusPathName();
文書管理_文書公開_Status status
= BuriDef.文書管理_文書公開_Status.codeOf(statusPathName);
... = history.getStatusUpdateDatetime();
... = history.getStatusUpdateUser();
}
#
# 番外編
#
履歴に対してExistsSubQueryができます。
「とあるステータスになったことがある」という条件で
絞り込みができます。
このときのステータスの指定もタイプセーフです。
DocumentCB cb = ...;
cb.query().existsBuriAllRoundStateHistory_文書管理_文書公開(
new SubQuery<BuriAllRoundStateHistoryCB>() {
public void query(BuriAllRoundStateHistoryCB subCB) {
subCB.query().setStatus_Equal(BuriDef.文書管理_文書公開_Status.レビュー待ち);
}
});
DBFluteが提供するViewはカスタマイズが可能です。
基本構造を崩さなければ、カラムを追加などはアプリの自由です。
#
# おまけ
#
履歴に関してですが「ステータス変更時のコメント」のような
業務的に結構重要な値を保持することができません。
なので、結局アプリ側で独自の履歴テーブルを用意して、
画面でステータス変更履歴を表示するときにBuriの履歴と
一緒に合わせてどうのこうのってやらなければなりません。
「ステータス変更履歴はBuriに任せることができる」って
いうのが無いとBuri利用の説得力が半減するので、
DBFluteで拡張してどうにかできないかなぁと考えたり...
(でもさすがに簡単な拡張じゃないので無理かも)
インターフェース的には、toNextStatus()に引数を増やして、
コメントを一緒に(内部的に)登録できるようにできるといいかな。
コメントに限らず設定次第で独自の値を第三引数で指定できると
一番良いのかも。。。