Edited at

【Java】Reladomoでデータ履歴管理の実装を簡略化する


Reladomo とは?

reladomo-200.png

Reladomo は、ゴールドマン・サックス社がOSSとして公開している Java ORM フレームワークです。

RDBMS での変更履歴管理が容易になる特徴的な機能があり、結構気に入ってます。

Reladomo では、エンティティを XML で定義することで、Java、 DDL を生成することができるんですが、

生成された API を活用することで、対応するテーブルへの CRUD 操作を行うことができます。


この記事でやること

今回は、Reladomo の基本操作、データモデルの種類などを簡単に触れています。

その際、バイテンポラルデータモデル(後述)については、実物の挙動を見た方がわかりやすいと思うので、

Spring Boot と組み合わせた REST API のサンプルを用いて、見ていきたいと思います。

https://github.com/amtkxa/spring-boot-reladomo


Reladomo によるデータ履歴管理について

データの変更履歴を追跡できるようにしておくためには、履歴データを保存する必要があり、

何らかの意味を持つ時間も一緒に保存する必要があります。


Transaction Time, Vaild Time

Reladomo を活用すると、データの変更履歴を追跡するための2つの時間の管理を容易に行うことができます。


  • Vaild Time (Business Time とも呼ばれる)


    • 現実世界で発生した時間。



  • Transaction Time (Processing Time とも呼ばれる)


    • システムに変更が加えられた時間。




データモデル

Transaction Time, Vaild Time を使うか使わないかで、以下の4つのデータモデルが考えられます。

Reladomo を使用すると、エンティティ別にデータモデルを選択するような柔軟な設計も可能です。

データモデル
説明

スナップショットデータモデル
どの時間ディメンションもサポートされない(履歴管理なし)

トランザクション時間データモデル
Transaction Time のみサポート

有効時間データモデル
Vaild Time のみサポート

バイテンポラルデータモデル
Vaild Time と Transaction Time の両方をサポート


実際に使ってみる(トランザクション時間データモデル)

まずは、Reladomo の基本的な使い方に触れながら、トランザクション時間データモデルを扱ってみようと思います。


簡単なドメインモデルの用意

Reladomo の機能を確認することが目的なので、それが実現できそうな簡単なドメインモデルを用意しました。

50054750-b2e45800-0188-11e9-8aeb-fad5262e78d8.png


Reladomo オブジェクトの XML ファイル作成

まず、データベースのテーブルに対応するオブジェクトを定義するXMLファイルを作成します。


Account.xml

<MithraObject xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

objectType="transactional"
xsi:noNamespaceSchemaLocation="../config/mithraobject.xsd">
<PackageName>com.amtkxa.springbootreladomosimple.domain.entity</PackageName>
<ClassName>Account</ClassName>
<DefaultTable>ACCOUNT</DefaultTable>

<AsOfAttribute name="processingDate" fromColumnName="PROCESSING_DATE_FROM" toColumnName="PROCESSING_DATE_TO"
toIsInclusive="false"
isProcessingDate="true"
infinityDate="[com.gs.fw.common.mithra.util.DefaultInfinityTimestamp.getDefaultInfinity()]"
defaultIfNotSpecified="[com.gs.fw.common.mithra.util.DefaultInfinityTimestamp.getDefaultInfinity()]"
/>
<Attribute name="accountId" javaType="int" columnName="ACCOUNT_ID" primaryKey="true"
primaryKeyGeneratorStrategy="SimulatedSequence">
<SimulatedSequence sequenceName="Account"
sequenceObjectFactoryName="com.amtkxa.springbootreladomosimple.infrastructure.util.ObjectSequenceObjectFactory"
hasSourceAttribute="false"
batchSize="1"
initialValue="1"
incrementSize="1"/>
</Attribute>
<Attribute name="customerId" javaType="int" columnName="CUSTOMER_ID" nullable="false"/>
<Attribute name="balance" javaType="double" columnName="BALANCE"/>
</MithraObject>


各属性について簡単に説明すると、下記のような内容になっています。

Element
説明

PackageName
生成されるオブジェクトが属するパッケージ。

ClassName
オブジェクトのクラス名。

DefaultTable
オブジェクトが対応するテーブル。

Attribute
オブジェクトの属性。DefaultTable で定義したテーブルのカラムに対応している。

AsOfAttribute
履歴管理用のカラムに対応しており、from から to の2カラムで期間を表す。

SimulatedSequence
INSERT 時に自動採番するシーケンス値。

