データ履歴管理のための
テンポラルデータモデルと
Reladomoの紹介
株式会社FOLIO 伊藤博志
JJUG CCC 2017 Spring 2017.5.20
#ccc_g3
Reladomo is an open source software Licensed under Apache 2.0 License,
Copyright 2016 Goldman Sachs, Its name may be a trademark of its owner.
1
株式会社FOLIOの紹介 hashtag: #ccc_g3
https://www.wantedly.com/companies/folio/projectsエンジニア募集中!
2
Agenda
1. 自己紹介
2. RDBMSで履歴データを扱う
 スナップショットデータモデル
 トランザクション時間データモデル
 有効時間データモデル
 バイテンポラルデータモデル
3. Javaからバイテンポラルモデルを容易に扱うReladomoの紹介
hashtag: #ccc_g3
3
自己紹介
趣味:ドラム演奏
JavaOneコミュニティバンド
Null Pointersで演奏経験あり(日本人初)
Tech Lead @ FOLIO
伊藤 博志
Eclipse Collections:共同プロジェクトリード兼コミッター
Reladomo:コントリビューター
OpenJDK:コントリビューター
JJUG CCC、Java Day Tokyo、JavaOne San Francisco登壇
2017年5月17日に株式会社FOLIO入社。
hashtag: #ccc_g3
4
RDBMSで変更履歴をどう扱うか
hashtag: #ccc_g3
5
RDBMSで履歴データを扱う
単純なシナリオを考えてみましょう
シンプルな人事システム。扱う情報は姓名のみ。
 4月1日に「鈴木花子」さん入社。3月1日にシステムに登録
 6月6日に結婚し、姓が「斎藤」に変更。同日システム上に
間違えて「斉藤」と登録
 6月8日にシステム上で修正(斉藤=>斎藤) 。事実としては
6月6日づけで直したい
 12月1日、退職にともない同日にシステム上で情報を無効化
hashtag: #ccc_g3
6
RDBMSで履歴データを扱う
 シナリオをよく観察すると、2種類の履歴が存在
hashtag: #ccc_g3
3/1 4/1 6/6 6/8 12/1
鈴木花子
事実情報の
履歴
システムに
反映された
履歴
入社🎉
「鈴木花子」を
システムに登録
結婚して
斎藤になる👰
名字を
「斉藤」に変更
名字を
「斎藤」に変更
退職†
データを無効化
7
RDBMSで履歴データを扱う
最新の情報さえ
分かれば良い
いつシステムに
反映されたか履
歴が知りたい
2種類の履歴から考えうる4種類の要件
事実情報の履歴
が知りたい
事実情報と、い
つシステムに反
映されたか両方
の履歴が知りた
い
hashtag: #ccc_g3
8
RDBMSで履歴データを扱う
スナップショットデータモデル
トランザクション時間データモデル
有効時間データモデル
バイテンポラルデータモデル
各要件を満たすために、4つのデータモデルが考えられる
hashtag: #ccc_g3
9
スナップショットデータモデル
hashtag: #ccc_g3
最新の情報さえ
分かれば良い
10
スナップショットデータモデル
 スナップショットデータモデルで表現できるのは、最新
の情報のみ(履歴は表現できない)
hashtag: #ccc_g3
3/1 4/1 6/6 6/8 12/1
鈴木花子
事実情報の
履歴
システムに
反映された
履歴
入社🎉
「鈴木花子」を
システムに登録
結婚して
斎藤になる👰
名字を
「斉藤」に変更
名字を
「斎藤」に変更
退職†
データを無効化
11
スナップショットデータモデル
姓 名 inserted_at updated_at
鈴木 花子 2017/3/1 2017/3/1
 4月1日に「鈴木花子」さん入社。3月1日にシステムに登録
鈴木さん入社!
入社日というカラムが
あってもいいような気がする
hashtag: #ccc_g3
12
スナップショットデータモデル
 6月6日に結婚し、姓が「斎藤」に変更。同日システム
上に間違えて「斉藤」と登録
間違えて「斉藤」と入
力してしまいました
姓 名 inserted_at updated_at
斉藤 花子 2017/3/1 2017/6/6
hashtag: #ccc_g3
13
スナップショットデータモデル
 6月8日にシステム上で修正(斉藤=>斎藤)
