Asakusa FrameworkのOperator DSLのマスターつき更新演算子(@MasterJoinUpdate)のメモ。
|
マスターつき更新演算子は、レコードに合致するマスターレコードの値を使ってトランザクション(明細)レコードの内容を更新する演算子。
性能特性はJoin(旧ドキュメントではReduce、最適化によってはMap)。[/2016-02-11]
入力 ポート数 |
入力データモデル の制約 |
イメージ | 出力 ポート数 |
出力データモデル の制約 |
入力1レコード に対する 出力レコード数 |
||
---|---|---|---|---|---|---|---|
2 | master | (マスター) |
![]() |
2 | updated | txと同じ データモデル |
txの1レコードに対し updatedかmissedの どちらかに1レコード。 |
tx | missed | txと同じ データモデル |
トランザクション(明細データ)レコード(の指定したキー)に合致するマスターレコードが存在したら、そのトランザクションレコードの内容を更新してupdatedに出力する。
存在しなければmissedに出力する。
マスターつき更新演算子は、マスターデータの値によってトランザクションレコードの値を変更する為に使う。
マスターと結合した新しいデータモデルを出力したい場合はマスター結合演算子(@MasterJoin)を使う。
合致するマスターが存在するかどうかを確認するだけの場合はマスター確認演算子(@MasterCheck)を使う。
(マスターデータは出力しないが)結合したマスターデータの内容に応じて処理を分岐したい場合はマスター分岐演算子(@MasterBranch)を使う。
hoge_transactionに合致するhoge_masterから「master_name」項目を更新する例。
入力データ例 | 出力データ例 | ||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
master |
|
→ | updated |
|
|||||||||||||||||||||||||||||||||||||||||
tx |
|
→ | missed |
|
hoge_master = { id : TEXT; name : TEXT; }; hoge_transaction = { date : DATE; master_id : TEXT; master_name : TEXT; value : LONG; };
import com.asakusafw.vocabulary.model.Key; import com.asakusafw.vocabulary.operator.MasterJoinUpdate; import com.example.modelgen.dmdl.model.HogeMaster; import com.example.modelgen.dmdl.model.HogeTransaction;
public abstract class ExampleOperator { /** * HogeMasterのnameをHogeTransactionにセットする * * @param master * マスタデータ * @param tx * トランザクションデータ */ @MasterJoinUpdate public void updateWithMaster( @Key(group = "id") HogeMaster master, @Key(group = "master_id") HogeTransaction tx ) { tx.setMasterName(master.getName()); } }
第1引数でマスターとなるデータモデル、第2引数でトランザクション(明細)となるデータモデルを指定する。
各引数には@Keyアノテーションを付けて、結合キーを指定する。
複数の項目を結合キーとする場合は「@Key(group = { "key1", "key2" })
」のように波括弧でくくってカンマ区切りで指定する。
(MasterBranchとは異なり、)第1引数も第2引数も常にnull以外となる。[2015-03-22]
マスターが存在しない場合はmissedに出力されるので、自分がコーディングしたメソッドが呼ばれることは無い。
import com.example.modelgen.dmdl.model.HogeMaster; import com.example.modelgen.dmdl.model.HogeTransaction; import com.example.operator.ExampleOperatorFactory; import com.example.operator.ExampleOperatorFactory.UpdateWithMaster;
private final In<HogeMaster> master; private final In<HogeTransaction> detail; private final Out<HogeTransaction> out1; private final Out<HogeTransaction> out2;
@Override public void describe() { ExampleOperatorFactory operators = new ExampleOperatorFactory(); // マスターつき更新 UpdateWithMaster updateWithMaster = operators.updateWithMaster(this.master, this.detail); this.out1.add(exists.updated); this.out2.add(exists.missed); }
Flow DSLでは、自分が作ったOperatorのFactoryクラス(AsakusaFWのコンパイラーによって生成される)を使用する。
メソッド名はOperatorクラスに書いたメソッド名と同じ。
戻り値の型はAsakusaFWのコンパイラーによって生成されたクラス。(メソッド名を先頭が大文字のキャメルケースに変換したもの)
メソッドの引数は第1引数がマスターデータ、第2引数がトランザクション(明細)データ。
出力ポートの名前のデフォルトはupdatedとmissed。
出力ポート名を変えたい場合は@MasterJoinUpdateアノテーションで指定できる。
@MasterJoinUpdate(updatedPort = "updated", missedPort = "missed")
マスター更新演算子の単体テストの実装例。
package com.example.operator;
import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; import org.junit.Test; import com.example.modelgen.dmdl.model.HogeMaster; import com.example.modelgen.dmdl.model.HogeTransaction;
/** * {@link ExampleOperator}のテスト. */ public class ExampleOperatorTest { @Test public void updateWithMaster() { ExampleOperator operator = new ExampleOperatorImpl(); HogeMaster master = new HogeMaster(); master.setNameAsString("hishidama"); HogeTransaction tx = new HogeTransaction(); operator.updateWithMaster(master, tx); assertThat(tx.getMasterNameAsString(), is("hishidama")); } }
Operatorのテストクラスは、通常のJavaのJUnitのテストケースクラスとして作成する。
テスト対象のOperatorクラス自身は抽象クラスだが、Operatorクラス名の末尾に「Impl」の付いた具象クラスがAsakusaFWによって生成されるので、それを使う。
マスター分岐演算子は、マスター結合+更新+分割で同様の処理を行うことが出来る。
(分割演算子(@Split)は、結合モデルを結合元のデータモデルに戻す演算子)
つまり、後の処理で結合したデータを使うなら(分割の必要は無いので)、マスター結合+更新を使う。
マスターつき更新演算子は、SQLのJOINに似ている。
INSERT INTO updated (date, master_id, master_name, value)
SELECT tx.date, tx.master_id, master.name, tx.value FROM tx INNER JOIN master ON master.id = tx.master_id;