reladomo入門 jjugナイトセミナー #jjug

78
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.

Upload: hiroshi-ito

Post on 29-Jan-2018

3.552 views

Category:

Technology


1 download

TRANSCRIPT

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.

1

Agenda

1. 自己紹介

2. Reladomoの基本

3. Reladomoのコード生成

4. Reladomoの検索・挿入・更新・削除処理

5. Reladomoの関連

6. GS Collectionsサポート

7. ユニットテストサポート

8. バイテンポラルモデル

2

自己紹介

趣味:ドラム演奏

JavaOneコミュニティバンド

Null Pointersで演奏経験あり(日本人初)

Head of Engineering @ FOLIO

伊藤 博志

Eclipse Collections:共同プロジェクトリード兼コミッターReladomo:コントリビューターOpenJDK:コントリビューターJJUG CCC、Java Day Tokyo、JavaOne San Francisco登壇

2017年5月に株式会社FOLIO入社。

3

Reladomoの基本

4

Reladomoとは

https://github.com/goldmansachs/reladomo

ゴールドマン・サックス社が2016年9月にGitHubにOSSとして公開したJava ORMフレームワーク

Apache License 2.0

ORMフレームワークであり、オブジェクト指向の徹底

xmlからコード/DDLの自動生成

バイテンポラルデータモデルをネイティブサポート

強力に型付けられたクエリー言語(SQLは書かない)

ユニットテストのフルサポート

etc.

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]

6

Reladomoのコード生成

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>

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(Operation)

–複数検索:Finder.findMany(Operation)

17

Finder API:一件検索

Operation findTaroOp = PersonFinder.firstName().eq(“太郎");

Person john = PersonFinder.findOne(findTaroOp);

Finder APIを用いてOperationを作成

Finder.findOne()で一件検索結果はEntityオブジェクトで取得

18

Finder API:複数検索

Operation findAllOp = PersonFinder.all();

PersonList people = PersonFinder.findMany(findAllOp);

Finder APIを用いてOperationを作成

Finder.findMany()で複数検索結果はListオブジェクトで取得

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 '%藤'))

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

その他複雑なクエリを型安全に記述することが可能

21

Reladomoのキャッシュ

22

ReIadomoのキャッシュ

Reladomoは複数のキャッシュ戦略を持つ

–Partial Cache

–Full Cache

–None

ランタイムの設定でエンティティごとにキャッシュ戦略を選択可能

クエリーキャッシュとオブジェクトキャッシュ

キャッシュ機構は高度に最適化されており、データベースへのアクセスを最小に保つことができる

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のキャッシュ

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 per2017-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 per2017-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 > 252017-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アクセス

25

Operation findAllOp = PersonFinder.all();PersonList people = PersonFinder.findMany(findAllOp);people.forceResolve(); //Reladomoは通常遅延ロードするので例示のために強制ロード

Operation findTaroOp =PersonFinder.firstName().eq("太郎");

… 前ページと同じ3つのクエリー

前ページと同じ3種類のクエリーの直前に全選択のクエリーを走らせると…

ReIadomoのキャッシュ

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回のみ

その後のクエリーはキャッシュから取得

27

Reladomoのトランザクション処理

28

Reladomoのトランザクション処理

トランザクション内で行う必要のある処理に関してはラムダ式内に記述しexecuteTransactionalCommand()

にわたす。以下、例によってはこのトランザクションのコードを省略している。

MithraManagerProvider.getMithraManager().executeTransactionalCommand(tx -> {

//検索・挿入・更新・削除処理の記述

return someObject;});

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("さくら", "鈴木", 28));

newPeople.insertAll();

Reladomoの挿入処理:バッチ挿入

Listインスタンスを作成

新規作成したEntityをListに追加

InsertAll()メソッドで複数挿入

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は永続化されたオブジェクトはメモリ上に唯一つのみ存在することを保証する

33

Reladomoの更新処理

34

Operation tanakaOp = PersonFinder.lastName().eq("田中");Person tanaka = PersonFinder.findOne(tanakaOp);

tanaka.setAge(25);

Reladomoの更新処理:単一更新

Entityインスタンスを検索

setXxx()メソッドで更新*トランザクション外での更新は即時反映される

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)

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 = 02017-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

37

Reladomoの削除処理

38

Reladomoの削除処理:単一削除

Entityインスタンスを検索

delete()メソッドで削除

Operation op = PersonFinder.lastName().eq("田中");Person tanaka = PersonFinder.findOne(op);

tanaka.delete();

39

Reladomoの削除処理:複数削除

複数検索でListオブジェクトを取得

Listに対してdeleteAll()メソッドで複数削除