また、ここで設定している内容に関する詳細については、mithraobject.xsd を参照すると確認できます。


Java ファイル生成

先ほど定義した XML ファイルから、対応するオブジェクトの Java ファイルを生成することができます。


MithraClassList の作成


MithraClassList.xml

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>

<Mithra xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../config/mithraobject.xsd">
<MithraObjectResource name="ObjectSequence"/>
<MithraObjectResource name="Customer"/>
<MithraObjectResource name="Account"/>
<MithraObjectResource name="Transaction"/>
</Mithra>


gen-reladomo タスクの定義

pom.xml に、gen-reladomo タスクを定義します。この Ant タスクは、MithraClassList.xml ファイルを基に、MithraGeneratorを使用してJavaファイルを生成します。


pom.xml

<tasks>

<property name="plugin_classpath" refid="maven.plugin.classpath"/>
<taskdef name="gen-reladomo" classpath="plugin_classpath"
classname="com.gs.fw.common.mithra.generator.MithraGenerator"/>
<gen-reladomo
xml="${project.basedir}/src/main/resources/reladomo/model/MithraClassList.xml"
generateGscListMethod="true"
generatedDir="${project.build.directory}/generated-sources/reladomo"
nonGeneratedDir="${project.basedir}/src/main/java"
/>
</tasks>

ここでは、generatedDirnonGeneratedDir で2箇所出力先を定義しています。



  • generatedDir



    • gen-reladomo タスクが実行されるたびに、再作成する。

    • CRUD 操作などを実行するために必要なコードが含まれる抽象クラスとユーティリティクラスが生成される。

    • 変更やバージョン管理をするべきではないクラス。




  • nonGeneratedDir



    • gen-reladomo タスクが実行されても、生成するクラスが既に存在していれば再作成しない。


    • generatedDir に生成されたクラスに依存する具象クラスが生成される。初期状態では、最低限必要なコンストラクタのみ記載されており、ビジネスロジックなどの実装はこちらに行う。

    • バージョン管理するべきクラス。




gen-reladomo タスクの実行

mvn clean install などを実行することで、gen-reladomo タスクにより Java ファイルが生成されます。

generatedDir には、XML の変更に追従するために毎回再作成する抽象クラス、nonGeneratedDir には、ユーザが独自にビジネスロジックを追記する具象クラスが生成されます。生成される各クラスと依存関係は、以下のようになります。

FireShot Capture 21 - spring-boot-relado_ - https___app.genmymodel.com_edit__alkjIAXvEemAl9N48If50Q#.png

ここで生成される抽象クラスには、エンティティに対してCRUD操作を実行するために必要なコードの大部分が含まれています。


  • AccountAbstract


    • 単一レコードに対応したクラス。



  • AccountListAbstract


    • 複数レコードに対応したクラス。



  • AccountDatabaseObjectAbstract


    • コネクションやテーブルに関する情報が含まれるクラス。



  • AccountFinder


    • 検索時に条件の指定などを行う処理が含まれるクラス。




DDL生成

Reladomo で対応しているデータベースの DDL を生成することができます。DDL は、テーブル、インデックス、外部キーに対応しているようです。


gen-reladomo-db タスクの定義

pom.xml に、gen-reladomo-db タスクを定義します。この Ant タスクは、MithraClassList.xml ファイルを基に、MithraDbDefinitionGenerator を使用して DDL を生成します。


pom.xml

<tasks>

<property name="plugin_classpath" refid="maven.plugin.classpath"/>
<taskdef name="gen-reladomo-db" classpath="plugin_classpath"
classname="com.gs.fw.common.mithra.generator.dbgenerator.MithraDbDefinitionGenerator"/>
<gen-reladomo-db
xml="${project.basedir}/src/main/resources/reladomo/model/MithraClassList.xml"
generatedDir="${project.build.directory}/generated-resources/db"
databaseType="postgres"/>
</tasks>


gen-reladomo-db タスクの実行

mvn clean install などを実行することで、gen-reladomo-db タスクにより DDL が生成されます。

databaseTypepostgres と設定した場合に生成された DDL は、下記の内容になっていました。


target/generated-resources/db/ACCOUNT.ddl

drop table if exists ACCOUNT;

create table ACCOUNT
(
ACCOUNT_ID int not null,
CUSTOMER_ID int not null,
BALANCE float8,
PROCESSING_DATE_FROM timestamp not null,
PROCESSING_DATE_TO timestamp not null
);



