マッピング

MappingR2dbcConverter により、豊富なマッピングサポートが提供されます。MappingR2dbcConverter には、ドメインオブジェクトをデータ行にマッピングできる豊富なメタデータモデルがあります。マッピングメタデータモデルは、ドメインオブジェクトのアノテーションを使用して作成されます。ただし、インフラストラクチャは、メタデータ情報の唯一のソースとしてアノテーションを使用することに限定されません。MappingR2dbcConverter では、一連の規則に従って、追加のメタデータを提供せずにオブジェクトを行にマップすることもできます。

このセクションでは、オブジェクトを行にマッピングするための規則の使用方法や、アノテーションベースのマッピングメタデータでこれらの規則をオーバーライドする方法など、MappingR2dbcConverter の機能について説明します。

この章を続ける前に、オブジェクトマッピングの基礎の基本について参照してください。

規約ベースのマッピング

MappingR2dbcConverter には、追加のマッピングメタデータが提供されていない場合にオブジェクトを行にマッピングするためのいくつかの規則があります。規則は次のとおりです。

  • 短い Java クラス名は、次の方法でテーブル名にマップされます。com.bigbank.SavingsAccount クラスは、SAVINGS_ACCOUNT テーブル名にマップされます。同じ名前のマッピングが、フィールドを列名にマッピングするために適用されます。例: firstName フィールドは FIRST_NAME 列にマップされます。カスタム NamingStrategy を提供することにより、このマッピングを制御できます。詳細については、マッピング設定を参照してください。プロパティ名またはクラス名から派生したテーブル名と列名は、デフォルトで引用符なしで SQL ステートメントで使用されます。RelationalMappingContext.setForceQuote(true) を設定することにより、この動作を制御できます。

  • ネストされたオブジェクトはサポートされていません。

  • コンバーターは、CustomConversions に登録されている Spring コンバーターを使用して、オブジェクトプロパティの行列および値へのデフォルトのマッピングをオーバーライドします。

  • オブジェクトのフィールドは、行の列との間の変換に使用されます。パブリック JavaBean プロパティは使用されません。

  • コンストラクター引数名が行のトップレベルの列名と一致する非ゼロ引数のコンストラクターが 1 つある場合は、そのコンストラクターが使用されます。それ以外の場合は、引数なしのコンストラクターが使用されます。引数がゼロではないコンストラクターが複数ある場合、例外がスローされます。詳細についてはオブジェクトの作成を参照してください。

マッピング設定

デフォルトでは、(明示的に構成されていない限り) DatabaseClient を作成すると MappingR2dbcConverter のインスタンスが作成されます。MappingR2dbcConverter の独自のインスタンスを作成できます。独自のインスタンスを作成すると、Spring コンバーターを登録して、データベースとの間で特定のクラスをマップできます。

Java ベースのメタデータを使用して、MappingR2dbcConverter と DatabaseClient および ConnectionFactory を構成できます。次の例では、Spring の Java ベースの構成を使用しています。

R2dbcMappingContext to の setForceQuote を true に設定すると、クラスおよびプロパティから派生したテーブル名と列名がデータベース固有の引用符とともに使用されます。これは、これらの名前に予約済みの SQL ワード(順序など)を使用しても問題がないことを意味します。これを行うには、AbstractR2dbcConfiguration の r2dbcMappingContext(Optional<NamingStrategy>) をオーバーライドします。Spring Data は、そのような名前の大文字小文字を、引用符が使用されていない場合に構成済みデータベースでも使用される形式に変換します。名前にキーワードや特殊文字を使用しない限り、テーブルを作成するときに引用符で囲まれていない名前を使用できます。SQL 標準に準拠するデータベースの場合、これは名前が大文字に変換されることを意味します。引用文字と名前の大文字化の方法は、使用される Dialect によって制御されます。カスタムダイアレクトを構成する方法については、R2DBC ドライバーを参照してください。

R2DBC マッピングサポートを構成する @Configuration クラス
@Configuration
public class MyAppConfig extends AbstractR2dbcConfiguration {

  public ConnectionFactory connectionFactory() {
    return ConnectionFactories.get("r2dbc:…");
  }

