マッピング
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 ドライバーを参照してください。
@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 | 明示的なコンバーターを登録することでカスタマイズ可能です。 |
| パススルー | 明示的なコンバーターを使用してカスタマイズできます。 |
| パススルー | バイナリペイロードと見なされます。 |
|
| 構成されたドライバーでサポートされている場合は配列型への変換が行われますが、それ以外の場合はサポートされません。 |
プリミティブ型、ラッパー型、 | ラッパー型の配列 (たとえば | 構成されたドライバーでサポートされている場合は配列型への変換が行われますが、それ以外の場合はサポートされません。 |
ドライバー固有の型 | パススルー | 中古 |
複雑なオブジェクト | ターゲット型は、登録された | 明示的なコンバーターが必要です。それ以外の場合はサポートされていません。 |
列のネイティブデータ型は、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;
}
Spring Data の SpEL サポートを使用してテーブル名を動的に作成できます。テーブル名は生成されるとキャッシュされるため、マッピングコンテキストごとにのみ動的になります。
列名をオーバーライドする
列の命名方法がデータベーステーブル名と一致しない場合は、テーブル名を Column
(Javadoc) アノテーションでオーバーライドできます。このアノテーションの要素 value
はカスタム列名を提供します。次の例では、MyEntity
クラスの name
プロパティをデータベースの CUSTOM_COLUMN_NAME
列にマップします。
class MyEntity {
@Id
Integer id;
@Column("CUSTOM_COLUMN_NAME")
String name;
}
Spring Data の SpEL サポートを使用して列名を動的に作成できます。生成された名前はキャッシュされるため、マッピングコンテキストごとにのみ動的になります。
読み取り専用プロパティ
@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;
// …
}