レラドモの紹介
1. 概要
Goldman Sachsで開発されたReladomo (formerly known as Mithra) is an object-relational mapping (ORM) framework for Javaは、現在オープンソースプロジェクトとしてリリースされています。 このフレームワークは、ORMに一般的に必要な機能といくつかの追加機能を提供します。
Reladomoの主な機能のいくつかを見てみましょう。
-
JavaクラスとDDLスクリプトを生成できます
-
XMLファイルに記述されたメタデータによって駆動されます
-
生成されたコードは拡張可能です
-
クエリ言語はオブジェクト指向であり、強く型付けされています
-
フレームワークはシャーディングのサポートを提供します(同じスキーマ、異なるデータセット)
-
テストのサポートも含まれています
-
パフォーマンスのキャッシュやトランザクションなどの便利な機能を提供します
次のセクションでは、セットアップといくつかの基本的な使用例を示します。
2. Mavenセットアップ
ORMの使用を開始するには、reladomo依存関係をpom.xmlファイルに追加する必要があります。
com.goldmansachs.reladomo
reladomo
16.5.1
例ではH2データベースを使用するので、h2依存関係も追加しましょう。
com.h2database
h2
1.4.196
In addition to this, we need to setup plugins that will generate classes and SQL filesを実行し、実行中にロードします。
ファイルの生成には、maven-antrun-pluginを使用して実行されるタスクを使用できます。 まず、Javaクラスを生成するためのタスクを定義する方法を見てみましょう。
maven-antrun-plugin
generateMithra
generate-sources
run
The gen-reladomo task uses the provided MithraGenerator to create Java files based on the configuration in ReladomoClassList.xml file.このファイルに含まれる内容については、後のセクションで詳しく見ていきます。
タスクには、生成されたファイルの場所を定義する2つのプロパティもあります。
-
generatedDir –変更またはバージョン管理してはならないクラスが含まれています
-
nonGeneratedDir –さらにカスタマイズおよびバージョン管理できる生成された具象オブジェクトクラス
Javaオブジェクトに対応するデータベーステーブルは、2番目のAntタスクによって生成されたDDLスクリプトを使用して、手動または自動で作成できます。
このタスクは、前述の同じReladomoClassList.xmlファイルに基づくMithraDbDefinitionGeneratorを使用します。 SQLスクリプトはgenerated-db/sqlディレクトリに配置されます。
このプラグインの定義を完了するには、作成に使用される2つの依存関係も追加する必要があります。
maven-antrun-plugin
//...
com.goldmansachs.reladomo
reladomogen
16.5.1
com.goldmansachs.reladomo
reladomo-gen-util
16.5.1
最後に、build-helper-maven-pluginを使用して、生成されたファイルをクラスパスに追加できます。
org.codehaus.mojo
build-helper-maven-plugin
add-source
generate-sources
add-source
${project.build.directory}/generated-sources/reladomo
add-resource
generate-resources
add-resource
${project.build.directory}/generated-db/
DDLスクリプトの追加はオプションです。 この例では、インメモリデータベースを使用するため、テーブルを作成するためにスクリプトを実行します。
3. XML構成
Reladomoフレームワークのメタデータは、いくつかのXMLファイルで定義できます。
3.1. オブジェクトXMLファイル
作成する各エンティティは、そのXMLファイルで定義する必要があります。
部門と従業員の2つのエンティティを使用して簡単な例を作成しましょう。 ドメインモデルの視覚的表現は次のとおりです。
最初のDepartment.xmlファイルを定義しましょう:
com.example.reladomo
Department
departments
Employee.departmentId = this.id
上記のthe entity is defined inside a root element called MithraObjectを見ることができます。 次に、対応するデータベーステーブルのパッケージ、クラス、および名前を指定しました。
タイプの各プロパティは、Attribute要素を使用して定義されます。この要素には、名前、Javaタイプ、および列名を指定できます。
We can describe the relationships between objects using the Relationship tag.この例では、次の式に基づいて、DepartmentオブジェクトとEmployeeオブジェクトの間にone-to-many関係を定義しました。
Employee.departmentId = this.id
reverseRelationshipName属性を使用すると、関係を2回定義せずに双方向にすることができます。
relatedIsDependent属性を使用すると、操作をカスケードできます。
次に、同様の方法でEmployee.xmlファイルを作成しましょう。
com.example.reladomo
Employee
employees
3.2. ReladomoClassList.xmlファイル
Reladomoは、生成する必要のあるオブジェクトについて通知する必要があります。
Mavenセクションでは、生成タスクのソースとしてReladomoClassList.xmlファイルを定義したので、ファイルを作成します。
これは、XML構成に基づいてクラスが生成されるエンティティのリストを含む単純なファイルです。
4. 生成されたクラス
これで、コマンドmvn clean installを使用してMavenアプリケーションを構築することにより、コード生成を開始するために必要なすべての要素が揃いました。
具象クラスは、指定されたパッケージのsrc/main/javaフォルダーに生成されます。
これらは、カスタムコードを追加できる単純なクラスです。 たとえば、Departmentクラスには、削除してはならないコンストラクターのみが含まれています。
public class Department extends DepartmentAbstract {
public Department() {
super();
// You must not modify this constructor. Mithra calls this internally.
// You can call this constructor. You can also add new constructors.
}
}
このクラスにカスタムコンストラクターを追加する場合は、親コンストラクターも呼び出す必要があります。
public Department(long id, String name){
super();
this.setId(id);
this.setName(name);
}
これらのクラスは、generated-sources/reladomoフォルダー内の抽象クラスとユーティリティクラスに基づいています。
このフォルダー内のクラスの主なタイプは次のとおりです。
-
DepartmentAbstractおよびEmployeeAbstractクラス–定義されたエンティティを操作するためのメソッドが含まれています
-
DepartmentListAbstractおよびEmployeeListAbstract –部門および従業員のリストを操作するためのメソッドが含まれています
-
DepartmentFinderおよびEmployeeFinder –これらはエンティティをクエリするためのメソッドを提供します
-
他のユーティリティクラス
これらのクラスを生成することで、エンティティでCRUD操作を実行するために必要なコードの大部分がすでに作成されています。
5. Reladomoアプリケーション
データベースで操作を実行するには、データベース接続を取得できる接続マネージャークラスが必要です。
5.1. 接続マネージャ
単一のデータベースで作業する場合、SourcelessConnectionManagerインターフェースを実装できます。
public class ReladomoConnectionManager implements SourcelessConnectionManager {
private static ReladomoConnectionManager instance;
private XAConnectionManager xaConnectionManager;
public static synchronized ReladomoConnectionManager getInstance() {
if (instance == null) {
instance = new ReladomoConnectionManager();
}
return instance;
}
private ReladomoConnectionManager() {
this.createConnectionManager();
}
//...
}
ReladomoConnectionManagerクラスはシングルトンパターンを実装し、トランザクション接続マネージャーのユーティリティクラスであるXAConnectionManagerに基づいています。
createConnectionManager()メソッドを詳しく見てみましょう。
private XAConnectionManager createConnectionManager() {
xaConnectionManager = new XAConnectionManager();
xaConnectionManager.setDriverClassName("org.h2.Driver");
xaConnectionManager.setJdbcConnectionString("jdbc:h2:mem:myDb");
xaConnectionManager.setJdbcUser("sa");
xaConnectionManager.setJdbcPassword("");
xaConnectionManager.setPoolName("My Connection Pool");
xaConnectionManager.setInitialSize(1);
xaConnectionManager.setPoolSize(10);
xaConnectionManager.initialisePool();
return xaConnectionManager;
}
この方法では、H2インメモリデータベースへの接続を作成するために必要なプロパティを設定しました。
また、SourcelessConnectionManagerインターフェイスからいくつかのメソッドを実装する必要があります。
@Override
public Connection getConnection() {
return xaConnectionManager.getConnection();
}
@Override
public DatabaseType getDatabaseType() {
return H2DatabaseType.getInstance();
}
@Override
public TimeZone getDatabaseTimeZone() {
return TimeZone.getDefault();
}
@Override
public String getDatabaseIdentifier() {
return "myDb";
}
@Override
public BulkLoader createBulkLoader() throws BulkLoaderException {
return null;
}
最後に、データベーステーブルを作成する生成されたDDLスクリプトを実行するカスタムメソッドを追加しましょう。
public void createTables() throws Exception {
Path ddlPath = Paths.get(ClassLoader.getSystemResource("sql").toURI());
try (
Connection conn = xaConnectionManager.getConnection();
Stream list = Files.list(ddlPath)) {
list.forEach(path -> {
try {
RunScript.execute(conn, Files.newBufferedReader(path));
}
catch (SQLException | IOException exc){
exc.printStackTrace();
}
});
}
}
もちろん、これは、実行ごとにテーブルが再作成されない本番アプリケーションには必要ありません。
5.2. Reladomoの初期化
Reladomo初期化プロセスは、接続マネージャークラスと使用されるオブジェクトタイプを指定する構成ファイルを使用します。 ReladomoRuntimeConfig.xmlファイルを定義しましょう:
次に、最初にcreateTables()メソッドを呼び出し、次にuse the MithraManager class to load the configuration and initialize Reladomoを呼び出すメインクラスを作成できます。
public class ReladomoApplication {
public static void main(String[] args) {
try {
ReladomoConnectionManager.getInstance().createTables();
} catch (Exception e1) {
e1.printStackTrace();
}
MithraManager mithraManager = MithraManagerProvider.getMithraManager();
mithraManager.setTransactionTimeout(120);
try (InputStream is = ReladomoApplication.class.getClassLoader()
.getResourceAsStream("ReladomoRuntimeConfig.xml")) {
MithraManagerProvider.getMithraManager()
.readConfiguration(is);
//execute operations
}
catch (IOException exc){
exc.printStackTrace();
}
}
}
5.3. CRUD操作の実行
次に、Reladomoで生成されたクラスを使用して、エンティティに対していくつかの操作を実行します。
まず、2つのDepartmentオブジェクトとEmployeeオブジェクトを作成してから、cascadeInsert()メソッドを使用して両方を保存しましょう。
Department department = new Department(1, "IT");
Employee employee = new Employee(1, "John");
department.getEmployees().add(employee);
department.cascadeInsert();
insert()メソッドを呼び出すことにより、各オブジェクトを個別に保存することもできます。 この例では、関係定義にrelatedIsDependent=true属性を追加したため、cascadeInsert()を使用できます。
オブジェクトをクエリするには、生成されたFinderクラスを使用できます。
Department depFound = DepartmentFinder
.findByPrimaryKey(1);
Employee empFound = EmployeeFinder
.findOne(EmployeeFinder.name().eq("John"));
この方法で取得されたオブジェクトは「ライブ」オブジェクトです。つまり、セッターを使用してオブジェクトに加えられた変更はすぐにデータベースに反映されます。
empFound.setName("Steven");
この動作を回避するために、分離オブジェクトを取得できます。
Department depDetached = DepartmentFinder
.findByPrimaryKey(1).getDetachedCopy();
オブジェクトを削除するには、delete()メソッドを使用できます。
empFound.delete();
5.4. トランザクション管理
一連の操作を1つのユニットとして実行する場合、または実行しない場合は、トランザクションでラップできます。
mithraManager.executeTransactionalCommand(tx -> {
Department dep = new Department(2, "HR");
Employee emp = new Employee(2, "Jim");
dep.getEmployees().add(emp);
dep.cascadeInsert();
return null;
});
6. Reladomoテストサポート
上記のセクションでは、Javaメインクラスでサンプルを記述しました。
アプリケーションのテストを作成する場合、これを行う1つの方法は、テストクラスに同じコードを簡単に作成することです。
ただし、for better test support, Reladomo also provides the MithraTestResource class.これにより、テスト専用に異なる構成とインメモリデータベースを使用できます。
まず、junit依存関係とともに、追加のreladomo-test-util依存関係を追加する必要があります。
com.goldmansachs.reladomo
reladomo-test-util
16.5.1
junit
junit
4.12
次に、ConnectionManagerForTestsクラスを使用するReladomoTestConfig.xmlファイルを作成する必要があります。
この接続マネージャーは、テストにのみ使用されるメモリ内のH2データベースを構成します。
次の形式のA convenient feature of the MithraTestResource class is that we can provide text files with test data:
class com.example.reladomo.Department
id, name
1, "Marketing"
class com.example.reladomo.Employee
id, name
1, "Paul"
JUnitテストクラスを作成し、@BeforeメソッドでMithraTestResourceインスタンスを設定しましょう。
public class ReladomoTest {
private MithraTestResource mithraTestResource;
@Before
public void setUp() throws Exception {
this.mithraTestResource
= new MithraTestResource("reladomo/ReladomoTestConfig.xml");
ConnectionManagerForTests connectionManager
= ConnectionManagerForTests.getInstanceForDbName("testDb");
this.mithraTestResource.createSingleDatabase(connectionManager);
mithraTestResource.addTestDataToDatabase("reladomo/test-data.txt",
connectionManager);
this.mithraTestResource.setUp();
}
}
次に、テストデータがロードされたことを確認する簡単な@Testメソッドを記述できます。
@Test
public void whenGetTestData_thenOk() {
Employee employee = EmployeeFinder.findByPrimaryKey(1);
assertEquals(employee.getName(), "Paul");
}
テストの実行後、テストデータベースをクリアする必要があります。
@After
public void tearDown() throws Exception {
this.mithraTestResource.tearDown();
}
7. 結論
この記事では、Reladomo ORMフレームワークの主な機能、およびセットアップと一般的な使用例について説明しました。
例のソースコードはover on GitHubにあります。