このバージョンはまだ開発中であり、まだ安定しているとは見なされていません。最新の安定バージョンについては、Spring Data Relational 3.5.5 を使用してください! |
マッピング
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 は正しくマッピングされます)、クラスパススキャナーでドメインオブジェクトを見つけて前処理し、必要なメタデータを抽出できます。このアノテーションを使用しない場合、マッピングフレームワークは、ドメインオブジェクトのプロパティとそのメソッドを認識できるように内部メタデータモデルを構築する必要があるため、ドメインオブジェクトを最初に保存するときにアプリケーションのパフォーマンスがわずかに低下します。永続化します。次の例は、ドメインオブジェクトを示しています。
@Table
public class Person {
@Id
private Long id;
private Integer ssn;
private String firstName;
private String lastName;
}@Id アノテーションは、どのプロパティを主キーとして使用するかをマッパーに伝えます。 |
デフォルトの型マッピング
次の表は、エンティティのプロパティ型がマッピングにどのように影響するかを説明しています。
| ソースタイプ | ターゲットタイプ | コメント |
|---|---|---|
プリミティブ型とラッパー型 | パススルー | 明示的なコンバーターを使用してカスタマイズできます。 |
JSR-310 日付 / 時刻型 | パススルー | 明示的なコンバーターを使用してカスタマイズできます。 |
| パススルー | 明示的なコンバーターを使用してカスタマイズできます。 |
| String | 明示的なコンバーターを登録することでカスタマイズ可能です。 |
| パススルー | 明示的なコンバーターを使用してカスタマイズできます。 |
| パススルー | バイナリペイロードと見なされます。 |
|
| 構成されたドライバーでサポートされている場合は配列型への変換が行われますが、それ以外の場合はサポートされません。 |
プリミティブ型、ラッパー型、 | ラッパー型の配列 (たとえば | 構成されたドライバーでサポートされている場合は配列型への変換が行われますが、それ以外の場合はサポートされません。 |
ドライバー固有の型 | パススルー | 中古 |
複雑なオブジェクト | ターゲット型は、登録された | 明示的なコンバーターが必要です。それ以外の場合はサポートされていません。 |
| 列のネイティブデータ型は、R2DBC ドライバーの型マッピングに依存します。ドライバーは、ジオメトリ型などの追加のシンプル型を提供できます。 |
マッピングアノテーションの概要
RelationalConverter は、メタデータを使用してオブジェクトの行へのマッピングを駆動できます。次のアノテーションを使用できます。
@Embedded: このアノテーションを持つプロパティは、別のテーブルではなく、親エンティティのテーブルにマッピングされます。結果の列に共通のプレフィックスを付けるかどうかを指定できます。このようなエンティティから得られるすべての列がnullの場合、アノテーション付きエンティティはnullになるか、空になります。つまり、そのすべてのプロパティはnullになります。これは@Embedded.onEmpty()の値によって異なります。@Idと組み合わせて複合 ID を形成することもできます。@Id: フィールドレベルで適用され、主キーをマークします。@Embeddedと組み合わせて複合 ID を形成することもできます。@InsertOnlyProperty: プロパティを挿入時にのみ書き込み対象としてマークします。集約ルート上のこのプロパティは一度だけ書き込まれ、更新されることはありません。ネストされたエンティティでは、すべての保存操作は挿入処理となるため、このアノテーションはネストされたエンティティのプロパティには影響しません。@MappedCollection: コレクションまたは単一のネストされたエンティティのマッピング方法を設定できます。idColumnは、親エンティティの主キーを参照するために使用する列を指定します。keyColumnは、ListのインデックスまたはMapのキーを格納するために使用する列を指定します。@Sequence: アノテーション付きプロパティの値を生成するためのデータベースシーケンスを指定します。@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 サポートを使用して列名を動的に作成できます。生成された名前はキャッシュされるため、マッピングコンテキストごとにのみ動的になります。
埋め込み Id
識別子プロパティに @Embedded アノテーションを付与することで、複合 ID を使用できます。埋め込まれたエンティティ全体が ID とみなされるため、集約が挿入を必要とする新しい集約か、更新を要求する既存の集約かを判断するチェックは、そのエンティティに基づいて行われ、集約の要素に基づいて行われることはありません。ほとんどのユースケースでは、新しい集約の ID を設定するためにカスタム BeforeConvertCallback が必要になります。
@Table("PERSON_WITH_COMPOSITE_ID")
record Person( (1)
@Id @Embedded.Nullable Name pk, (2)
String nickName,
Integer age
) {
}
record Name(String first, String last) {
}CREATE TABLE PERSON_WITH_COMPOSITE_ID (
FIRST VARCHAR(100),
LAST VARCHAR(100),
NICK_NAME VARCHAR(100),
AGE INT,
PRIMARY KEY (FIRST, LAST) (3)
);| 1 | エンティティは特別な考慮なしにレコードとして表現される可能性がある |
| 2 | pk は ID としてマークされ埋め込まれています |
| 3 | 埋め込まれた Name エンティティの 2 つの列がデータベースの主キーを構成します。 |
テーブル作成の詳細は、使用するデータベースによって異なります。
読み取り専用プロパティ
@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;
// …
}