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入門
株式会社FOLIO 伊藤博志
JJUGナイトセミナー 2017.7.26
Reladomo is an open source software Licensed under Apache 2.0 License,
Cop...
1
Agenda
1. 自己紹介
2. Reladomoの基本
3. Reladomoのコード生成
4. Reladomoの検索・挿入・更新・削除処理
5. Reladomoの関連
6. GS Collectionsサポート
7. ユニットテス...
2
自己紹介
趣味:ドラム演奏
JavaOneコミュニティバンド
Null Pointersで演奏経験あり(日本人初)
Head of Engineering @ FOLIO
伊藤 博志
Eclipse Collections:共同プロジェクト...
3
Reladomoの基本
4
Reladomoとは
https://github.com/goldmansachs/reladomo
 ゴールドマン・サックス社が2016年9月にGitHubに
OSSとして公開したJava ORMフレームワーク
 Apache Li...
5
Reladomoの哲学
Reladomo Philosophy & Vision
Long Lived Code [LLC]
Don’t Repeat Yourself [DRY]
Agile [AGL]
Domain based ...
6
Reladomoのコード生成
7
コード生成:xmlファイル
<MithraObject objectType="transactional"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNames...
8
コード生成:xmlファイルからJavaクラス生成
Xml file
ビルド内でコード生成
9
コード生成:Finderクラス
Xml file
検索に用いる
10
コード生成:Entityクラス
Xml file
単一レコードに対応
11
コード生成:Listクラス
Xml file
複数レコードに対応
12
コード生成:DatabaseObjectクラス
Xml file
コネクション・テーブル情報を保持
13
コード生成:抽象クラス・Finderクラス
Xml file
抽象クラス・Finderクラスは
xml変更に追従するため常にビルド時に生成する
14
コード生成:具象クラス
Xml file
具象クラスは生成コードをVCSにコミット
独自のビジネスロジックを追加可能
15
Reladomoの検索処理
16
Finder API
Finderクラスを用いてOperationを生成
型安全に検索
sqlは一切書かない
–検索条件: Operation - Finder APIから作成
–1件検索:Finder.findOne(Operat...
17
Finder API:一件検索
Operation findTaroOp =
PersonFinder.firstName().eq(“太郎");
Person john =
PersonFinder.findOne(findTaroOp...
18
Finder API:複数検索
Operation findAllOp = PersonFinder.all();
PersonList people =
PersonFinder.findMany(findAllOp);
Finder ...
19
Finder API:Operationの例1
Operation op1 = PersonFinder.firstName().eq("大輔");
// SQL: WHERE first_name = '大輔’
Operation op...
20
Finder API:Operationの例2
Operation op3 =
PersonFinder.age().in(IntHashSet.newSetWith(22, 24, 30));
// SQL: WHERE age in ...
21
Reladomoのキャッシュ
22
ReIadomoのキャッシュ
Reladomoは複数のキャッシュ戦略を持つ
–Partial Cache
–Full Cache
–None
ランタイムの設定でエンティティごとにキャッシュ
戦略を選択可能
クエリーキャッシュとオブジ...
23
Operation findTaroOp = PersonFinder.firstName().eq("太郎");
Person taro = PersonFinder.findOne(findTaroOp);
Operation op1...
24
2017-07-23 18:02:06:881 [main] DEBUG com.gs.fw.common.mithra.sqllogs.Person -
connection:2005169944 find with: select t...
25
Operation findAllOp = PersonFinder.all();
PersonList people = PersonFinder.findMany(findAllOp);
people.forceResolve(); ...
26
2017-07-23 19:34:04:415 [main] DEBUG com.gs.fw.common.mithra.sqllogs.Person -
connection:112049309 find with: select t0...
27
Reladomoのトランザクション処理
28
Reladomoのトランザクション処理
トランザクション内で行う必要のある処理に関しては
ラムダ式内に記述しexecuteTransactionalCommand()
にわたす。
以下、例によってはこのトランザクションのコード
を省略して...
29
Reladomoの挿入処理
30
Reladomoの挿入処理:単一挿入
Person jiro = new Person("二郎", "山田", 45);
jiro.insert();
Entityインスタンスを作成
Insert()メソッドで挿入
31
PersonList newPeople = new PersonList();
newPeople.add(new Person("二郎", "山田", 45));
newPeople.add(new Person("さくら", "鈴木...
32
Reladomoの挿入処理:オブジェクトの同一性
// 参照を取得後に挿入処理
Person jiro = new Person("二郎", "山田", 45);
jiro.insert();
// DBから取得
Operation op...
33
Reladomoの更新処理
34
Operation tanakaOp = PersonFinder.lastName().eq("田中");
Person tanaka = PersonFinder.findOne(tanakaOp);
tanaka.setAge(25...
35
Reladomoの更新処理:複数更新
複数検索でListオブジェクトを取得
Listに対してsetXxx()メソッドで複数更新
PersonList people =
PersonFinder.findMany(PersonFinder....
36
Reladomoの更新処理:バッチ更新
Entityインスタンスを検索
トランザクション内での複数更新はバッチ更新される
Person tanaka = PersonFinder.findOne(PersonFinder.lastName...
37
Reladomoの削除処理
38
Reladomoの削除処理:単一削除
Entityインスタンスを検索
delete()メソッドで削除
Operation op = PersonFinder.lastName().eq("田中");
Person tanaka = Per...
39
Reladomoの削除処理:複数削除
複数検索でListオブジェクトを取得
Listに対してdeleteAll()メソッドで複数削除
Operation op =
PersonFinder.lastName().in(Sets.mutab...
40
Reladomoの関連
41
Reladomoの関連:サンプルモデル
42
Reladomoの関連:Person.xml
<Relationship name="pets"
relatedObject="Pet"
cardinality="one-to-many"
relatedIsDependent="true...
43
Reladomoの関連:Pet.xml
<Relationship name="petType"
relatedObject="PetType"
cardinality="many-to-one">
this.petTypeId = Pe...
44
Reladomoの関連:Finderによる柔軟な検索1
//犬を飼っている飼い主を取得
Operation op =
PersonFinder.pets().petTypeId().eq(PetType.DOG);
Person dogO...
45
Reladomoの関連:Finderによる柔軟な検索1
2017-07-24 21:51:55:924 [main] DEBUG com.gs.fw.common.mithra.sqllogs.Person -
connection:19...
46
Reladomoの関連:Finderによる柔軟な検索2
reverseRelationshipNameで指定した逆引きのAPI(e.g. owner)も活用可
通常の複数検索と同様
//佐藤さんが飼っているペットを取得
Operation...
47
Reladomoの関連:Finderによる柔軟な検索2
2017-07-25 07:09:31:305 [main] DEBUG com.gs.fw.common.mithra.sqllogs.Pet - connection:70853...
48
Reladomoの関連:deepFetch
Relationshipを解決する際のN+1問題を避けるための機構
//ペット飼っている人を取得
Operation op = PersonFinder.pets().exists();
Per...
49
Reladomoの関連:deepFetch
deepFetchを使わない場合に発行されるSQL
2017-07-25 07:23:05:473 [main] DEBUG com.gs.fw.common.mithra.sqllogs.Pe...
50
Reladomoの関連:deepFetch
deepFetchを使った場合に発行されるSQL
2017-07-25 07:27:16:540 [main] DEBUG com.gs.fw.common.mithra.sqllogs.Per...
51
GS Collectionsのネイティブサポート
52
GS Collectionsサポート:使用例
データ取得後にインメモリーでさまざまなデータ
処理をしたい場合に最適
//苗字を取得
List<String> lastNames =
people.asGscList().collect(...
53
あれ、Eclipse Collectionsは?
54
Eclipse Collections対応
 まもなくサポートされる、予定。。。!
(今年初めに着手したものの、まだ終わってませんすみません。。。)
55
JUnitテストサポート
56
JUnitテストサポート
インメモリのh2データベース上にデータを初期化
57
JUnitテストサポート
DBアクセスを伴ったビジネスロジックをユニット
テストレベルで担保可能
バグの再現も容易
冪等性の担保のようなロジックもテスト可能
58
バイテンポラルモデルサポート
59
バイテンポラルモデルサポート
Reladomoのキラーコンテンツとも言える機能
2つの時間概念を同時に扱うことができる
–有効時間
–トランザクション時間
60
バイテンポラルモデル
詳しくはJJUG CCCのプレゼン資料をごらんください
61
JJUG CCCでは話せなかった「関連」
を絡めたバイテンポラルサポート
62
田中さん Petサクラ
pets
owner
バイテンポラルモデル:例 - 関連も自由自在
田中さん チビ
pets
owner
田中さん
1月1日時点ではペットを飼っていなかった田中さん
3月1日から犬のチビを飼い始めました
5月1日か...
63
バイテンポラルモデルサポート:初期状態
Operation findTanakaOp = PersonFinder.lastName().eq("田中")
.and(PersonFinder.businessDate().eq(parse...
64
バイテンポラルモデルサポート:3/1付で挿入処理
3月1日から犬のチビを飼い始めました
Pet chibi = new Pet(parse("2017/03/01"));
chibi.setName("チビ");
chibi.setAge...
65
バイテンポラルモデルサポート:5/1付で挿入処理
5月1日から猫のサクラを飼い始めました
Pet sakura = new
Pet(parse(“2017/05/01”));
sakura.setName(“サクラ");
sakura.s...
66
2つのPetオブジェクト挿入後の
検索結果を見てみましょう
67
Operation findTanakaAsOf20170201 =
PersonFinder.lastName().eq("田中")
.and(PersonFinder.businessDate()
.eq(parse("2017/02...
68
Operation findTanakaAsOf20170302 =
PersonFinder.lastName().eq("田中")
.and(PersonFinder.businessDate()
.eq(parse("2017/03...
69
Operation findTanakaAsOf20170502 =
PersonFinder.lastName().eq("田中")
.and(PersonFinder.businessDate()
.eq(parse("2017/05...
70
「関連」を絡めたバイテンポラルモデル
において実際にデータやクエリーがどう
表現されているのか気になる方は懇親会
で伊藤とお話ししましょう
*そのうちブログに書くかも
バイテンポラルモデルサポート:データの表現は割愛
71
まとめ
 Reladomoはオブジェクト指向の極みのようなORM
 検索はFinderによる型安全なクエリー
 挿入・更新・削除処理も一切SQLを書かずにオブジェクト
上のAPIで完結
 複雑なビジネスロジックもJUnit上で安全...
72
入門を超えて
Reladomoは非常に高度な機能を持ち合わせたエンタープライ
ズグレードのORMフレームワークです
 シャーディングのネイティブサポート
 Temp tableを用いたクエリーのサポート
 入力集合と出力集合をMul...
73
Reladomoをもっと学ぶには
つづきはReladomo Kata / Reladomo Tourで
 Reladomo Kata GitHub (Reladomo チュートリアル)
 Guided Tour of Reladomo...
74
告知
75
Scala関西Summit 2017(9月9日 土)
ScalikeJDBCやSkinny Frameworkでおなじみ
グッドフロー・テクノロジーズ瀬良さんと共同で
Reladomo in Scalaというセッションをします!
お楽しみ...
76
APPENDIX
77
リンク集
 データ履歴管理のためのテンポラルデータモデルと
Reladomoの紹介
 Reladomo GitHub
 Reladomo Kata GitHub (Reladomo チュートリアル)
 Guided Tour of...
Upcoming SlideShare
Loading in …5
×

Reladomo入門 JJUGナイトセミナー #jjug

5,273 views

Published on

Reladomo入門 JJUGナイトセミナー #jjug 株式会社FOLIO 伊藤博志

Published in: Technology
  • Be the first to comment

Reladomo入門 JJUGナイトセミナー #jjug

  1. 1. Reladomo入門 株式会社FOLIO 伊藤博志 JJUGナイトセミナー 2017.7.26 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 Agenda 1. 自己紹介 2. Reladomoの基本 3. Reladomoのコード生成 4. Reladomoの検索・挿入・更新・削除処理 5. Reladomoの関連 6. GS Collectionsサポート 7. ユニットテストサポート 8. バイテンポラルモデル
  3. 3. 2 自己紹介 趣味:ドラム演奏 JavaOneコミュニティバンド Null Pointersで演奏経験あり(日本人初) Head of Engineering @ FOLIO 伊藤 博志 Eclipse Collections:共同プロジェクトリード兼コミッター Reladomo:コントリビューター OpenJDK:コントリビューター JJUG CCC、Java Day Tokyo、JavaOne San Francisco登壇 2017年5月に株式会社FOLIO入社。
  4. 4. 3 Reladomoの基本
  5. 5. 4 Reladomoとは https://github.com/goldmansachs/reladomo  ゴールドマン・サックス社が2016年9月にGitHubに OSSとして公開したJava ORMフレームワーク  Apache License 2.0  ORMフレームワークであり、オブジェクト指向の徹底  xmlからコード/DDLの自動生成  バイテンポラルデータモデルをネイティブサポート  強力に型付けられたクエリー言語(SQLは書かない)  ユニットテストのフルサポート  etc.
  6. 6. 5 Reladomoの哲学 Reladomo Philosophy & Vision Long Lived Code [LLC] Don’t Repeat Yourself [DRY] Agile [AGL] Domain based Object Oriented paradigm [DOO] Correctness & Consistency [CC]
  7. 7. 6 Reladomoのコード生成
  8. 8. 7 コード生成:xmlファイル <MithraObject objectType="transactional" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="reladomoobject.xsd"> <PackageName>sample.domain</PackageName> <ClassName>Person</ClassName> <DefaultTable>PERSON</DefaultTable> <Attribute javaType="int" name="personId" columnName="PERSON_ID" primaryKey="true" primaryKeyGeneratorStrategy="Max"/> <Attribute javaType="String" name="firstName" columnName="FIRST_NAME" nullable="false" maxLength="64"/> <Attribute javaType="String" name="lastName" columnName="LAST_NAME" nullable="false" maxLength="64"/> </MithraObject>
  9. 9. 8 コード生成:xmlファイルからJavaクラス生成 Xml file ビルド内でコード生成
  10. 10. 9 コード生成:Finderクラス Xml file 検索に用いる
  11. 11. 10 コード生成:Entityクラス Xml file 単一レコードに対応
  12. 12. 11 コード生成:Listクラス Xml file 複数レコードに対応
  13. 13. 12 コード生成:DatabaseObjectクラス Xml file コネクション・テーブル情報を保持
  14. 14. 13 コード生成:抽象クラス・Finderクラス Xml file 抽象クラス・Finderクラスは xml変更に追従するため常にビルド時に生成する
  15. 15. 14 コード生成:具象クラス Xml file 具象クラスは生成コードをVCSにコミット 独自のビジネスロジックを追加可能
  16. 16. 15 Reladomoの検索処理
  17. 17. 16 Finder API Finderクラスを用いてOperationを生成 型安全に検索 sqlは一切書かない –検索条件: Operation - Finder APIから作成 –1件検索:Finder.findOne(Operation) –複数検索:Finder.findMany(Operation)
  18. 18. 17 Finder API:一件検索 Operation findTaroOp = PersonFinder.firstName().eq(“太郎"); Person john = PersonFinder.findOne(findTaroOp); Finder APIを用いてOperationを作成 Finder.findOne()で一件検索 結果はEntityオブジェクトで取得
  19. 19. 18 Finder API:複数検索 Operation findAllOp = PersonFinder.all(); PersonList people = PersonFinder.findMany(findAllOp); Finder APIを用いてOperationを作成 Finder.findMany()で複数検索 結果はListオブジェクトで取得
  20. 20. 19 Finder API:Operationの例1 Operation op1 = PersonFinder.firstName().eq("大輔"); // SQL: WHERE first_name = '大輔’ Operation op2 = PersonFinder.lastName().endsWith("藤"); // SQL: WHERE last_name LIKE '%藤’ Operation op1OrOp2 = op1.or(op2); // SQL: WHERE (( first_name = '大輔') OR ( last_name LIKE '%藤')) Operation op1AndOp2 = op1.and(op2); // SQL: WHERE (( first_name = '大輔') AND ( last_name LIKE '%藤'))
  21. 21. 20 Finder API:Operationの例2 Operation op3 = PersonFinder.age().in(IntHashSet.newSetWith(22, 24, 30)); // SQL: WHERE age in (22, 24, 30) Operation op4 = PersonFinder.age().notIn(IntHashSet.newSetWith(22, 25)); // SQL: WHERE age in (22, 25) Operation op5 = PersonFinder.age().greaterThan(25); // SQL: WHERE age > 25 その他複雑なクエリを型安全に記述することが可能
  22. 22. 21 Reladomoのキャッシュ
  23. 23. 22 ReIadomoのキャッシュ Reladomoは複数のキャッシュ戦略を持つ –Partial Cache –Full Cache –None ランタイムの設定でエンティティごとにキャッシュ 戦略を選択可能 クエリーキャッシュとオブジェクトキャッシュ キャッシュ機構は高度に最適化されており、データ ベースへのアクセスを最小に保つことができる
  24. 24. 23 Operation findTaroOp = PersonFinder.firstName().eq("太郎"); Person taro = PersonFinder.findOne(findTaroOp); Operation op1 = PersonFinder.firstName().eq("大輔"); Operation op2 = PersonFinder.lastName().endsWith("藤"); PersonList people2 = PersonFinder.findMany(op1.or(op2)); people2.forceResolve(); Operation op3 = PersonFinder.age().greaterThan(25); PersonList people3 = PersonFinder.findMany(op3); people3.forceResolve(); 3種類の条件の違うクエリーは直接DBを叩く ReIadomoのキャッシュ
  25. 25. 24 2017-07-23 18:02:06:881 [main] DEBUG com.gs.fw.common.mithra.sqllogs.Person - connection:2005169944 find with: select t0.person_id,t0.first_name,t0.last_name,t0.age from person t0 where t0.first_name = '太郎’ 2017-07-23 18:02:06:891 [main] DEBUG com.gs.fw.common.mithra.sqllogs.Person - retrieved 1 objects, 83.0 ms per 2017-07-23 18:02:06:992 [main] DEBUG com.gs.fw.common.mithra.sqllogs.Person - connection:2005169944 find with: select t0.person_id,t0.first_name,t0.last_name,t0.age from person t0 where (( t0.first_name = '大輔') or ( t0.last_name like '%藤')) 2017-07-23 18:02:06:994 [main] DEBUG com.gs.fw.common.mithra.sqllogs.Person - retrieved 3 objects, 1.3333333333333333 ms per 2017-07-23 18:02:06:997 [main] DEBUG com.gs.fw.common.mithra.sqllogs.Person - connection:2005169944 find with: select t0.person_id,t0.first_name,t0.last_name,t0.age from person t0 where t0.age > 25 2017-07-23 18:02:07:005 [main] DEBUG com.gs.fw.common.mithra.sqllogs.Person - retrieved 1 objects, 8.0 ms per ReIadomoのキャッシュ 3回のDBアクセス
  26. 26. 25 Operation findAllOp = PersonFinder.all(); PersonList people = PersonFinder.findMany(findAllOp); people.forceResolve(); //Reladomoは通常遅延ロードするので 例示のために強制ロード Operation findTaroOp = PersonFinder.firstName().eq("太郎"); … 前ページと同じ3つのクエリー 前ページと同じ3種類のクエリーの直前に全選択の クエリーを走らせると… ReIadomoのキャッシュ
  27. 27. 26 2017-07-23 19:34:04:415 [main] DEBUG com.gs.fw.common.mithra.sqllogs.Person - connection:112049309 find with: select t0.person_id,t0.first_name,t0.last_name,t0.age from person t02017-07-23 19:34:04:458 [main] DEBUG com.gs.fw.common.mithra.sqllogs.Person - retrieved 4 objects, 25.5 ms per ReIadomoのキャッシュ 1回のDBアクセスで全選択 DBへのIOは全選択クエリーの1回のみ その後のクエリーはキャッシュから取得
  28. 28. 27 Reladomoのトランザクション処理
  29. 29. 28 Reladomoのトランザクション処理 トランザクション内で行う必要のある処理に関しては ラムダ式内に記述しexecuteTransactionalCommand() にわたす。 以下、例によってはこのトランザクションのコード を省略している。 MithraManagerProvider.getMithraManager().executeTransactionalCommand(tx -> { //検索・挿入・更新・削除処理の記述 return someObject; });
  30. 30. 29 Reladomoの挿入処理
  31. 31. 30 Reladomoの挿入処理:単一挿入 Person jiro = new Person("二郎", "山田", 45); jiro.insert(); Entityインスタンスを作成 Insert()メソッドで挿入
  32. 32. 31 PersonList newPeople = new PersonList(); newPeople.add(new Person("二郎", "山田", 45)); newPeople.add(new Person("さくら", "鈴木", 28)); newPeople.insertAll(); Reladomoの挿入処理:バッチ挿入 Listインスタンスを作成 新規作成したEntityをListに追加 InsertAll()メソッドで複数挿入
  33. 33. 32 Reladomoの挿入処理:オブジェクトの同一性 // 参照を取得後に挿入処理 Person jiro = new Person("二郎", "山田", 45); jiro.insert(); // DBから取得 Operation op = PersonFinder.firstName().eq(“二郎"); Person jiroDb = PersonFinder.findOne(op); // 作成されたオブジェクトはDBから取得したものと同値かつ同一 Assert.assertTrue(jiro.equals(jiroFromDb)); Assert.assertTrue(jiro == jiroFromDb); Reladomoは永続化されたオブジェクトはメモリ上に 唯一つのみ存在することを保証する
  34. 34. 33 Reladomoの更新処理
  35. 35. 34 Operation tanakaOp = PersonFinder.lastName().eq("田中"); Person tanaka = PersonFinder.findOne(tanakaOp); tanaka.setAge(25); Reladomoの更新処理:単一更新 Entityインスタンスを検索 setXxx()メソッドで更新 *トランザクション外での更新は即時反映される
  36. 36. 35 Reladomoの更新処理:複数更新 複数検索でListオブジェクトを取得 Listに対してsetXxx()メソッドで複数更新 PersonList people = PersonFinder.findMany(PersonFinder.all()); people.setAge(20); 2017-07-23 21:45:08:489 [main] DEBUG com.gs.fw.common.mithra.sqllogs.Person - multi update of 6 objects with: update person set age = ? where person_id in (?...) 2017-07-23 21:45:08:489 [main] DEBUG com.gs.fw.common.mithra.batch.sqllogs.Person - multi updating with: update person set age = 20 where person_id in (0,1,2,3,4,5)
  37. 37. 36 Reladomoの更新処理:バッチ更新 Entityインスタンスを検索 トランザクション内での複数更新はバッチ更新される Person tanaka = PersonFinder.findOne(PersonFinder.lastName().eq("田中")); Person sato = PersonFinder.findOne(PersonFinder.lastName().eq("佐藤")); MithraManagerProvider.getMithraManager().executeTransactionalCommand(tx -> { tanaka.setAge(25); sato.setAge(23); return null; }); 2017-07-23 21:38:39:403 [main] DEBUG com.gs.fw.common.mithra.sqllogs.Person - batch update of 2 objects with: update person set age = ? where person_id = ? 2017-07-23 21:38:39:404 [main] DEBUG com.gs.fw.common.mithra.batch.sqllogs.Person - batch updating with: update person set age = 25 where person_id = 0 2017-07-23 21:38:39:405 [main] DEBUG com.gs.fw.common.mithra.batch.sqllogs.Person - batch updating with: update person set age = 23 where person_id = 1
  38. 38. 37 Reladomoの削除処理
  39. 39. 38 Reladomoの削除処理:単一削除 Entityインスタンスを検索 delete()メソッドで削除 Operation op = PersonFinder.lastName().eq("田中"); Person tanaka = PersonFinder.findOne(op); tanaka.delete();
  40. 40. 39 Reladomoの削除処理:複数削除 複数検索でListオブジェクトを取得 Listに対してdeleteAll()メソッドで複数削除 Operation op = PersonFinder.lastName().in(Sets.mutable.of(“田中”, “佐藤")); PersonList people = PersonFinder.findMany(op); people.deleteAll();
  41. 41. 40 Reladomoの関連
  42. 42. 41 Reladomoの関連:サンプルモデル
  43. 43. 42 Reladomoの関連:Person.xml <Relationship name="pets" relatedObject="Pet" cardinality="one-to-many" relatedIsDependent="true" reverseRelationshipName="owner"> this.personId = Pet.ownerId </Relationship> Person PetPetPet pets owner
  44. 44. 43 Reladomoの関連:Pet.xml <Relationship name="petType" relatedObject="PetType" cardinality="many-to-one"> this.petTypeId = PetType.petTypeId </Relationship> Pet PetTypepetType
  45. 45. 44 Reladomoの関連:Finderによる柔軟な検索1 //犬を飼っている飼い主を取得 Operation op = PersonFinder.pets().petTypeId().eq(PetType.DOG); Person dogOwner = PersonFinder.findOne(op); テーブル間のjoinを柔軟に記述 通常の一件検索と同様
  46. 46. 45 Reladomoの関連:Finderによる柔軟な検索1 2017-07-24 21:51:55:924 [main] DEBUG com.gs.fw.common.mithra.sqllogs.Person - connection:1954406292 find with: select t0.person_id,t0.first_name,t0.last_name,t0.age from person t0 inner join (select distinct t1.owner_id c0 from pet t1 where t1.pet_type_id = 0) as d1 on t0.person_id = d1.c0 2017-07-24 21:51:55:977 [main] DEBUG com.gs.fw.common.mithra.sqllogs.Person - retrieved 1 objects, 347.0 ms per 生成されるSQL
  47. 47. 46 Reladomoの関連:Finderによる柔軟な検索2 reverseRelationshipNameで指定した逆引きのAPI(e.g. owner)も活用可 通常の複数検索と同様 //佐藤さんが飼っているペットを取得 Operation op = PetFinder.owner().lastName().eq("佐藤"); PetList satoPets = PetFinder.findMany(op);
  48. 48. 47 Reladomoの関連:Finderによる柔軟な検索2 2017-07-25 07:09:31:305 [main] DEBUG com.gs.fw.common.mithra.sqllogs.Pet - connection:708533063 find with: select t0.pet_id,t0.name,t0.owner_id,t0.age,t0.pet_type_id from pet t0 inner join person t1 on t0.owner_id = t1.person_id where t1.last_name = '佐藤’ 2017-07-25 07:09:31:316 [main] DEBUG com.gs.fw.common.mithra.sqllogs.Pet - retrieved 2 objects, 49.5 ms per 生成されるSQL
  49. 49. 48 Reladomoの関連:deepFetch Relationshipを解決する際のN+1問題を避けるための機構 //ペット飼っている人を取得 Operation op = PersonFinder.pets().exists(); PersonList petOwners = PersonFinder.findMany(op); petOwners.deepFetch(PersonFinder.pets()); petOwners.deepFetch(PersonFinder.pets().petType()); petOwners.forEach(petOwner -> { petOwner.getPets().forEach(pet -> { System.out.println( petOwner.getLastName() + "さんは" + pet.getName() + "という名の" + pet.getPetType().getPetType() + "を飼っています"); }); }); 取得したい関連を deepFetch指定
  50. 50. 49 Reladomoの関連:deepFetch deepFetchを使わない場合に発行されるSQL 2017-07-25 07:23:05:473 [main] DEBUG com.gs.fw.common.mithra.sqllogs.Person - connection:576020159 find with: select t0.person_id,t0.first_name,t0.last_name,t0.age from person t0 inner join (select distinct t1.owner_id c0 from pet t1) as d1 on t0.person_id = d1.c 02017-07-25 07:23:05:493 [main] DEBUG com.gs.fw.common.mithra.sqllogs.Person - retrieved 2 objects, 51.0 ms per 2017-07-25 07:23:05:604 [main] DEBUG com.gs.fw.common.mithra.sqllogs.Pet - connection:576020159 find with: select t0.pet_id,t0.name,t0.owner_id,t0.age,t0.pet_type_id from pet t0 where t0.owner_id = 1 2017-07-25 07:23:05:608 [main] DEBUG com.gs.fw.common.mithra.sqllogs.Pet - retrieved 2 objects, 6.0 ms per2017- 07-25 07:23:05:645 [main] DEBUG com.gs.fw.common.mithra.sqllogs.PetType - connection:576020159 find with: select t0.pet_type_id,t0.pet_type from pet_type t0 where t0.pet_type_id = 0 2017-07-25 07:23:05:647 [main] DEBUG com.gs.fw.common.mithra.sqllogs.PetType - retrieved 1 objects, 3.0 ms per2017-07-25 07:23:05:658 [main] DEBUG com.gs.fw.common.mithra.sqllogs.PetType - connection:576020159 find with: select t0.pet_type_id,t0.pet_type from pet_type t0 where t0.pet_type_id = 3 2017-07-25 07:23:05:658 [main] DEBUG com.gs.fw.common.mithra.sqllogs.PetType - retrieved 1 objects, 2.0 ms per 2017-07-25 07:23:05:659 [main] DEBUG com.gs.fw.common.mithra.sqllogs.Pet - connection:576020159 find with: select t0.pet_id,t0.name,t0.owner_id,t0.age,t0.pet_type_id from pet t0 where t0.owner_id = 2 2017-07-25 07:23:05:660 [main] DEBUG com.gs.fw.common.mithra.sqllogs.Pet - retrieved 2 objects, 0.5 ms per 2017-07-25 07:23:05:661 [main] DEBUG com.gs.fw.common.mithra.sqllogs.PetType - connection:576020159 find with: select t0.pet_type_id,t0.pet_type from pet_type t0 where t0.pet_type_id = 1 2017-07-25 07:23:05:662 [main] DEBUG com.gs.fw.common.mithra.sqllogs.PetType - retrieved 1 objects, 2.0 ms per 2017-07-25 07:23:05:663 [main] DEBUG com.gs.fw.common.mithra.sqllogs.PetType - connection:576020159 find with: select t0.pet_type_id,t0.pet_type from pet_type t0 where t0.pet_type_id = 2 2017-07-25 07:23:05:664 [main] DEBUG com.gs.fw.common.mithra.sqllogs.PetType - retrieved 1 objects, 1.0 ms per
  51. 51. 50 Reladomoの関連:deepFetch deepFetchを使った場合に発行されるSQL 2017-07-25 07:27:16:540 [main] DEBUG com.gs.fw.common.mithra.sqllogs.Person - connection:587153993 find with: select t0.person_id,t0.first_name,t0.last_name,t0.age from person t0 inner join (select distinct t1.owner_id c0 from pet t1) as d1 on t0.person_id = d1.c0 2017-07-25 07:27:16:564 [main] DEBUG com.gs.fw.common.mithra.sqllogs.Person - retrieved 2 objects, 58.0 ms per 2017-07-25 07:27:16:650 [main] DEBUG com.gs.fw.common.mithra.sqllogs.Pet - connection:587153993 find with: select t0.pet_id,t0.name,t0.owner_id,t0.age,t0.pet_type_id from pet t0 where t0.owner_id in ( 1,2) 2017-07-25 07:27:16:652 [main] DEBUG com.gs.fw.common.mithra.sqllogs.Pet - retrieved 4 objects, 1.75 ms per 2017-07-25 07:27:16:661 [main] DEBUG com.gs.fw.common.mithra.sqllogs.PetType - connection:587153993 find with: select t0.pet_type_id,t0.pet_type from pet_type t0 where t0.pet_type_id in ( 0,1,2,3) 2017-07-25 07:27:16:666 [main] DEBUG com.gs.fw.common.mithra.sqllogs.PetType - retrieved 4 objects, 1.25 ms per
  52. 52. 51 GS Collectionsのネイティブサポート
  53. 53. 52 GS Collectionsサポート:使用例 データ取得後にインメモリーでさまざまなデータ 処理をしたい場合に最適 //苗字を取得 List<String> lastNames = people.asGscList().collect(Person::getLastName); //猫を飼っている飼い主を取得 List<Person> catOwner = people.asGscList().select(person -> person.hasPet(PetType.CAT)); //PetTypeごとに飼い主をグルーピング MutableListMultimap<PetType, Person> peopleByPetType = people.asGscList().groupByEach( person -> person.getPets().asGscList().collect(Pet::getPetType));
  54. 54. 53 あれ、Eclipse Collectionsは?
  55. 55. 54 Eclipse Collections対応  まもなくサポートされる、予定。。。! (今年初めに着手したものの、まだ終わってませんすみません。。。)
  56. 56. 55 JUnitテストサポート
  57. 57. 56 JUnitテストサポート インメモリのh2データベース上にデータを初期化
  58. 58. 57 JUnitテストサポート DBアクセスを伴ったビジネスロジックをユニット テストレベルで担保可能 バグの再現も容易 冪等性の担保のようなロジックもテスト可能
  59. 59. 58 バイテンポラルモデルサポート
  60. 60. 59 バイテンポラルモデルサポート Reladomoのキラーコンテンツとも言える機能 2つの時間概念を同時に扱うことができる –有効時間 –トランザクション時間
  61. 61. 60 バイテンポラルモデル 詳しくはJJUG CCCのプレゼン資料をごらんください
  62. 62. 61 JJUG CCCでは話せなかった「関連」 を絡めたバイテンポラルサポート
  63. 63. 62 田中さん Petサクラ pets owner バイテンポラルモデル:例 - 関連も自由自在 田中さん チビ pets owner 田中さん 1月1日時点ではペットを飼っていなかった田中さん 3月1日から犬のチビを飼い始めました 5月1日から猫のサクラを飼い始めました
  64. 64. 63 バイテンポラルモデルサポート:初期状態 Operation findTanakaOp = PersonFinder.lastName().eq("田中") .and(PersonFinder.businessDate().eq(parse("2017/01/01"))); Person tanaka = PersonFinder.findOne(findTanakaOp); Assert.assertEquals(0, tanaka.getPets().size()); 1月1日時点ではペットを飼っていなかった田中さん
  65. 65. 64 バイテンポラルモデルサポート:3/1付で挿入処理 3月1日から犬のチビを飼い始めました Pet chibi = new Pet(parse("2017/03/01")); chibi.setName("チビ"); chibi.setAge(3); chibi.setPetTypeId(PetType.DOG); chibi.setOwner(tanaka); *このケースでは関連の解決のためsetOwnerの時点でchibiがinsertされます
  66. 66. 65 バイテンポラルモデルサポート:5/1付で挿入処理 5月1日から猫のサクラを飼い始めました Pet sakura = new Pet(parse(“2017/05/01”)); sakura.setName(“サクラ"); sakura.setAge(5); sakura.setPetTypeId(PetType.CAT); sakura.setOwner(tanaka); *このケースでは関連の解決のためsetOwnerの時点でsakuraがinsertされます
  67. 67. 66 2つのPetオブジェクト挿入後の 検索結果を見てみましょう
  68. 68. 67 Operation findTanakaAsOf20170201 = PersonFinder.lastName().eq("田中") .and(PersonFinder.businessDate() .eq(parse("2017/02/01"))); Person tanakaAsOf20170201 = PersonFinder.findOne(findTanakaAsOf20170201); System.out.println(tanakaAsOf20170201.getPets().size()); //0 バイテンポラルモデルサポート:2/1付の検索結果 2月1日付で田中さんはペットの関連を持ちません
  69. 69. 68 Operation findTanakaAsOf20170302 = PersonFinder.lastName().eq("田中") .and(PersonFinder.businessDate() .eq(parse("2017/03/02"))); Person tanakaAsOf20170302 = PersonFinder.findOne(findTanakaAsOf20170302); System.out.println( tanakaAsOf20170302 .getPets() .asGscList() .collect(Pet::getName)); //[チビ] バイテンポラルモデルサポート:3/2付の検索結果 3月2日付で田中さんは「チビ」への関連を持っています
  70. 70. 69 Operation findTanakaAsOf20170502 = PersonFinder.lastName().eq("田中") .and(PersonFinder.businessDate() .eq(parse("2017/05/02"))); Person tanakaAsOf20170502 = PersonFinder.findOne(findTanakaAsOf20170502); System.out.println( tanakaAsOf20170502 .getPets() .asGscList() .collect(Pet::getName)); //[チビ, サクラ] バイテンポラルモデルサポート:5/2付の検索結果 5月2日付で田中さんは「チビ」と「サクラ」への関連を持っています
  71. 71. 70 「関連」を絡めたバイテンポラルモデル において実際にデータやクエリーがどう 表現されているのか気になる方は懇親会 で伊藤とお話ししましょう *そのうちブログに書くかも バイテンポラルモデルサポート:データの表現は割愛
  72. 72. 71 まとめ  Reladomoはオブジェクト指向の極みのようなORM  検索はFinderによる型安全なクエリー  挿入・更新・削除処理も一切SQLを書かずにオブジェクト 上のAPIで完結  複雑なビジネスロジックもJUnit上で安全にテスト  DBクエリはReladomo / インメモリ処理はGS Collections (or Eclipse Collections)  テンポラルデータモデルの扱いが非常に容易
  73. 73. 72 入門を超えて Reladomoは非常に高度な機能を持ち合わせたエンタープライ ズグレードのORMフレームワークです  シャーディングのネイティブサポート  Temp tableを用いたクエリーのサポート  入力集合と出力集合をMulti-threadで比較更新するMulti- Threaded DB Loader  Off-Heapキャッシュ
  74. 74. 73 Reladomoをもっと学ぶには つづきはReladomo Kata / Reladomo Tourで  Reladomo Kata GitHub (Reladomo チュートリアル)  Guided Tour of Reladomo Reladomo Kataは、JUnit上でテストをパスしながら学べる トレーニング教材 本資料を学んだ方はKata内のmini-kataで手を動かして試し てみることをおススメします
  75. 75. 74 告知
  76. 76. 75 Scala関西Summit 2017(9月9日 土) ScalikeJDBCやSkinny Frameworkでおなじみ グッドフロー・テクノロジーズ瀬良さんと共同で Reladomo in Scalaというセッションをします! お楽しみに。
  77. 77. 76 APPENDIX
  78. 78. 77 リンク集  データ履歴管理のためのテンポラルデータモデルと Reladomoの紹介  Reladomo GitHub  Reladomo Kata GitHub (Reladomo チュートリアル)  Guided Tour of Reladomo  Reladomo Documentations

×