正しい姓は「斎藤」さ
んですね。。。
姓 名 inserted_at updated_at
斎藤 花子 2017/3/1 2017/6/8
hashtag: #ccc_g3
14
スナップショットデータモデル
 12月1日、退職にともない同日に人事情報をシステム上
で無効化
_人人人人人人人人人人人_
> 物理削除 <
 ̄Y^Y^Y^Y^Y^Y^Y^Y^Y^Y ̄
物理削除がイヤ
なら禁断の論理
削除フラグとい
う手もあるよ。
フフッ
姓 名 inserted_at updated_at
hashtag: #ccc_g3
15
スナップショットデータモデル
 スナップショットデータモデルで履歴を実装しようと
すると、バージョンカラムや履歴テーブルを別に用意
するなど、力技が求められる
 世に存在する履歴系のデータは、この力技で実装され
ていることが多い(印象)
 さらにスナップショットデータモデルでは、データを
無効化するのに「論理削除」か「物理削除」か、と悩
むケースも
hashtag: #ccc_g3
16
トランザクション時間データモデル
hashtag: #ccc_g3
いつシステムに
反映されたか履
歴が知りたい
17
トランザクション時間データモデル
 トランザクション時間データモデルで表現できるのは、
「システムに反映された」履歴
hashtag: #ccc_g3
3/1 4/1 6/6 6/8 12/1
鈴木花子
事実情報の
履歴
システムに
反映された
履歴
入社🎉
「鈴木花子」を
システムに登録
結婚して
斎藤になる👰
名字を
「斉藤」に変更
名字を
「斎藤」に変更
退職†
データを無効化
18
トランザクション時間データモデル
姓 名 IN OUT
鈴木 花子 2017/3/1 9999/12/1
鈴木さん入社!
INはデータベースに挿入された日付、
OUTはその行が無効になった(なる)日付、
OUTはデフォルトでINFINITY(この場合9999年)とするよ
やっぱり入社日カラムが欲しくなる。。
 4月1日に「鈴木花子」さん入社。3月1日にシステムに登録
hashtag: #ccc_g3
19
トランザクション時間データモデル
姓 名 IN OUT
鈴木 花子 2017/3/1 2017/6/6
斉藤 花子 2017/6/6 9999/12/1
鈴木さん姓が2017/6/6に無効(OUT)
になり、斉藤さん姓が同日入力(IN)
されたということだね
 6月6日に結婚し、姓が「斎藤」に変更。同日システム
上に間違えて「斉藤」と登録
hashtag: #ccc_g3
20
トランザクション時間データモデル
姓 名 IN OUT
鈴木 花子 2017/3/1 2017/6/6
斉藤 花子 2017/6/6 2017/6/8
斎藤 花子 2017/6/8 9999/12/1
斉藤さん姓が2017/6/8に無効(OUT)
になり、斎藤さん姓が同日入力(IN)
されたということだね
 6月8日にシステム上で修正(斉藤=>斎藤) 。事実として
は6月6日づけで直したい
「斎藤」姓がシステム上有効なのは6/8
からか。。
hashtag: #ccc_g3
21
トランザクション時間データモデル
姓 名 IN OUT
鈴木 花子 2017/3/1 2017/6/6
斉藤 花子 2017/6/6 2017/6/8
斎藤 花子 2017/6/8 2017/12/1
2017/12/1日付で無効化(OUT)!
 12月1日、退職にともない同日に人事情報をシステム上
で無効化
hashtag: #ccc_g3
22
トランザクション時間データモデル
履歴データをどう検索するのか?
hashtag: #ccc_g3
23
トランザクション時間データモデル
 任意の時間Xにデータベース上で最新だった情報を
とってくる
select * from EMPLOYEE
where IN < X
and OUT >= X
hashtag: #ccc_g3
24
トランザクション時間データモデル
姓 名 IN OUT
鈴木 花子 2017/3/1 2017/6/6
 3月15日時点でデータベース上で最新だった情報を
