最新の安定バージョンについては、Spring Data Relational 4.1.0 を使用してください! |
マッピング
MappingR2dbcConverter により、豊富なマッピングサポートが提供されます。MappingR2dbcConverter には、ドメインオブジェクトをデータ行にマッピングできる豊富なメタデータモデルがあります。マッピングメタデータモデルは、ドメインオブジェクトのアノテーションを使用して作成されます。ただし、インフラストラクチャは、メタデータ情報の唯一のソースとしてアノテーションを使用することに限定されません。MappingR2dbcConverter では、一連の規則に従って、追加のメタデータを提供せずにオブジェクトを行にマップすることもできます。
このセクションでは、オブジェクトを行にマッピングするための規則の使用方法や、アノテーションベースのマッピングメタデータでこれらの規則をオーバーライドする方法など、MappingR2dbcConverter の機能について説明します。
この章を続ける前に、オブジェクトマッピングの基礎の基本について参照してください。
規約ベースのマッピング
MappingR2dbcConverter には、追加のマッピングメタデータが提供されていない場合にオブジェクトを行にマッピングするためのいくつかの規則があります。規則は次のとおりです。
短い Java クラス名は、次のようにテーブル名にマッピングされます。
com.bigbank.SavingsAccountクラスは、SAVINGS_ACCOUNTテーブル名にマッピングされます。フィールドを列名にマッピングする場合も、同じ名前マッピングが適用されます。例:firstNameフィールドは、FIRST_NAME列にマッピングされます。カスタムNamingStrategyを指定することで、このマッピングを制御できます。詳細については、マッピング設定を参照してください。プロパティ名またはクラス名から派生したテーブル名と列名は、デフォルトでは引用符付きの SQL ステートメントで使用されます。この動作は、R2dbcMappingContext.setForceQuote(false)を設定するか、R2dbcMappingContext.forPlainIdentifiers()を使用してプレーン (引用符なし) の識別子を持つコンテキストを作成することで制御できます。慣例として、任意にネストされたオブジェクトはサポートされていません。同じテーブル内の列にマッピングされる値オブジェクトには、
@Embeddedを使用してください([entity-persistence.embedded-entities] を参照)。コンバーターは、
CustomConversionsに登録されている Spring コンバーターを使用して、オブジェクトプロパティの行列および値へのデフォルトのマッピングをオーバーライドします。オブジェクトのフィールドは、行の列との間の変換に使用されます。パブリック
JavaBeanプロパティは使用されません。コンストラクター引数名が行のトップレベルの列名と一致する非ゼロ引数のコンストラクターが 1 つある場合は、そのコンストラクターが使用されます。それ以外の場合は、引数なしのコンストラクターが使用されます。引数がゼロではないコンストラクターが複数ある場合、例外がスローされます。詳細についてはオブジェクトの作成を参照してください。
マッピング設定
デフォルトでは、(明示的に構成されていない限り) DatabaseClient を作成すると MappingR2dbcConverter のインスタンスが作成されます。MappingR2dbcConverter の独自のインスタンスを作成できます。独自のインスタンスを作成すると、Spring コンバーターを登録して、データベースとの間で特定のクラスをマップできます。
Java ベースのメタデータを使用して、MappingR2dbcConverter と DatabaseClient および ConnectionFactory を構成できます。次の例では、Spring の Java ベースの構成を使用しています。
By default, table and column names derived from classes and properties are used with database-specific quotes, meaning they are typically case-sensitive and reserved SQL words (such as order) can be used safely in names. To disable quoting, override r2dbcMappingContext(Optional<NamingStrategy>) in AbstractR2dbcConfiguration and return R2dbcMappingContext.forPlainIdentifiers(…), or call context.setForceQuote(false) on the context. When quoting is disabled, Spring Data converts identifier casing to the form used by the configured database without quoting. For databases that adhere to the SQL standard, names are converted to upper case, so you can use unquoted names when creating tables as long as you do not use keywords or special characters. The quoting character and the way names get capitalized is controlled by the used R2dbcDialect. See R2DBC ドライバー for how to configure custom dialects.
@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 を定義するメソッドを実装する必要があります。
getCustomConverters メソッドをオーバーライドすることにより、コンバーターにコンバーターを追加できます。
カスタム 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: フィールドレベルで適用され、行内で表現される列名を記述します。この名前は、クラスのフィールド名とは異なる名前を使用できます。forceQuoteが有効になっている場合(デフォルト)、@Columnアノテーションで指定された名前は引用符で囲まれます。ほとんどのデータベースでは、引用符で囲むということは、これらの名前が大文字と小文字を区別することを意味します。また、これらの名前に特殊文字を使用できることも意味します。ただし、他のツールで問題が発生する可能性があるため、これは推奨されません。@Version: Applied at field level is used for optimistic locking and checked for modification on save operations. A value ofnullfor wrapper types (orzerofor primitive types) marks the entity as new. The initially stored value iszerofor wrapper types (onefor primitive types). The version gets incremented automatically on every update. See 楽観的ロック for further reference.
マッピングメタデータインフラストラクチャは、テクノロジーに依存しない別個の 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 インスタンスにほとんどの作業を行わせたい場合がありますが、パフォーマンスを最適化するために、特定の型の変換を選択的に処理できます。
To selectively handle the conversion yourself, register one or more org.springframework.core.convert.converter.Converter instances with the R2dbcConverter.
AbstractR2dbcConfiguration の getCustomConverters メソッドを使用して、コンバーターを構成できます。この章の最初の例は、 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;
}
}明示的なコンバーターを使用した列挙型マッピングのオーバーライド
Some databases, such as Postgres [GitHub] (英語) , can natively write enum values using their database-specific enumerated column type. Spring Data converts Enum values by default to String values for maximum portability. To retain the actual enum value, register a @WritingConverter whose source and target types use the actual enum type to avoid using Enum.name() conversion. Additionally, you need to configure the enum type on the driver level so that the driver is aware how to represent the enum type.
次の例は、Color 列挙値をネイティブに読み書きするための関連コンポーネントを示しています。
enum Color {
Grey, Blue
}
class ColorConverter extends EnumWriteSupport<Color> {
}
class Product {
@Id long id;
Color color;
// …
}