target/generated-resources/db/ACCOUNT.idx

alter table ACCOUNT add constraint ACCOUNT_PK primary key (ACCOUNT_ID, PROCESSING_DATE_TO);

drop index if exists ACCOUNT_IDX0;
create index ACCOUNT_IDX0 on ACCOUNT(CUSTOMER_ID, PROCESSING_DATE_TO);


Reladomo を用いて履歴管理をする場合、該当の XML からの Foreign Key DDL は生成されないようです。

ちなみに、ここで生成される DDL はそのまま使用するものではなく、これを基にカスタマイズすることが想定されているようです。


The DDLs generated by Reladomo are not meant to be used as is. They are merely meant to be a starting point for further customization. There are two common reasons to customize the generated DDLs. One is to add database specific clauses for specifying table spaces, schemas etc. Another is to add additional indices based on usage patterns seen in the application.

Guided Tour Of Reladomo - 2.3. Database DDL Generation


DDL の生成機能はありがたいですね。


Reladomo の CRUD 操作


CRUD (Create)


単一レコードの登録

Reladomo により生成された具象クラスに、次のようなコンストラクタを追加します。


Account.java

public Account(int customerId, double balance) {

this(com.gs.fw.common.mithra.util.DefaultInfinityTimestamp.getDefaultInfinity());
this.setCustomerId(customerId);
this.setBalance(balance);
}

その後、下記のコードを実行してみます。

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

Account account = new Account(1, 100);
account.insert();
return null;
});

すると、接続先DBの ACCOUNT テーブルにレコードが登録されました。

testdb=> select * from account;

account_id | customer_id | balance | processing_date_from | processing_date_to
------------+-------------+---------+------------------------+---------------------
1 | 1 | 100 | 2018-12-23 14:42:14.61 | 9999-12-01 23:59:00


シーケンス値の採番

値を設定していない account_id には、採番されたシーケンス値がセットするようにしているので、そこの補足をします。

先ほど定義した Account.xml の該当列に、下記のような記載をするとシーケンス値の採番もできるようになります。


Account.xml

    <Attribute name="accountId" javaType="int" columnName="ACCOUNT_ID" primaryKey="true"

primaryKeyGeneratorStrategy="SimulatedSequence">
<SimulatedSequence sequenceName="Account"
sequenceObjectFactoryName="com.amtkxa.springbootreladomosimple.infrastructure.util.ObjectSequenceObjectFactory"
hasSourceAttribute="false"
batchSize="1"
initialValue="1"
incrementSize="1"/>
</Attribute>

Account.xml で指定している ObjectSequenceObjectFactory は別途作成しています。


ObjectSequenceObjectFactory.java

public class ObjectSequenceObjectFactory implements MithraSequenceObjectFactory {

public MithraSequence getMithraSequenceObject(String sequenceName, Object sourceAttribute, int initialValue) {
ObjectSequence objectSequence = ObjectSequenceFinder.findByPrimaryKey(sequenceName);
if (objectSequence == null) {
objectSequence = new ObjectSequence();
objectSequence.setSimulatedSequenceName(sequenceName);
objectSequence.setNextValue(initialValue);
objectSequence.insert();
}
return objectSequence;
}
}


採番する値は、OBJECT_SEQUENCE テーブルにて管理されていて、これも XML にてメタデータを定義しています。


ObjectSequence.xml

<MithraObject xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

objectType="transactional"
xsi:noNamespaceSchemaLocation="../config/mithraobject.xsd">

<PackageName>com.amtkxa.springbootreladomosimple.domain.entity</PackageName>
<ClassName>ObjectSequence</ClassName>
<DefaultTable>OBJECT_SEQUENCE</DefaultTable>

<Attribute name="simulatedSequenceName" javaType="String" columnName="SEQUENCE_NAME" primaryKey="true" maxLength="64"/>
<Attribute name="nextValue" javaType="long" columnName="NEXT_VALUE"/>

</MithraObject>


testdb=> select * from object_sequence;

sequence_name | next_value
---------------+------------
Account | 2


複数レコードの登録

Reladomo により生成された AccountList を使用することで、複数レコードの登録も可能です。

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

AccountList accountList = new AccountList();
accountList.add(new Account(2, 200));
accountList.add(new Account(3, 300));
accountList.insertAll();
return null;
});

このコードを実行すると、複数レコード登録されました。

testdb=> select * from account where customer_id in (2, 3);