とってくる
select * from EMPLOYEE
where IN < “2017/3/15”
and OUT >= “2017/3/15”
姓 名 IN OUT
鈴木 花子 2017/3/1 2017/6/6
斉藤 花子 2017/6/6 2017/6/8
斎藤 花子 2017/6/8 2017/12/1
hashtag: #ccc_g3
25
トランザクション時間データモデル
姓 名 IN OUT
斉藤 花子 2017/6/6 2017/6/8
select * from EMPLOYEE
where IN < “2017/6/7”
and OUT >= “2017/6/7”
姓 名 IN OUT
鈴木 花子 2017/3/1 2017/6/6
斉藤 花子 2017/6/6 2017/6/8
斎藤 花子 2017/6/8 2017/12/1
 6月7日時点でデータベース上で最新だった情報を
とってくる
hashtag: #ccc_g3
26
トランザクション時間データモデル
姓 名 IN OUT
斎藤 花子 2017/6/8 2017/12/1
姓 名 IN OUT
鈴木 花子 2017/3/1 2017/6/6
斉藤 花子 2017/6/6 2017/6/8
斎藤 花子 2017/6/8 2017/12/1
 6月9日時点でデータベース上で最新だった情報を
とってくる
select * from EMPLOYEE
where IN < “2017/6/9"
and OUT >= “2017/6/9”
hashtag: #ccc_g3
27
トランザクション時間データモデル
姓 名 IN OUT
select * from EMPLOYEE
where IN < “2017/12/5"
and OUT >= “2017/12/5”
姓 名 IN OUT
鈴木 花子 2017/3/1 2017/6/6
斉藤 花子 2017/6/6 2017/6/8
斎藤 花子 2017/6/8 2017/12/1
2017/12/1以降に有効な行が存在しな
いので、論理削除フラグを使わなく
てもシステム上で無効化されてい
る!
 12月5日時点でデータベース上で最新だった情報を
とってくる
hashtag: #ccc_g3
28
トランザクション時間データモデル
 ある時点でデータベース上で最新だった情報を一意に
取得することができる
 過去の履歴を一気に取得することもでき、時間軸で
ソートも可
 最新の行を無効化するにはOUTコラムを更新するだけ
でOK(物理削除も論理削除フラグも不要)
 監査情報を自然な形でモデリングできる
トランザクション時間データモデルの適用例
 ドキュメントの履歴管理
 状態遷移の履歴管理
 etc.
hashtag: #ccc_g3
29
有効時間データモデル
hashtag: #ccc_g3
事実情報の履歴
がほしい
30
有効時間データモデル
 有効時間データモデルで表現できるのは、「事実情報」
の履歴
hashtag: #ccc_g3
3/1 4/1 6/6 6/8 12/1
鈴木花子
事実情報の
履歴
システムに
反映された
履歴
入社🎉
「鈴木花子」を
システムに登録
結婚して
斎藤になる👰
名字を
「斉藤」に変更
名字を
「斎藤」に変更
退職†
データを無効化
31
有効時間データモデル
姓 名 FROM THRU
鈴木 花子 2017/4/1 9999/12/1
 4月1日に「鈴木花子」さん入社。3月1日にシステムに登録
鈴木さん入社!
FROMは当該データが事実として有効である最初の日、
THRUは当該データが事実として有効である最終日の翌日*、
THRUはデフォルトでINFINITY(この場合9999年)とするよ
これで入社日がわかる。。
*ここではTHRUを最終日翌日としていますが、FROMを最初の日の前日とする方法もあります
hashtag: #ccc_g3
*Throughの略
32
有効時間データモデル
姓 名 FROM THRU
鈴木 花子 2017/4/1 2017/6/6
斉藤 花子 2017/6/6 9999/12/1
 6月6日に結婚し、姓が「斎藤」に変更。同日システム
上に間違えて「斉藤」と登録
事実として「鈴木」姓の最終日が2017/6/5
(THRU-1)になり、2017/6/6(FROM)から
間違った「斉藤」姓が有効に
hashtag: #ccc_g3
33
有効時間データモデル
姓 名 FROM THRU
鈴木 花子 2017/4/1 2017/6/6
斎藤 花子 2017/6/6 9999/12/1
2017/6/6(FROM)から正しい「斎藤」さん
姓になった。間違えた「斉藤」姓は上書きさ
れたので、システム上の履歴はわからないね。
 6月8日にシステム上で修正(斉藤=>斎藤) 。事実として
は6月6日づけで直したい
hashtag: #ccc_g3
34
有効時間データモデル
姓 名 FROM THRU
鈴木 花子 2017/4/1 2017/6/6
斎藤 花子 2017/6/6 2017/12/1
 12月1日、退職にともない同日に人事情報をシステム上
