1. 概要

Reladomo(旧称Mithra)は、Java のオブジェクトリレーショナルマッピング(ORM)フレームワークであり、 Goldman Sachs で開発され、現在オープンソースプロジェクトとしてリリースされています。 フレームワークは、ORMに一般的に必要な機能と、いくつかの追加機能を提供します。

Reladomoの主な機能のいくつかを見てみましょう。

  • JavaクラスとDDLスクリプトを生成できます
  • XMLファイルで記述されたメタデータによって駆動されます
  • 生成されたコードは拡張可能です
  • クエリ言語はオブジェクト指向であり、強く型付けされています
  • フレームワークはシャーディングのサポートを提供します(同じスキーマ、異なるデータセット)
  • テストのサポートも含まれています
  • パフォーマンスの高いキャッシングやトランザクションなどの便利な機能を提供します

次のセクションでは、セットアップといくつかの基本的な使用例を示します。

2. Mavenセットアップ

ORMの使用を開始するには、reladomo依存関係をpom.xmlファイルに追加する必要があります。

<dependency>
    <groupId>com.goldmansachs.reladomo</groupId>
    <artifactId>reladomo</artifactId>
    <version>16.5.1</version>
</dependency>

例ではH2データベースを使用するので、h2依存関係も追加しましょう。

<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <version>1.4.196</version>
</dependency>

これに加えて、クラスとSQLファイルを生成するプラグインをセットアップし、実行中にそれらをロードする必要があります。

ファイルの生成には、maven-antrun-pluginを使用して実行されるタスクを使用できます。 まず、Javaクラスを生成するためのタスクを定義する方法を見てみましょう。

<plugin>
    <artifactId>maven-antrun-plugin</artifactId>
    <executions>
        <execution>
            <id>generateMithra</id>
            <phase>generate-sources</phase>
            <goals>
                <goal>run</goal>
            </goals>
            <configuration>
                <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/ReladomoClassList.xml"
                      generateGscListMethod="true"
                      generatedDir="${project.build.directory}/generated-sources/reladomo"
                      nonGeneratedDir="${project.basedir}/src/main/java"/>
                </tasks>
            </configuration>
        </execution>
    </executions>
</plugin>    

gen-reladomoタスクは、提供されたMithraGeneratorを使用して、ReladomoClassList.xmlファイルの構成に基づいてJavaファイルを作成します。このファイルに含まれる内容については、後のセクションで詳しく説明します。

タスクには、生成されたファイルの場所を定義する2つのプロパティもあります。

  • 生成されたDir–変更またはバージョン管理されるべきではないクラスが含まれています
  • nonGeneratedDir –さらにカスタマイズおよびバージョン管理できる生成された具象オブジェクトクラス

Javaオブジェクトに対応するデータベーステーブルは、2番目の Ant タスクによって生成されたDDLスクリプトを使用して、手動または自動で作成できます。

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

このタスクは、前述の同じReladomoClassList.xmlファイルに基づくMithraDbDefinitionGeneratorを使用します。 SQLスクリプトはgenerated-db/sqlディレクトリに配置されます。

このプラグインの定義を完了するには、作成に使用する2つの依存関係も追加する必要があります。

<plugin>
    <artifactId>maven-antrun-plugin</artifactId>
    <executions>
    //...               
    </executions>
    <dependencies>
        <dependency>
            <groupId>com.goldmansachs.reladomo</groupId>
            <artifactId>reladomogen</artifactId>
            <version>16.5.1</version>
        </dependency>
        <dependency>
            <groupId>com.goldmansachs.reladomo</groupId>
            <artifactId>reladomo-gen-util</artifactId>
            <version>16.5.1</version>
        </dependency>
    </dependencies>
</plugin>

最後に、 build-helper-maven-plugin を使用して、生成されたファイルをクラスパスに追加できます。

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>build-helper-maven-plugin</artifactId>
    <executions>
        <execution>
            <id>add-source</id>
            <phase>generate-sources</phase>
            <goals>
                <goal>add-source</goal>
            </goals>
            <configuration>
                <sources>
                    <source>${project.build.directory}/generated-sources/reladomo</source>
                </sources>
            </configuration>
        </execution>
        <execution>
            <id>add-resource</id>
            <phase>generate-resources</phase>
            <goals>
                <goal>add-resource</goal>
            </goals>
            <configuration>
                <resources>
                    <resource>
                        <directory>${project.build.directory}/generated-db/</directory>
                    </resource>
                </resources>
            </configuration>
        </execution>
    </executions>
</plugin>

DDLスクリプトの追加はオプションです。 この例では、インメモリデータベースを使用するため、テーブルを作成するためにスクリプトを実行します。

3. XML構成

Reladomo フレームワークのメタデータは、いくつかのXMLファイルで定義できます。

3.1. オブジェクトXMLファイル

作成する各エンティティは、そのXMLファイルで定義する必要があります。

部門と従業員の2つのエンティティを使用して簡単な例を作成しましょう。 これが私たちのドメインモデルの視覚的表現です:

最初のDepartment.xmlファイルを定義しましょう。

<MithraObject objectType="transactional">
    <PackageName>com.baeldung.reladomo</PackageName>
    <ClassName>Department</ClassName>
    <DefaultTable>departments</DefaultTable>

    <Attribute name="id" javaType="long" 
      columnName="department_id" primaryKey="true"/>
    <Attribute name="name" javaType="String" 
      columnName="name" maxLength="50" truncate="true"/>
    <Relationship name="employees" relatedObject="Employee" 
      cardinality="one-to-many" 
      reverseRelationshipName="department" 
      relatedIsDependent="true">
         Employee.departmentId = this.id
    </Relationship>
</MithraObject>

上記のエンティティは、MithraObjectというルート要素内で定義されていることがわかります。 次に、対応するデータベーステーブルのパッケージ、クラス、および名前を指定しました。

タイプの各プロパティは、 Attribute 要素を使用して定義されます。この要素には、名前、Javaタイプ、および列名を指定できます。

Relationshipタグを使用して、オブジェクト間の関係を記述できます。この例では、Departmentの間に1対多の関係を定義しました。次の式に基づくEmployeeオブジェクト:

Employee.departmentId = this.id

reverseRelationshipName 属性を使用すると、関係を2回定義せずに双方向にすることができます。

relatedIsDependent 属性を使用すると、操作をカスケードできます。

次に、同様の方法でEmployee.xmlファイルを作成しましょう。

<MithraObject objectType="transactional">
    <PackageName>com.baeldung.reladomo</PackageName>
    <ClassName>Employee</ClassName>
    <DefaultTable>employees</DefaultTable>

    <Attribute name="id" javaType="long" 
      columnName="employee_id" primaryKey="true"/>
    <Attribute name="name" javaType="String" 
      columnName="name" maxLength="50" truncate="true"/>
    <Attribute name="departmentId" javaType="long" 
      columnName="department_id"/>
</MithraObject>

3.2. ReladomoClassList.xmlファイル

Reladomo は、生成する必要のあるオブジェクトについて通知する必要があります。

Maven セクションで、 ReladomoClassList.xml ファイルを生成タスクのソースとして定義したので、ファイルを作成します。

<Mithra>
    <MithraObjectResource name="Department"/>
    <MithraObjectResource name="Employee"/>
</Mithra>

これは、XML構成に基づいてクラスが生成されるエンティティのリストを含む単純なファイルです。

4. 生成されたクラス

これで、コマンド mvn cleaninstallを使用して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<Path> 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ファイルを定義しましょう。

<MithraRuntime>
    <ConnectionManager 
      className="com.baeldung.reladomo.ReladomoConnectionManager ">
    <MithraObjectConfiguration 
      className="com.baeldung.reladomo.Department" cacheType="partial"/>
    <MithraObjectConfiguration 
      className="com.baeldung.reladomo.Employee " cacheType="partial"/>
    </ConnectionManager>
</MithraRuntime>

次に、最初に createTables()メソッドを呼び出し、次に MithraManagerクラスを使用して構成をロードし、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オブジェクトを作成し、 casinoInsert()メソッドを使用して両方を保存します。

Department department = new Department(1, "IT");
Employee employee = new Employee(1, "John");
department.getEmployees().add(employee);
department.cascadeInsert();

insert()メソッドを呼び出すことにより、各オブジェクトを個別に保存することもできます。 この例では、関係定義に relatedIsDependent = true 属性を追加したため、 casinoInsert()を使用できます。

オブジェクトをクエリするには、生成された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つの方法は、テストクラスに同じコードを簡単に作成することです。

ただし、より良いテストサポートのために、ReladomoはMithraTestResourceクラスも提供します。これにより、テスト専用に異なる構成とインメモリデータベースを使用できます。

まず、 reladomo-test-util 依存関係を、junit依存関係とともに追加する必要があります。

<dependency>
    <groupId>com.goldmansachs.reladomo</groupId>
    <artifactId>reladomo-test-util</artifactId>
    <version>16.5.1</version>
</dependency>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>

次に、ConnectionManagerForTestsクラスを使用するReladomoTestConfig.xmlファイルを作成する必要があります。

<MithraRuntime>
    <ConnectionManager 
      className="com.gs.fw.common.mithra.test.ConnectionManagerForTests">
        <Property name="resourceName" value="testDb"/>
        <MithraObjectConfiguration 
          className="com.baeldung.reladomo.Department" cacheType="partial"/>
        <MithraObjectConfiguration 
          className="com.baeldung.reladomo.Employee " cacheType="partial"/>
    </ConnectionManager>
 </MithraRuntime>

この接続マネージャーは、テストにのみ使用されるメモリー内H2データベースを構成します。

MithraTestResourceクラスの便利な機能は、次の形式でテストデータを含むテキストファイルを提供できることです。

class com.baeldung.reladomo.Department
id, name
1, "Marketing"

class com.baeldung.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フレームワークの主な機能と、セットアップおよび一般的な使用例について説明しました。

例のソースコードは、GitHubにあります。