account_id | customer_id | balance | processing_date_from | processing_date_to
------------+-------------+---------+------------------------+---------------------
2 | 2 | 200 | 2018-12-23 15:10:37.92 | 9999-12-01 23:59:00
3 | 3 | 300 | 2018-12-23 15:10:37.92 | 9999-12-01 23:59:00


CRUD (Read)

検索処理は、Reladomo で生成された Finder クラスを利用します。


検索条件の設定

Finder クラスを利用することで、Operation クラスのインスタンスを生成することができます。

Operation クラスは、発行されるクエリの WHERE 句となる部分を設定することができます。

いくつか例を挙げてみます。

// where NAME = 'tanaka'

Operation op1 = CustomerFinder.name().eq("tanaka");
// where NAME <> 'tanaka'
Operation op2 = CustomerFinder.name().notEq("tanaka");
// where NAME like 'tanaka%'
Operation op3 = CustomerFinder.name().startsWith("tanaka");
// where NAME like '%taro'
Operation op4 = CustomerFinder.name().endsWith("taro");
// where NAME like '%tanaka%'
Operation op5 = CustomerFinder.name().wildCardEq("*tanaka*");
// where CUSTOMER_ID < 3
Operation op6 = CustomerFinder.customerId().lessThan(3);

ここであげている他にも、isNullcontains などが用意されています。

また、以下のように Operation を連鎖させることもできます。

// where NAME like '%tanaka%' and CUSTOMER_ID < 3

Operation op = op5.and(op6);


1件検索

Finder クラスの findOne を利用することで、単一レコードを取得することができます。

// select t0.ACCOUNT_ID,t0.CUSTOMER_ID,t0.BALANCE,t0.PROCESSING_DATE_FROM,t0.PROCESSING_DATE_TO from ACCOUNT t0 

// where t0.ACCOUNT_ID = 1 and t0.PROCESSING_DATE_TO = '9999-12-01 23:59:00.000'
Account account = AccountFinder.findOne(AccountFinder.accountId().eq(1));

実際に発行されたクエリの条件に、t0.PROCESSING_DATE_TO = '9999-12-01 23:59:00.000'とありますが、これは最新履歴に該当するレコードを指すものです。


複数検索

Finder クラスの findMany を利用した場合は、複数レコードを List で取得することができます。

// select t0.CUSTOMER_ID,t0.NAME,t0.COUNTRY,t0.PROCESSING_DATE_FROM,t0.PROCESSING_DATE_TO from CUSTOMER t0

// where t0.NAME like 'tanaka%' and t0.PROCESSING_DATE_TO = '9999-12-01 23:59:00.000'
CustomerList customerList = CustomerFinder.findMany(CustomerFinder.name().startsWith("tanaka"));


CRUD (Update)

Finder クラスを使って取得したオブジェクトに対して変更を加えると、変更内容が DB のテーブルにも反映されます。

挙動を確認するために、こんなデータを用意しました。

testdb=> select * from customer where customer_id = 7;

customer_id | name | country | processing_date_from | processing_date_to
-------------+---------+---------+------------------------+---------------------
7 | Pikachu | Japan | 2018-12-23 19:36:07.06 | 9999-12-01 23:59:00

ピカチュウさんがライチュウさんに変わるので、変更を加えるために下記のコードが実行されたとします。

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

Customer customer = CustomerFinder.findOne(CustomerFinder.customerId().eq(7));
customer.setName("Raichu");
return null;
});

実行結果は、以下のようになりました。

testdb=> select * from customer where customer_id = 7;

customer_id | name | country | processing_date_from | processing_date_to
-------------+---------+---------+------------------------+------------------------
7 | Pikachu | Japan | 2018-12-23 19:36:07.06 | 2018-12-23 19:46:13.91
7 | Raichu | Japan | 2018-12-23 19:46:13.91 | 9999-12-01 23:59:00

実際に発行されたクエリは、下記の通りです。

-- 最新履歴のデータを取得

select t0.CUSTOMER_ID,t0.NAME,t0.COUNTRY,t0.PROCESSING_DATE_FROM,t0.PROCESSING_DATE_TO from CUSTOMER t0
where t0.CUSTOMER_ID = 7 and t0.PROCESSING_DATE_TO = '9999-12-01 23:59:00.000' FOR SHARE OF t0;

-- 最新履歴のデータの有効期間を更新
update CUSTOMER set PROCESSING_DATE_TO = '2018-12-23 19:46:13.910'
where CUSTOMER_ID = 7 AND PROCESSING_DATE_TO = '9999-12-01 23:59:00.000';