で無効化
これで
鈴木さんが2017/4/1に入社、
2017/6/6に斎藤姓に代わり、
2017/12/1に退社した、
という事実情報がすべて表現されるね!
hashtag: #ccc_g3
35
有効時間データモデル
検索方法はトランザクション時間
データモデルの場合と同様なので省略
hashtag: #ccc_g3
36
有効時間データモデル
 ある時点での「事実情報」を一意に取得することがで
きる
 過去の事実の履歴を一気に取得することもでき、時間
軸でソートも可
 最新の行を無効化するにはTHRUコラムを更新するだ
けでOK(物理削除も論理削除フラグも不要)
 事実の履歴を自然な形でモデリングできる
有効時間データモデルの適用例
 変更履歴を気にしないビジネスデータ
 etc.
hashtag: #ccc_g3
37
バイテンポラルデータモデル
hashtag: #ccc_g3
事実情報と、い
つシステムに反
映されたか両方
の履歴が知りた
い
38
バイテンポラルデータモデル
 バイテンポラルデータモデルで表現できるのは、「シス
テムに反映された」履歴と「事実情報」の履歴両方
hashtag: #ccc_g3
3/1 4/1 6/6 6/8 12/1
鈴木花子
事実情報の
履歴
システムに
反映された
履歴
入社🎉
「鈴木花子」を
システムに登録
結婚して
斎藤になる👰
名字を
「斉藤」に変更
名字を
「斎藤」に変更
退職†
データを無効化
39
バイテンポラルデータモデル
姓 名 FROM THRU IN OUT
鈴木 花子 2017/4/1 9999/12/1 2017/3/1 9999/12/1
 4月1日に「鈴木花子」さん入社。3月1日にシステムに登録
鈴木さん入社!
4月1日に入社したことも
わかるし、3月1日にシス
テムに反映されたこともわ
かる!
hashtag: #ccc_g3
40
バイテンポラルデータモデル
姓 名 FROM THRU IN OUT
鈴木 花子 2017/4/1 9999/12/1 2017/3/1 2017/6/6
鈴木 花子 2017/4/1 2017/6/6 2017/6/6 9999/12/1
斉藤 花子 2017/6/6 9999/12/1 2017/6/6 9999/12/1
 6月6日に結婚し、姓が「斎藤」に変更。同日システム
上に間違えて「斉藤」と登録
最初の行が「システム上」無効になり、新し
い「事実」が2つの期間にわたって挿入されて
いるね。
hashtag: #ccc_g3
41
バイテンポラルデータモデル
姓 名 FROM THRU IN OUT
鈴木 花子 2017/4/1 9999/12/1 2017/3/1 2017/6/6
鈴木 花子 2017/4/1 2017/6/6 2017/6/6 9999/12/1
斉藤 花子 2017/6/6 9999/12/1 2017/6/6 2017/6/8
斎藤 花子 2017/6/6 9999/12/1 2017/6/8 9999/12/1
 名字の漢字が間違っており、6月8日にシステム上で修正
(斉藤=>斎藤)
間違えた行が「システム上」無効になり、正しい
「事実」が挿入されているね。
3つ目の行は「斉藤」という間違った事実が
「システム上有効だった」期間が6/6から6/8の
間存在するという情報を表しているよ。
hashtag: #ccc_g3
42
バイテンポラルデータモデル
姓 名 FROM THRU IN OUT
鈴木 花子 2017/4/1 9999/12/1 2017/3/1 2017/6/6
鈴木 花子 2017/4/1 2017/6/6 2017/6/6 9999/12/1
斉藤 花子 2017/6/6 9999/12/1 2017/6/6 2017/6/8
斎藤 花子 2017/6/6 9999/12/1 2017/6/8 2017/12/1
斎藤 花子 2017/6/6 2017/12/1 2017/12/1 9999/12/1
 12月1日、退職にともない同日に人事情報をシステム上
で無効化
無事、事実情報(入社、姓変更、退社)と変
更履歴がすべて記録されました!
hashtag: #ccc_g3
43
バイテンポラルデータモデル
 バイテンポラルデータモデルは「システム履歴」と