  // the following are optional

  @Override
  protected List<Object> getCustomConverters() {
    return List.of(new PersonReadConverter(), new PersonWriteConverter());
  }
}

AbstractR2dbcConfiguration では、ConnectionFactory を定義するメソッドを実装する必要があります。

r2dbcCustomConversions メソッドをオーバーライドすることにより、コンバーターにコンバーターを追加できます。

カスタム NamingStrategy は、Bean として登録することで構成できます。NamingStrategy は、クラスとプロパティの名前をテーブルと列の名前に変換する方法を制御します。

AbstractR2dbcConfiguration は DatabaseClient インスタンスを作成し、databaseClient という名前でコンテナーに登録します。

メタデータベースのマッピング

Spring Data R2DBC サポート内のオブジェクトマッピング機能を最大限に活用するには、マップされたオブジェクトに @Table アノテーションを付ける必要があります。マッピングフレームワークにこのアノテーションを付ける必要はありませんが(アノテーションがなくても POJO は正しくマッピングされます)、クラスパススキャナーでドメインオブジェクトを見つけて前処理し、必要なメタデータを抽出できます。このアノテーションを使用しない場合、マッピングフレームワークは、ドメインオブジェクトのプロパティとそのメソッドを認識できるように内部メタデータモデルを構築する必要があるため、ドメインオブジェクトを最初に保存するときにアプリケーションのパフォーマンスがわずかに低下します。永続化します。次の例は、ドメインオブジェクトを示しています。

ドメインオブジェクトの例
package com.mycompany.domain;

@Table
public class Person {

  @Id
  private Long id;

  private Integer ssn;

  private String firstName;

  private String lastName;
}
@Id アノテーションは、どのプロパティを主キーとして使用するかをマッパーに伝えます。

デフォルトの型マッピング

次の表は、エンティティのプロパティ型がマッピングにどのように影響するかを説明しています。

ソースタイプ ターゲットタイプ コメント

プリミティブ型とラッパー型

パススルー

明示的なコンバーターを使用してカスタマイズできます。

JSR-310 日付 / 時刻型

パススルー

明示的なコンバーターを使用してカスタマイズできます。

String, BigInteger, BigDecimal, and UUID

パススルー

明示的なコンバーターを使用してカスタマイズできます。

Enum

String

明示的なコンバーターを登録することでカスタマイズ可能です。

Blob and Clob

パススルー

明示的なコンバーターを使用してカスタマイズできます。

byte[], ByteBuffer

パススルー

バイナリペイロードと見なされます。

Collection<T>

T の配列

構成されたドライバーでサポートされている場合は配列型への変換が行われますが、それ以外の場合はサポートされません。

プリミティブ型、ラッパー型、String の配列

ラッパー型の配列 (たとえば int[] → Integer[])

構成されたドライバーでサポートされている場合は配列型への変換が行われますが、それ以外の場合はサポートされません。

ドライバー固有の型

パススルー

中古 R2dbcDialect からシンプル型としてコントリビュート。

複雑なオブジェクト

ターゲット型は、登録された Converter に依存します。

明示的なコンバーターが必要です。それ以外の場合はサポートされていません。

列のネイティブデータ型は、R2DBC ドライバーの型マッピングに依存します。ドライバーは、ジオメトリ型などの追加のシンプル型を提供できます。

マッピングアノテーションの概要