-- 最新履歴として、変更後のデータを登録
insert into CUSTOMER(CUSTOMER_ID,NAME,COUNTRY,PROCESSING_DATE_FROM,PROCESSING_DATE_TO)
values (7,'Raichu','Japan','2018-12-23 19:46:13.910','9999-12-01 23:59:00.000');


CRUD (Delete)

Finder クラスを使って取得したオブジェクトを削除することで、テーブルにも反映することができます。

その場合、delete(), terminate(), purge() でそれぞれ挙動が異なるので、簡単に動かしてみます。


delete()

delete() は、Reladomo で履歴管理をしないオブジェクト(XML に AsOfAttribute があるかにより判断)に対して使用できます。

これを実行すると、テーブルからレコードが物理削除されます。

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

Transaction transaction = TransactionFinder.findOne(TransactionFinder.transactionId().eq(4));
transaction.delete();
return null;
});

実際に実行されたクエリは、以下の内容です。

select t0.LOG_ID,t0.ACCOUNT_ID,t0.TRANSACTION_TYPE,t0.AMOUNT,t0.TRANSACTION_DATE from TRANSACTION t0 

where t0.LOG_ID = 4 FOR SHARE OF t0;

delete from TRANSACTION where LOG_ID = 4;


terminate()

terminate() は、Reladomo で履歴管理をするオブジェクトの履歴を終了させます。(履歴としてのレコードは残る)

挙動を確認するために、以下のデータを用意しました。

testdb=> select * from customer where customer_id = 7;

customer_id | name | country | processing_date_from | processing_date_to
-------------+---------+---------+------------------------+------------------------
7 | Pikachu | Japan | 2018-12-23 19:36:07.06 | 2018-12-23 19:46:13.91
7 | Raichu | Japan | 2018-12-23 19:46:13.91 | 9999-12-01 23:59:00

それに対して、このコードを実行します。

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

Customer customer = CustomerFinder.findOne(CustomerFinder.customerId().eq(7));
customer.terminate();
return null;
});

すると、processing_date_to9999-12-01 23:59:00 と登録されていた箇所が更新されます。

testdb=> select * from customer where customer_id = 7;

customer_id | name | country | processing_date_from | processing_date_to
-------------+---------+---------+------------------------+------------------------
7 | Pikachu | Japan | 2018-12-23 19:36:07.06 | 2018-12-23 19:46:13.91
7 | Raichu | Japan | 2018-12-23 19:46:13.91 | 2018-12-23 22:10:36.68

実際に実行されたクエリは、以下の内容です。

select t0.CUSTOMER_ID,t0.NAME,t0.COUNTRY,t0.PROCESSING_DATE_FROM,t0.PROCESSING_DATE_TO from CUSTOMER t0 

where t0.CUSTOMER_ID = 7 and t0.PROCESSING_DATE_TO = '9999-12-01 23:59:00.000' FOR SHARE OF t0;

update CUSTOMER set PROCESSING_DATE_TO = '2018-12-23 22:10:36.680'
where CUSTOMER_ID = 7 AND PROCESSING_DATE_TO = '9999-12-01 23:59:00.000';


purge()

purge() は、Reladomo で履歴管理をするオブジェクトの履歴そのものを削除します。

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

Customer customer = CustomerFinder.findOne(CustomerFinder.customerId().eq(6));
customer.purge();
return null;
});

実際に実行されたクエリは、以下の内容です。

select t0.CUSTOMER_ID,t0.NAME,t0.COUNTRY,t0.PROCESSING_DATE_FROM,t0.PROCESSING_DATE_TO from CUSTOMER t0

where t0.CUSTOMER_ID = 6 and t0.PROCESSING_DATE_TO = '9999-12-01 23:59:00.000' FOR SHARE OF t0;

delete from CUSTOMER where CUSTOMER_ID = 6;


実際に使ってみる(バイテンポラルデータモデル)

Spring Boot と組み合わせてバイテンポラルデータモデルを扱う REST API のサンプルを使います。

https://github.com/amtkxa/spring-boot-reladomo

バイテンポラルデータモデルで扱う Transaction Time, Vaild Time は、同じタイミングになることも多いのですが、ここではその2つを同時に管理するメリットがわかるケースを辿ってみます。


  • このケースは、Guided Tour Of Reladomo で扱っているものとほぼ同じ内容です。

  • processing_date には、私が操作してシステムに反映された時間で履歴が登録されています。