「事実情報の履歴」を同時に正しく表現することがで
きる非常に強力なモデル
バイテンポラルデータモデルの適用例
 監査履歴が重要な金融システムの基幹データ
 その他、時系列に紐づくビジネスデータで変更履歴が
重要なユースケース
 etc.
hashtag: #ccc_g3
44
どう実装する?
バイテンポラルデータモデル
の雰囲気はわかった
でも、ぱっと見複雑そう。
結局、実装するのが大変
なのでは。。。?
安心してください
Reladomoを使えば簡単です!
hashtag: #ccc_g3
45
Javaからバイテンポラルモデルを
容易に扱えるReladomoの紹介
hashtag: #ccc_g3
46
Reladomoとは
https://github.com/goldmansachs/reladomo
 ゴールドマン・サックス社が2016年10月にGitHubに
OSSとして公開したJava ORMフレームワーク
 Apache License 2.0
 バイテンポラルデータモデルをネイティブサポート
 強力に型付けられたクエリー言語
 シャーディングのネイティブサポート
 ユニットテストのフルサポート
 etc.
hashtag: #ccc_g3
47
Reladomoとは
Reladomoのコード生成
 xmlによるエンティティ定義を用いてコード生成
 同xmlからddlの生成も可能
 Abstract Classはxmlに変更のある都度生成される
 Concrete Classは初回のみ生成しVCSにコミット、ビ
ジネスロジックを記述。コード生成で上書きされない。
hashtag: #ccc_g3
48
Employeeエンティティ定義ファイル hashtag: #ccc_g3
<MithraObject objectType="transactional"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="reladomoobject.xsd">
<PackageName>sample.domain</PackageName>
<ClassName>Employee</ClassName>
<DefaultTable>EMPLOYEE</DefaultTable>
<Attribute name="employeeId" javaType="int" columnName="EMPLOYEE_ID" primaryKey="true"
primaryKeyGeneratorStrategy="SimulatedSequence">
<SimulatedSequence sequenceName="Employee"
sequenceObjectFactoryName="sample.util.ObjectSequenceObjectFactory"
hasSourceAttribute="false"
batchSize="1"
initialValue="1"
incrementSize="1"/>
</Attribute>
<Attribute name="firstName" javaType="String" columnName="FIRST_NAME" nullable="false" maxLength="64"/>
<Attribute name="lastName" javaType="String" columnName="LAST_NAME" nullable="false" maxLength="64"/>
</MithraObject>
49
Employeeエンティティ hashtag: #ccc_g3
トランザクション時間
生成されたAbstract Class
50
Employeeエンティティ hashtag: #ccc_g3
Concrete Class (初回のみ生成)
さまざまなビジネスロジックを記述できる
51
DBトランザクション hashtag: #ccc_g3
テンポラルオブジェクトの挿入、更新はトランザクション内で
のみ可能
(以降のコード例では省略している場合も)
Employee hanako =
MithraManagerProvider.getMithraManager().executeTransactionalCommand(tx -> {
Employee hanakoNew = new Employee();
hanakoNew.setLastName(“鈴木”);
hanakoNew.setFirstName(“花子”);
hanakoNew.insert();
return hanakoNew; //トランザクション終了後に使用したいオブジェクトを返り値とし
て与えることができる。
});
52
ReladomoのFinder DSL
 型付けられた強力なクエリー言語
 自動生成されたフィールドを用いてOperationを構築