Operation op = PersonFinder.lastName().in(Sets.mutable.of(“田中”, “佐藤"));

PersonList people = PersonFinder.findMany(op);

people.deleteAll();

40

Reladomoの関連

41

Reladomoの関連:サンプルモデル

42

Reladomoの関連:Person.xml

<Relationship name="pets"relatedObject="Pet"cardinality="one-to-many"relatedIsDependent="true"reverseRelationshipName="owner">

this.personId = Pet.ownerId</Relationship>

Person PetPetPetpets

owner

43

Reladomoの関連:Pet.xml

<Relationship name="petType"relatedObject="PetType"cardinality="many-to-one">

this.petTypeId = PetType.petTypeId</Relationship>

Pet PetTypepetType

44

Reladomoの関連:Finderによる柔軟な検索1

//犬を飼っている飼い主を取得Operation op =

PersonFinder.pets().petTypeId().eq(PetType.DOG);

Person dogOwner = PersonFinder.findOne(op);

テーブル間のjoinを柔軟に記述

通常の一件検索と同様

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.c02017-07-24 21:51:55:977 [main] DEBUG com.gs.fw.common.mithra.sqllogs.Person - retrieved 1 objects, 347.0 ms per

生成されるSQL

46

Reladomoの関連:Finderによる柔軟な検索2

reverseRelationshipNameで指定した逆引きのAPI(e.g. owner)も活用可

通常の複数検索と同様

//佐藤さんが飼っているペットを取得Operation op =

PetFinder.owner().lastName().eq("佐藤");

PetList satoPets = PetFinder.findMany(op);

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

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指定

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.c02017-07-25 07:23:05:493 [main] DEBUG com.gs.fw.common.mithra.sqllogs.Person - retrieved 2 objects, 51.0 ms per2017-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 = 12017-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 = 02017-07-25 07:23:05:647 [main] DEBUG com.gs.fw.common.mithra.sqllogs.PetType - retrieved 1 objects, 3.0 msper2017-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 = 32017-07-25 07:23:05:658 [main] DEBUG com.gs.fw.common.mithra.sqllogs.PetType - retrieved 1 objects, 2.0 ms per2017-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 = 22017-07-25 07:23:05:660 [main] DEBUG com.gs.fw.common.mithra.sqllogs.Pet - retrieved 2 objects, 0.5 ms per2017-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 = 12017-07-25 07:23:05:662 [main] DEBUG com.gs.fw.common.mithra.sqllogs.PetType - retrieved 1 objects, 2.0 ms per2017-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 = 22017-07-25 07:23:05:664 [main] DEBUG com.gs.fw.common.mithra.sqllogs.PetType - retrieved 1 objects, 1.0 ms per

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.c02017-07-25 07:27:16:564 [main] DEBUG com.gs.fw.common.mithra.sqllogs.Person - retrieved 2 objects, 58.0 ms per2017-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 per2017-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

51

GS Collectionsのネイティブサポート

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));

53

あれ、Eclipse Collectionsは?

54

Eclipse Collections対応

まもなくサポートされる、予定。。。!

(今年初めに着手したものの、まだ終わってませんすみません。。。)

55

JUnitテストサポート

56

JUnitテストサポート

インメモリのh2データベース上にデータを初期化

57

JUnitテストサポート

DBアクセスを伴ったビジネスロジックをユニットテストレベルで担保可能

バグの再現も容易

冪等性の担保のようなロジックもテスト可能

58

バイテンポラルモデルサポート

59

バイテンポラルモデルサポート

Reladomoのキラーコンテンツとも言える機能

2つの時間概念を同時に扱うことができる

–有効時間

–トランザクション時間

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("2017/01/01")));

Person tanaka = PersonFinder.findOne(findTanakaOp);

Assert.assertEquals(0, tanaka.getPets().size());

1月1日時点ではペットを飼っていなかった田中さん

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されます

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されます

66

2つのPetオブジェクト挿入後の

検索結果を見てみましょう

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日付で田中さんはペットの関連を持ちません

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日付で田中さんは「チビ」への関連を持っています

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日付で田中さんは「チビ」と「サクラ」への関連を持っています

70

「関連」を絡めたバイテンポラルモデルにおいて実際にデータやクエリーがどう表現されているのか気になる方は懇親会

で伊藤とお話ししましょう

*そのうちブログに書くかも

バイテンポラルモデルサポート:データの表現は割愛

71

まとめ

Reladomoはオブジェクト指向の極みのようなORM

検索はFinderによる型安全なクエリー

挿入・更新・削除処理も一切SQLを書かずにオブジェクト上のAPIで完結

複雑なビジネスロジックもJUnit上で安全にテスト

DBクエリはReladomo / インメモリ処理はGS Collections

(or Eclipse Collections)

テンポラルデータモデルの扱いが非常に容易

72

入門を超えて

Reladomoは非常に高度な機能を持ち合わせたエンタープライズグレードのORMフレームワークです

シャーディングのネイティブサポート

Temp tableを用いたクエリーのサポート

入力集合と出力集合をMulti-threadで比較更新するMulti-

Threaded DB Loader

Off-Heapキャッシュ

73

Reladomoをもっと学ぶには

つづきはReladomo Kata / Reladomo Tourで

Reladomo Kata GitHub (Reladomo チュートリアル)

Guided Tour of Reladomo

Reladomo Kataは、JUnit上でテストをパスしながら学べるトレーニング教材

本資料を学んだ方はKata内のmini-kataで手を動かして試してみることをおススメします

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 Reladomo

Reladomo Documentations