Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

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

42,644 views

Published on

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

Published in: Engineering

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

  1. 1. データ履歴管理のための テンポラルデータモデルと 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.
  2. 2. 1 株式会社FOLIOの紹介 hashtag: #ccc_g3 https://www.wantedly.com/companies/folio/projectsエンジニア募集中!
  3. 3. 2 Agenda 1. 自己紹介 2. RDBMSで履歴データを扱う  スナップショットデータモデル  トランザクション時間データモデル  有効時間データモデル  バイテンポラルデータモデル 3. Javaからバイテンポラルモデルを容易に扱うReladomoの紹介 hashtag: #ccc_g3
  4. 4. 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
  5. 5. 4 RDBMSで変更履歴をどう扱うか hashtag: #ccc_g3
  6. 6. 5 RDBMSで履歴データを扱う 単純なシナリオを考えてみましょう シンプルな人事システム。扱う情報は姓名のみ。  4月1日に「鈴木花子」さん入社。3月1日にシステムに登録  6月6日に結婚し、姓が「斎藤」に変更。同日システム上に 間違えて「斉藤」と登録  6月8日にシステム上で修正(斉藤=>斎藤) 。事実としては 6月6日づけで直したい  12月1日、退職にともない同日にシステム上で情報を無効化 hashtag: #ccc_g3
  7. 7. 6 RDBMSで履歴データを扱う  シナリオをよく観察すると、2種類の履歴が存在 hashtag: #ccc_g3 3/1 4/1 6/6 6/8 12/1 鈴木花子 事実情報の 履歴 システムに 反映された 履歴 入社🎉 「鈴木花子」を システムに登録 結婚して 斎藤になる👰 名字を 「斉藤」に変更 名字を 「斎藤」に変更 退職† データを無効化
  8. 8. 7 RDBMSで履歴データを扱う 最新の情報さえ 分かれば良い いつシステムに 反映されたか履 歴が知りたい 2種類の履歴から考えうる4種類の要件 事実情報の履歴 が知りたい 事実情報と、い つシステムに反 映されたか両方 の履歴が知りた い hashtag: #ccc_g3
  9. 9. 8 RDBMSで履歴データを扱う スナップショットデータモデル トランザクション時間データモデル 有効時間データモデル バイテンポラルデータモデル 各要件を満たすために、4つのデータモデルが考えられる hashtag: #ccc_g3
  10. 10. 9 スナップショットデータモデル hashtag: #ccc_g3 最新の情報さえ 分かれば良い
  11. 11. 10 スナップショットデータモデル  スナップショットデータモデルで表現できるのは、最新 の情報のみ(履歴は表現できない) hashtag: #ccc_g3 3/1 4/1 6/6 6/8 12/1 鈴木花子 事実情報の 履歴 システムに 反映された 履歴 入社🎉 「鈴木花子」を システムに登録 結婚して 斎藤になる👰 名字を 「斉藤」に変更 名字を 「斎藤」に変更 退職† データを無効化
  12. 12. 11 スナップショットデータモデル 姓 名 inserted_at updated_at 鈴木 花子 2017/3/1 2017/3/1  4月1日に「鈴木花子」さん入社。3月1日にシステムに登録 鈴木さん入社! 入社日というカラムが あってもいいような気がする hashtag: #ccc_g3
  13. 13. 12 スナップショットデータモデル  6月6日に結婚し、姓が「斎藤」に変更。同日システム 上に間違えて「斉藤」と登録 間違えて「斉藤」と入 力してしまいました 姓 名 inserted_at updated_at 斉藤 花子 2017/3/1 2017/6/6 hashtag: #ccc_g3
  14. 14. 13 スナップショットデータモデル  6月8日にシステム上で修正(斉藤=>斎藤) 正しい姓は「斎藤」さ んですね。。。 姓 名 inserted_at updated_at 斎藤 花子 2017/3/1 2017/6/8 hashtag: #ccc_g3
  15. 15. 14 スナップショットデータモデル  12月1日、退職にともない同日に人事情報をシステム上 で無効化 _人人人人人人人人人人人_ > 物理削除 <  ̄Y^Y^Y^Y^Y^Y^Y^Y^Y^Y ̄ 物理削除がイヤ なら禁断の論理 削除フラグとい う手もあるよ。 フフッ 姓 名 inserted_at updated_at hashtag: #ccc_g3
  16. 16. 15 スナップショットデータモデル  スナップショットデータモデルで履歴を実装しようと すると、バージョンカラムや履歴テーブルを別に用意 するなど、力技が求められる  世に存在する履歴系のデータは、この力技で実装され ていることが多い(印象)  さらにスナップショットデータモデルでは、データを 無効化するのに「論理削除」か「物理削除」か、と悩 むケースも hashtag: #ccc_g3
  17. 17. 16 トランザクション時間データモデル hashtag: #ccc_g3 いつシステムに 反映されたか履 歴が知りたい
  18. 18. 17 トランザクション時間データモデル  トランザクション時間データモデルで表現できるのは、 「システムに反映された」履歴 hashtag: #ccc_g3 3/1 4/1 6/6 6/8 12/1 鈴木花子 事実情報の 履歴 システムに 反映された 履歴 入社🎉 「鈴木花子」を システムに登録 結婚して 斎藤になる👰 名字を 「斉藤」に変更 名字を 「斎藤」に変更 退職† データを無効化
  19. 19. 18 トランザクション時間データモデル 姓 名 IN OUT 鈴木 花子 2017/3/1 9999/12/1 鈴木さん入社! INはデータベースに挿入された日付、 OUTはその行が無効になった(なる)日付、 OUTはデフォルトでINFINITY(この場合9999年)とするよ やっぱり入社日カラムが欲しくなる。。  4月1日に「鈴木花子」さん入社。3月1日にシステムに登録 hashtag: #ccc_g3
  20. 20. 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
  21. 21. 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
  22. 22. 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
  23. 23. 22 トランザクション時間データモデル 履歴データをどう検索するのか? hashtag: #ccc_g3
  24. 24. 23 トランザクション時間データモデル  任意の時間Xにデータベース上で最新だった情報を とってくる select * from EMPLOYEE where IN < X and OUT >= X hashtag: #ccc_g3
  25. 25. 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
  26. 26. 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
  27. 27. 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
  28. 28. 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
  29. 29. 28 トランザクション時間データモデル  ある時点でデータベース上で最新だった情報を一意に 取得することができる  過去の履歴を一気に取得することもでき、時間軸で ソートも可  最新の行を無効化するにはOUTコラムを更新するだけ でOK(物理削除も論理削除フラグも不要)  監査情報を自然な形でモデリングできる トランザクション時間データモデルの適用例  ドキュメントの履歴管理  状態遷移の履歴管理  etc. hashtag: #ccc_g3
  30. 30. 29 有効時間データモデル hashtag: #ccc_g3 事実情報の履歴 がほしい
  31. 31. 30 有効時間データモデル  有効時間データモデルで表現できるのは、「事実情報」 の履歴 hashtag: #ccc_g3 3/1 4/1 6/6 6/8 12/1 鈴木花子 事実情報の 履歴 システムに 反映された 履歴 入社🎉 「鈴木花子」を システムに登録 結婚して 斎藤になる👰 名字を 「斉藤」に変更 名字を 「斎藤」に変更 退職† データを無効化
  32. 32. 31 有効時間データモデル 姓 名 FROM THRU 鈴木 花子 2017/4/1 9999/12/1  4月1日に「鈴木花子」さん入社。3月1日にシステムに登録 鈴木さん入社! FROMは当該データが事実として有効である最初の日、 THRUは当該データが事実として有効である最終日の翌日*、 THRUはデフォルトでINFINITY(この場合9999年)とするよ これで入社日がわかる。。 *ここではTHRUを最終日翌日としていますが、FROMを最初の日の前日とする方法もあります hashtag: #ccc_g3 *Throughの略
  33. 33. 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
  34. 34. 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
  35. 35. 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
  36. 36. 35 有効時間データモデル 検索方法はトランザクション時間 データモデルの場合と同様なので省略 hashtag: #ccc_g3
  37. 37. 36 有効時間データモデル  ある時点での「事実情報」を一意に取得することがで きる  過去の事実の履歴を一気に取得することもでき、時間 軸でソートも可  最新の行を無効化するにはTHRUコラムを更新するだ けでOK(物理削除も論理削除フラグも不要)  事実の履歴を自然な形でモデリングできる 有効時間データモデルの適用例  変更履歴を気にしないビジネスデータ  etc. hashtag: #ccc_g3
  38. 38. 37 バイテンポラルデータモデル hashtag: #ccc_g3 事実情報と、い つシステムに反 映されたか両方 の履歴が知りた い
  39. 39. 38 バイテンポラルデータモデル  バイテンポラルデータモデルで表現できるのは、「シス テムに反映された」履歴と「事実情報」の履歴両方 hashtag: #ccc_g3 3/1 4/1 6/6 6/8 12/1 鈴木花子 事実情報の 履歴 システムに 反映された 履歴 入社🎉 「鈴木花子」を システムに登録 結婚して 斎藤になる👰 名字を 「斉藤」に変更 名字を 「斎藤」に変更 退職† データを無効化
  40. 40. 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
  41. 41. 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
  42. 42. 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
  43. 43. 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
  44. 44. 43 バイテンポラルデータモデル  バイテンポラルデータモデルは「システム履歴」と 「事実情報の履歴」を同時に正しく表現することがで きる非常に強力なモデル バイテンポラルデータモデルの適用例  監査履歴が重要な金融システムの基幹データ  その他、時系列に紐づくビジネスデータで変更履歴が 重要なユースケース  etc. hashtag: #ccc_g3
  45. 45. 44 どう実装する? バイテンポラルデータモデル の雰囲気はわかった でも、ぱっと見複雑そう。 結局、実装するのが大変 なのでは。。。? 安心してください Reladomoを使えば簡単です! hashtag: #ccc_g3
  46. 46. 45 Javaからバイテンポラルモデルを 容易に扱えるReladomoの紹介 hashtag: #ccc_g3
  47. 47. 46 Reladomoとは https://github.com/goldmansachs/reladomo  ゴールドマン・サックス社が2016年10月にGitHubに OSSとして公開したJava ORMフレームワーク  Apache License 2.0  バイテンポラルデータモデルをネイティブサポート  強力に型付けられたクエリー言語  シャーディングのネイティブサポート  ユニットテストのフルサポート  etc. hashtag: #ccc_g3
  48. 48. 47 Reladomoとは Reladomoのコード生成  xmlによるエンティティ定義を用いてコード生成  同xmlからddlの生成も可能  Abstract Classはxmlに変更のある都度生成される  Concrete Classは初回のみ生成しVCSにコミット、ビ ジネスロジックを記述。コード生成で上書きされない。 hashtag: #ccc_g3
  49. 49. 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>
  50. 50. 49 Employeeエンティティ hashtag: #ccc_g3 トランザクション時間 生成されたAbstract Class
  51. 51. 50 Employeeエンティティ hashtag: #ccc_g3 Concrete Class (初回のみ生成) さまざまなビジネスロジックを記述できる
  52. 52. 51 DBトランザクション hashtag: #ccc_g3 テンポラルオブジェクトの挿入、更新はトランザクション内で のみ可能 (以降のコード例では省略している場合も) Employee hanako = MithraManagerProvider.getMithraManager().executeTransactionalCommand(tx -> { Employee hanakoNew = new Employee(); hanakoNew.setLastName(“鈴木”); hanakoNew.setFirstName(“花子”); hanakoNew.insert(); return hanakoNew; //トランザクション終了後に使用したいオブジェクトを返り値とし て与えることができる。 });
  53. 53. 52 ReladomoのFinder DSL  型付けられた強力なクエリー言語  自動生成されたフィールドを用いてOperationを構築 hashtag: #ccc_g3 Operation op = EmployeeFinder.lastName().eq("鈴木") .and(EmployeeFinder.firstName().eq("花子")); final Employee hanako = EmployeeFinder.findOne(op);
  54. 54. 53 Reladomoでトランザクション時間 を扱う例 hashtag: #ccc_g3
  55. 55. 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> トランザクション時間
  56. 56. 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日にシステムに登録
  57. 57. 56 トランザクション時間データモデル 姓 名 IN OUT 鈴木 花子 2017/3/1 2017/6/6 斉藤 花子 2017/6/6 9999/12/1  6月6日に結婚し、姓が「斎藤」に変更。同日システム 上に間違えて「斉藤」と登録 hashtag: #ccc_g3 //6月6日にこのコードが実行される hanako.setLastName("斉藤"); // アップデート
  58. 58. 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(“斎藤"); // アップデート
  59. 59. 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
  60. 60. 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'
  61. 61. 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'
  62. 62. 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'
  63. 63. 62 トランザクション時間データモデル Reladomoのトランザクション時間データ操作  IN/OUTカラムにまつわる処理はユーザーが気にするこ となくフレームワークが吸収してくれる  デフォルトで最新のデータが検索される(OUT = INFINITY)  データをある日付(トランザクション時間)から無効 化するにはterminate()を使う  過去の履歴の検索の際はprocessingDate()を明示的に 指定してやる hashtag: #ccc_g3
  64. 64. 63 Reladomoでバイテンポラルデータ モデルを扱う例 hashtag: #ccc_g3
  65. 65. 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 有効時間
  66. 66. 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(); // データベースに挿入
  67. 67. 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; }); *スペースの都合上、以降テーブルは省略
  68. 68. 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; });
  69. 69. 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; });
  70. 70. 69 バイテンポラルデータモデル Reladomoのバイテンポラルデータ操作  オブジェクト作成の際は有効時間の開始時を引数に与 える  更新の際は、まず更新したい日付(有効時間)で フェッチする。フェッチしたオブジェクトを更新する と自動でIN/OUT/FROM/THRUが更新・挿入される  データをある日付(有効時間)から無効化するには terminate()を使う  バイテンポラルデータモデルのテーブル構造を意識す ることなく処理を記述することが可能 hashtag: #ccc_g3
  71. 71. 70 Reladomoを用いたテンポラルデータモデルの操作 ここで挙げた例はReladomoの機能のさわりのみ つづきはReladomo Kata / Reladomo Tourで  Reladomo Kata GitHub (Reladomo チュートリアル)  Guided Tour of Reladomo hashtag: #ccc_g3
  72. 72. 71 まとめ  RDBで履歴を扱う際にはテンポラルデータモデルを用 いると素直に表現できる  トランザクション時間と有効時間を組み合わせたテン ポラルデータモデルが存在  Reladomoを使うとテンポラルデータモデルの扱いが 抽象化できる ぜひ、みなさんも試してみてください! hashtag: #ccc_g3
  73. 73. 72 株式会社FOLIOの紹介 hashtag: #ccc_g3 https://www.wantedly.com/companies/folio/projectsエンジニア募集中!
  74. 74. 73 APPENDIX
  75. 75. 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
  76. 76. 75 Reladomo hashtag: #ccc_g3  Reladomo GitHub  Reladomo Kata GitHub (Reladomo チュートリアル)  Guided Tour of Reladomo  Reladomo Documentations

×