hashtag: #ccc_g3
Operation op = EmployeeFinder.lastName().eq("鈴木")
.and(EmployeeFinder.firstName().eq("花子"));
final Employee hanako = EmployeeFinder.findOne(op);
53
Reladomoでトランザクション時間
を扱う例
hashtag: #ccc_g3
54
Employeeエンティティ定義ファイル hashtag: #ccc_g3
<MithraObject objectType="transactional"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="reladomoobject.xsd">
<PackageName>sample.domain</PackageName>
<ClassName>Employee</ClassName>
<DefaultTable>EMPLOYEE</DefaultTable>
<AsOfAttribute name="processingDate" fromColumnName="IN_Z" toColumnName="OUT_Z"
toIsInclusive="false"
isProcessingDate="true"
infinityDate="[com.gs.fw.common.mithra.util.DefaultInfinityTimestamp.getDefaultInfinity()]"
defaultIfNotSpecified="[com.gs.fw.common.mithra.util.DefaultInfinityTimestamp.getDefaultInfinity()]"
/>
<Attribute name="employeeId" javaType="int" columnName="EMPLOYEE_ID" primaryKey="true"
primaryKeyGeneratorStrategy="SimulatedSequence">
<SimulatedSequence sequenceName="Employee"
sequenceObjectFactoryName="sample.util.ObjectSequenceObjectFactory"
hasSourceAttribute="false"
batchSize="1"
initialValue="1"
incrementSize="1"/>
</Attribute>
<Attribute name="firstName" javaType="String" columnName="FIRST_NAME" nullable="false" maxLength="64"/>
<Attribute name="lastName" javaType="String" columnName="LAST_NAME" nullable="false" maxLength="64"/>
</MithraObject>
トランザクション時間
55
トランザクション時間データモデル
姓 名 IN OUT
鈴木 花子 2017/3/1 9999/12/1
hashtag: #ccc_g3
//3月1日にこのコードが実行される
Employee hanakoNew = new Employee(); //メモリ上にオブジェクト作成
hanakoNew.setLastName("鈴木");
hanakoNew.setFirstName("花子");
hanakoNew.insert(); // データベースに挿入
 4月1日に「鈴木花子」さん入社。3月1日にシステムに登録
56
トランザクション時間データモデル
姓 名 IN OUT
鈴木 花子 2017/3/1 2017/6/6
斉藤 花子 2017/6/6 9999/12/1
 6月6日に結婚し、姓が「斎藤」に変更。同日システム
上に間違えて「斉藤」と登録
hashtag: #ccc_g3
//6月6日にこのコードが実行される
hanako.setLastName("斉藤"); // アップデート
57
トランザクション時間データモデル
姓 名 IN OUT
鈴木 花子 2017/3/1 2017/6/6
斉藤 花子 2017/6/6 2017/6/8
斎藤 花子 2017/6/8 9999/12/1
 6月8日にシステム上で修正(斉藤=>斎藤) 。事実として