RelationalConverter は、メタデータを使用してオブジェクトの行へのマッピングを駆動できます。次のアノテーションを使用できます。

  • @Id: 主キーをマークするためにフィールドレベルで適用されます。

  • @Table: このクラスがデータベースへのマッピングの候補であることを示すために、クラスレベルで適用されます。データベースが保存されているテーブルの名前を指定できます。

  • @Transient: デフォルトでは、すべてのフィールドが行にマップされます。このアノテーションは、それが適用されるフィールドをデータベースに格納することから除外します。コンバーターはコンストラクターの引数の値を具体化できないため、永続的なコンストラクター内では一時的なプロパティを使用できません。

  • @PersistenceCreator: データベースからオブジェクトをインスタンス化するときに使用する、特定のコンストラクターまたは静的ファクトリメソッド (パッケージで protected メソッドであっても) をマークします。コンストラクターの引数は、名前によって取得された行の値にマップされます。

  • @Value: このアノテーションは Spring Framework の一部です。マッピングフレームワーク内で、コンストラクター引数に適用できます。これにより、Spring 式言語ステートメントを使用して、ドメインオブジェクトの構築に使用される前に、データベースで取得されたキーの値を変換できます。特定の行の列を参照するには、次のような式を使用する必要があります。: @Value("#root.myProperty") ここで、root は指定された Row のルートを指します。

  • @Column: フィールドレベルで適用され、行に表示される列の名前を記述し、クラスのフィールド名とは異なる名前にします。@Column アノテーションで指定された名前は、SQL ステートメントで使用される場合は常に引用符で囲まれます。ほとんどのデータベースでは、これはこれらの名前で大文字と小文字が区別されることを意味します。また、これらの名前に特殊文字を使用できることも意味します。ただし、他のツールで問題が発生する可能性があるため、これはお勧めしません。

  • @Version: フィールドレベルで適用され、楽観的ロックに使用され、保存操作の変更がチェックされます。値は null (プリミティブ型の場合は zero)であり、エンティティが新規であるためのマーカーと見なされます。最初に格納される値は zero (プリミティブ型の場合は one)です。バージョンは、更新のたびに自動的にインクリメントされます。詳細については、楽観的ロックを参照してください。

マッピングメタデータインフラストラクチャは、テクノロジーに依存しない別個の spring-data-commons プロジェクトで定義されています。アノテーションベースのメタデータをサポートするために、R2DBC サポートでは特定のサブクラスが使用されます。他の戦略を導入することもできます(需要がある場合)。

ネーミング戦略

慣例により、Spring Data は NamingStrategy を適用して、デフォルトでスネークケース [Wikipedia] (英語) になるテーブル名、列名、スキーマ名を決定します。firstName という名前のオブジェクトプロパティは first_name になります。アプリケーションコンテキストで NamingStrategy (Javadoc) を指定することで、これを微調整できます。

テーブル名をオーバーライドする

テーブルの命名方法がデータベースのテーブル名と一致しない場合は、テーブル名を @Table (Javadoc) アノテーションでオーバーライドできます。このアノテーションの要素 value はカスタムテーブル名を提供します。次の例では、MyEntity クラスをデータベース内の CUSTOM_TABLE_NAME テーブルにマップします。

@Table("CUSTOM_TABLE_NAME")
class MyEntity {
    @Id
    Integer id;

    String name;
}

列名をオーバーライドする

列の命名方法がデータベーステーブル名と一致しない場合は、テーブル名を @Column (Javadoc) アノテーションでオーバーライドできます。このアノテーションの要素 value はカスタム列名を提供します。次の例では、MyEntity クラスの name プロパティをデータベースの CUSTOM_COLUMN_NAME 列にマップします。

class MyEntity {
    @Id
    Integer id;

    @Column("CUSTOM_COLUMN_NAME")
    String name;
}

読み取り専用プロパティ

@ReadOnlyProperty アノテーションが付けられた属性は、Spring Data によってデータベースに書き込まれませんが、エンティティがロードされるときに読み取られます。

Spring Data は、エンティティの書き込み後にエンティティを自動的にリロードしません。そのような列のデータベースで生成されたデータを表示する場合は、明示的にリロードする必要があります。

アノテーション付き属性がエンティティまたはエンティティのコレクションである場合、個別のテーブル内の 1 つ以上の個別の行で表されます。Spring Data は、これらの行の挿入、削除、更新を実行しません。

挿入専用プロパティ

@InsertOnlyProperty でアノテーションが付けられた属性は、挿入操作中に Spring Data によってのみデータベースに書き込まれます。更新の場合、これらのプロパティは無視されます。

@InsertOnlyProperty は、集約ルートに対してのみサポートされています。

カスタマイズされたオブジェクト構築