Reladomo の基本的な使い方は先ほど触れているので、あまり被らない部分を紹介します。


1月1日 - 口座を初回入金額10000円で開設

一番最初の履歴レコードが登録されました。

sampledb=> select * from account where account_id = 10;

account_id | customer_id | balance | business_date_from | business_date_to | processing_date_from | processing_date_to
------------+-------------+---------+---------------------+---------------------+------------------------+---------------------
12 | 1 | 10000 | 2019-01-01 00:00:00 | 9999-12-01 23:59:00 | 2018-12-22 16:17:17.49 | 9999-12-01 23:59:00


1月17日 - 5000円入金

何らかのトラブルにより、入金できていなかったとします。

そのため、テーブル内容に変更はありません。


1月20日 - 20000円入金

20000円入金されたことにより、履歴が切られました。

sampledb=> select * from account where account_id = 12 order by 4, 6;

account_id | customer_id | balance | business_date_from | business_date_to | processing_date_from | processing_date_to
------------+-------------+---------+---------------------+---------------------+------------------------+------------------------
12 | 1 | 10000 | 2019-01-01 00:00:00 | 9999-12-01 23:59:00 | 2018-12-22 16:33:09.54 | 2018-12-22 16:38:27.48
12 | 1 | 10000 | 2019-01-01 00:00:00 | 2019-01-20 00:00:00 | 2018-12-22 16:38:27.48 | 9999-12-01 23:59:00
12 | 1 | 30000 | 2019-01-20 00:00:00 | 9999-12-01 23:59:00 | 2018-12-22 16:38:27.48 | 9999-12-01 23:59:00


  • Reladomo で生成された Account クラスの incrementBalance() を使った実装にしていると、入金額20000円が残高に加算される挙動になるよう、該当テーブルが更新されます。


  • processing_date_toprocessing_date_to9999-12-01 23:59:00 のレコードが最新履歴です。


1月25日 - 入金できていなかった5000円の修正

1月17日 に入金していた分の金額の修正が、1月25日に発生したとします。

この場合、business_date に1月17日を指定して取得したオブジェクトに対し、incrementBalance() を使用して更新処理を行うと、該当テーブルが下記の内容に更新されます。

sampledb=> select * from account where account_id = 12 order by 6, 4;

account_id | customer_id | balance | business_date_from | business_date_to | processing_date_from | processing_date_to
------------+-------------+---------+---------------------+---------------------+------------------------+------------------------
12 | 1 | 10000 | 2019-01-01 00:00:00 | 9999-12-01 23:59:00 | 2018-12-22 16:33:09.54 | 2018-12-22 16:38:27.48
12 | 1 | 10000 | 2019-01-01 00:00:00 | 2019-01-20 00:00:00 | 2018-12-22 16:38:27.48 | 2018-12-22 16:45:31.94
12 | 1 | 30000 | 2019-01-20 00:00:00 | 9999-12-01 23:59:00 | 2018-12-22 16:38:27.48 | 2018-12-22 16:45:31.94
12 | 1 | 10000 | 2019-01-01 00:00:00 | 2019-01-17 00:00:00 | 2018-12-22 16:45:31.94 | 9999-12-01 23:59:00
12 | 1 | 15000 | 2019-01-17 00:00:00 | 2019-01-20 00:00:00 | 2018-12-22 16:45:31.94 | 9999-12-01 23:59:00
12 | 1 | 35000 | 2019-01-20 00:00:00 | 9999-12-01 23:59:00 | 2018-12-22 16:45:31.94 | 9999-12-01 23:59:00

修正前の履歴データを保持したまま、修正が必要な履歴データの残高が更新できていることを確認できました。


最後に

Reladomo を実際に使ってみて、データ履歴管理が容易に実現できることを実感しました。

監査などのために変更履歴を後から追跡したいケースなどでは特に、開発効率の向上が期待できそうなので、

JVM 言語を利用する開発においては、役立つ場面が多いかもしれません。

また、エンティティごとのキャッシュ戦略選択や、Multi-Threaded matcher Loader (MTLoader) など、

Reladomo で提供されている高パフォーマンスを実現するための機能は特に気になっているので、

そちらも別の機会に調べたいと思います。


参考にさせて頂いた資料

今回調べてみて、英語のドキュメントが多いように感じましたが、質の高いものばかりでした。

また、とてもわかりやすい日本語の資料もあり、本当に参考になりました。