は6月6日づけで直したい
hashtag: #ccc_g3
//6月8日にこのコードが実行される
hanako.setLastName(“斎藤"); // アップデート
58
トランザクション時間データモデル
姓 名 IN OUT
鈴木 花子 2017/3/1 2017/6/6
斉藤 花子 2017/6/6 2017/6/8
斎藤 花子 2017/6/8 2017/12/1
 12月1日、退職にともない同日に人事情報をシステム上
で無効化
hashtag: #ccc_g3
//12月1日にこのコードが実行される
hanako.terminate(); // OUT = NOW
59
トランザクション時間データモデル hashtag: #ccc_g3
Timestamp mar5 = Timestamp.valueOf(LocalDateTime.of(2017, 3, 5, 0, 0));
Operation op = EmployeeFinder.firstName().eq("花子")
.and(EmployeeFinder.processingDate().eq(mar5));
Employee hanakoMar5 = EmployeeFinder.findOne(op);
logger.info("Mar 5: " + hanakoMar5.getFullName());
 3月5日時点の花子さんを検索
[main] INFO sample.HelloReladomoApp - Mar 5: 鈴木 花子
select t0.EMPLOYEE_ID,t0.FIRST_NAME,t0.LAST_NAME,t0.IN,t0.OUT
from EMPLOYEE t0
where t0.FIRST_NAME = '花子'
and t0.IN <= '2017-03-05 00:00:00.000'
and t0.OUT > '2017-03-05 00:00:00.000'
60
トランザクション時間データモデル hashtag: #ccc_g3
Timestamp jun7 = Timestamp.valueOf(LocalDateTime.of(2017, 6, 7, 0, 0));
Operation op = EmployeeFinder.firstName().eq("花子")
.and(EmployeeFinder.processingDate().eq(jun7));
Employee hanakoJum7 = EmployeeFinder.findOne(op);
logger.info(”Jun 7: " + hanakoJun7.getFullName());
 6月7日時点の花子さんを検索
[main] INFO sample.HelloReladomoApp - June 7: 斉藤 花子
select t0.EMPLOYEE_ID,t0.FIRST_NAME,t0.LAST_NAME,t0.IN,t0.OUT
from EMPLOYEE t0
where t0.FIRST_NAME = '花子'
and t0.IN <= '2017-06-07 00:00:00.000'
and t0.OUT > '2017-06-07 00:00:00.000'
61
トランザクション時間データモデル hashtag: #ccc_g3
Timestamp jun8 = Timestamp.valueOf(LocalDateTime.of(2017, 6, 8, 0, 0));
Operation op = EmployeeFinder.firstName().eq("花子")
.and(EmployeeFinder.processingDate().eq(jun8));
Employee hanakoJum8 = EmployeeFinder.findOne(op);
logger.info(”Jun 8: " + hanakoJun8.getFullName());
 6月8日時点の花子さんを検索
[main] INFO sample.HelloReladomoApp - June 8: 斎藤 花子
select t0.EMPLOYEE_ID,t0.FIRST_NAME,t0.LAST_NAME,t0.IN,t0.OUT
from EMPLOYEE t0
where t0.FIRST_NAME = '花子'
and t0.IN <= '2017-06-08 00:00:00.000'
and t0.OUT > '2017-06-08 00:00:00.000'
62
トランザクション時間データモデル
Reladomoのトランザクション時間データ操作
 IN/OUTカラムにまつわる処理はユーザーが気にするこ
となくフレームワークが吸収してくれる
 デフォルトで最新のデータが検索される(OUT =
INFINITY)
 データをある日付(トランザクション時間)から無効
化するにはterminate()を使う
 過去の履歴の検索の際はprocessingDate()を明示的に
指定してやる
hashtag: #ccc_g3
63
Reladomoでバイテンポラルデータ
モデルを扱う例
hashtag: #ccc_g3
64
<MithraObject objectType=“transactional”
xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance”
xsi:noNamespaceSchemaLocation=“reladomoobject.xsd”>
<PackageName>sample.domain</PackageName>
<ClassName>Employee</ClassName>
<DefaultTable>EMPLOYEE</DefaultTable>
<AsOfAttribute name=“processingDate” fromColumnName=“IN_Z” toColumnName=“OUT_Z”
toIsInclusive=“false”
isProcessingDate=“true”
timezoneConversion=“none”
infinityDate=“[com.gs.fw.common.mithra.util.DefaultInfinityTimestamp.getDefaultInfinity()]”
defaultIfNotSpecified=“[com.gs.fw.common.mithra.util.DefaultInfinityTimestamp.getDefaultInfinity()]”
/>
<AsOfAttribute name=“businessDate” fromColumnName=“FROM_Z” toColumnName=“THRU_Z”
toIsInclusive=“false”
isProcessingDate=“false”
timezoneConversion=“none”
infinityDate=“[com.gs.fw.common.mithra.util.DefaultInfinityTimestamp.getDefaultInfinity()]”
futureExpiringRowsExist=“true”
/>
・・・省略・・・
</MithraObject>
Employeeエンティティ定義ファイル hashtag: #ccc_g3
有効時間
65
バイテンポラルデータモデル
姓 名 FROM THRU IN OUT
鈴木 花子 2017/4/1 9999/12/1 2017/3/1 9999/12/1
 4月1日に「鈴木花子」さん入社。3月1日にシステムに登録
hashtag: #ccc_g3
Timestamp apr1 = Timestamp.valueOf(LocalDateTime.of(2017, 4, 1, 0, 0));
Employee hanakoNew = new Employee(apr1); //メモリ上にオブジェクト作成
hanakoNew.setLastName("鈴木");
hanakoNew.setFirstName("花子");
hanakoNew.insert(); // データベースに挿入
66
バイテンポラルデータモデル
 6月6日に結婚し、姓が「斎藤」に変更。同日システム
上に間違えて「斉藤」と登録
hashtag: #ccc_g3
//まずは6月6日付(有効時間)でフェッチ
Timestamp jun6 = Timestamp.valueOf(LocalDateTime.of(2017, 6, 6, 0, 0));
Operation opJun6 = EmployeeFinder.firstName().eq(“花子”).
and(EmployeeFinder.businessDate().eq(jun6)); //6月6日付でフェッチ
final Employee hanakoJun6 = EmployeeFinder.findOne(opJun6); //遅延ロードされるのでこの時点ではまだフ
ェッチされていない
//6月6日付(有効時間)でフェッチしたオブジェクトをアップデート
MithraManagerProvider.getMithraManager().executeTransactionalCommand(tx -> {
//6月6日(トランザクション時間)にこのコードが実行される
hanakoJun6.setLastName("斉藤"); // この時点で遅延フェッチ&アップデート
return null;
});
*スペースの都合上、以降テーブルは省略
67
バイテンポラルデータモデル
 名字の漢字が間違っており、6月8日にシステム上で修正
(斉藤=>斎藤)
hashtag: #ccc_g3
//まずは6月6日付(有効時間)でフェッチ
Timestamp jun6 = Timestamp.valueOf(LocalDateTime.of(2017, 6, 6, 0, 0));
Operation opJun6 = EmployeeFinder.firstName().eq(“花子”).
and(EmployeeFinder.businessDate().eq(jun6)); //6月6日付でフェッチ
final Employee hanakoJun6 = EmployeeFinder.findOne(opJun6); //遅延ロードされるのでこの時点ではまだフ
ェッチされていない
//6月6日付(有効時間)でフェッチしたオブジェクトをアップデート
MithraManagerProvider.getMithraManager().executeTransactionalCommand(tx -> {
//6月8日(トランザクション時間)にこのコードが実行される
hanakoJun6.setLastName(“斎藤"); // この時点で遅延フェッチ&アップデート
return null;
});
68
バイテンポラルデータモデル
 12月1日、退職にともない同日に人事情報をシステム上
で無効化
hashtag: #ccc_g3
//まずは12月1日付(有効時間)でフェッチ
Timestamp dec1 = Timestamp.valueOf(LocalDateTime.of(2017, 12, 1, 0, 0));
Operation opDec1 = EmployeeFinder.firstName().eq(“花子”).
and(EmployeeFinder.businessDate().eq(dec1)); //12月1日付でフェッチ
final Employee hanakoDec1 = EmployeeFinder.findOne(opDec1); //遅延ロードされ
るのでこの時点ではフェッチされない
//12月1日付(有効時間)でフェッチしたオブジェクトをアップデート
MithraManagerProvider.getMithraManager().executeTransactionalCommand(tx -> {
//12月1日(トランザクション時間)にこのコードが実行される
hanakoDec1.terminate(); // THRU = 12/1
return null;
});
69
バイテンポラルデータモデル
Reladomoのバイテンポラルデータ操作
 オブジェクト作成の際は有効時間の開始時を引数に与
える
 更新の際は、まず更新したい日付(有効時間)で
フェッチする。フェッチしたオブジェクトを更新する
と自動でIN/OUT/FROM/THRUが更新・挿入される
 データをある日付(有効時間)から無効化するには
terminate()を使う
 バイテンポラルデータモデルのテーブル構造を意識す
ることなく処理を記述することが可能
hashtag: #ccc_g3
70
Reladomoを用いたテンポラルデータモデルの操作
ここで挙げた例はReladomoの機能のさわりのみ
つづきはReladomo Kata / Reladomo Tourで
 Reladomo Kata GitHub (Reladomo チュートリアル)
 Guided Tour of Reladomo
hashtag: #ccc_g3
71
まとめ
 RDBで履歴を扱う際にはテンポラルデータモデルを用
いると素直に表現できる
 トランザクション時間と有効時間を組み合わせたテン
ポラルデータモデルが存在
 Reladomoを使うとテンポラルデータモデルの扱いが
抽象化できる
ぜひ、みなさんも試してみてください!
hashtag: #ccc_g3
72
株式会社FOLIOの紹介 hashtag: #ccc_g3
https://www.wantedly.com/companies/folio/projectsエンジニア募集中!
73
APPENDIX
74
テンポラルデータモデル hashtag: #ccc_g3
 Temporal Data Models(ppt)
 Temporal Databases - Richard T. Snodgrass 1998
 Temporal Databases - Richard T. Snodgrass and Ilsoo
Ahn 1986
 Temporal and Real-Time Databases: A Survey(ppt)
 Temporal Data and The Relational Model
75
Reladomo hashtag: #ccc_g3
 Reladomo GitHub
 Reladomo Kata GitHub (Reladomo チュートリアル)
 Guided Tour of Reladomo
 Reladomo Documentations

データ履歴管理のためのテンポラルデータモデルとReladomoの紹介 #jjug_ccc #ccc_g3