マッピングサブシステムでは、コンストラクターに @PersistenceConstructor アノテーションを付けることにより、オブジェクトの構成をカスタマイズできます。コンストラクターのパラメーターに使用される値は、次の方法で解決されます。

  • パラメーターに @Value アノテーションが付けられている場合、指定された式が評価され、結果がパラメーター値として使用されます。

  • Java 型に、入力行の指定されたフィールドと名前が一致するプロパティがある場合、そのプロパティ情報を使用して、入力フィールド値を渡す適切なコンストラクターパラメーターを選択します。これは、パラメーター名情報が Java .class ファイルに存在する場合にのみ機能します。これは、デバッグ情報を使用してソースをコンパイルするか、Java 8 の javac の -parameters コマンドラインスイッチを使用して実現できます。

  • そうでない場合、MappingException がスローされ、指定されたコンストラクターパラメーターをバインドできなかったことを示します。

class OrderItem {

  private @Id final String id;
  private final int quantity;
  private final double unitPrice;

  OrderItem(String id, int quantity, double unitPrice) {
    this.id = id;
    this.quantity = quantity;
    this.unitPrice = unitPrice;
  }

  // getters/setters omitted
}

明示的なコンバーターを使用したマッピングのオーバーライド

オブジェクトを保管および照会する場合、R2dbcConverter インスタンスを使用して、すべての Java 型から OutboundRow インスタンスへのマッピングを処理すると便利なことがよくあります。ただし、R2dbcConverter インスタンスにほとんどの作業を行わせたい場合がありますが、パフォーマンスを最適化するために、特定の型の変換を選択的に処理できます。

変換を自分で選択的に処理するには、1 つ以上の org.springframework.core.convert.converter.Converter インスタンスを R2dbcConverter に登録します。

AbstractR2dbcConfiguration の r2dbcCustomConversions メソッドを使用して、コンバーターを構成できます。この章の最初の例は、 Java を使用して構成を実行する方法を示しています。

カスタムトップレベルエンティティ変換には、変換に非対称型が必要です。受信データは、R2DBC の Row から抽出されます。送信データ(INSERT/UPDATE ステートメントで使用される)は OutboundRow として表され、後でステートメントにアセンブルされます。

Spring コンバーター実装の次の例は、Row から Person POJO に変換します。

@ReadingConverter
 public class PersonReadConverter implements Converter<Row, Person> {

  public Person convert(Row source) {
    Person p = new Person(source.get("id", String.class),source.get("name", String.class));
    p.setAge(source.get("age", Integer.class));
    return p;
  }
}

コンバーターは特異なプロパティに適用されることに注意してください。コレクションプロパティ(例: Collection<Person>)は反復され、要素ごとに変換されます。コレクションコンバーター(例: Converter<List<Person>>, OutboundRow)はサポートされていません。

R2DBC は、ボックス化されたプリミティブ(int.class ではなく Integer.class)を使用して、プリミティブ値を返します。

次の例は、Person から OutboundRow に変換します。

@WritingConverter
public class PersonWriteConverter implements Converter<Person, OutboundRow> {

  public OutboundRow convert(Person source) {
    OutboundRow row = new OutboundRow();
    row.put("id", Parameter.from(source.getId()));
    row.put("name", Parameter.from(source.getFirstName()));
    row.put("age", Parameter.from(source.getAge()));
    return row;
  }
}

明示的なコンバーターを使用した列挙型マッピングのオーバーライド

Postgres [GitHub] (英語) などの一部のデータベースは、データベース固有の列挙型列型を使用して、列挙値をネイティブに書き込むことができます。Spring Data は、移植性を最大化するために、デフォルトで Enum 値を String 値に変換します。実際の列挙値を保持するには、ソース型とターゲット型が実際の列挙型を使用して Enum.name() 変換を使用しないようにする @Writing コンバーターを登録します。さらに、ドライバーが列挙型を表す方法を認識できるように、ドライバーレベルで列挙型を構成する必要があります。

次の例は、Color 列挙値をネイティブに読み書きするための関連コンポーネントを示しています。

enum Color {
    Grey, Blue
}

class ColorConverter extends EnumWriteSupport<Color> {

}


class Product {
    @Id long id;
    Color color;

    // …
}