マッピング
MappingJdbcConverter
により、豊富なマッピングサポートが提供されます。MappingJdbcConverter
には、ドメインオブジェクトをデータ行にマッピングできる豊富なメタデータモデルがあります。マッピングメタデータモデルは、ドメインオブジェクトのアノテーションを使用して作成されます。ただし、インフラストラクチャは、メタデータ情報の唯一のソースとしてアノテーションを使用することに限定されません。MappingJdbcConverter
では、一連の規則に従って、追加のメタデータを提供せずにオブジェクトを行にマップすることもできます。
このセクションでは、オブジェクトを行にマッピングするための規則の使用方法や、アノテーションベースのマッピングメタデータでこれらの規則をオーバーライドする方法など、MappingJdbcConverter
の機能について説明します。
この章を続ける前に、オブジェクトマッピングの基礎の基本について参照してください。
規約ベースのマッピング
MappingJdbcConverter
には、追加のマッピングメタデータが提供されていない場合にオブジェクトを行にマッピングするためのいくつかの規則があります。規則は次のとおりです。
短い Java クラス名は、次の方法でテーブル名にマップされます。
com.bigbank.SavingsAccount
クラスは、SAVINGS_ACCOUNT
テーブル名にマップされます。同じ名前のマッピングが、フィールドを列名にマッピングするために適用されます。例:firstName
フィールドはFIRST_NAME
列にマップされます。カスタムNamingStrategy
を提供することにより、このマッピングを制御できます。詳細については、マッピング設定を参照してください。プロパティ名またはクラス名から派生したテーブル名と列名は、デフォルトで引用符なしで SQL ステートメントで使用されます。RelationalMappingContext.setForceQuote(true)
を設定することにより、この動作を制御できます。コンバーターは、
CustomConversions
に登録されている Spring コンバーターを使用して、オブジェクトプロパティの行列および値へのデフォルトのマッピングをオーバーライドします。オブジェクトのフィールドは、行の列との間の変換に使用されます。パブリック
JavaBean
プロパティは使用されません。コンストラクター引数名が行のトップレベルの列名と一致する非ゼロ引数のコンストラクターが 1 つある場合は、そのコンストラクターが使用されます。それ以外の場合は、引数なしのコンストラクターが使用されます。引数がゼロではないコンストラクターが複数ある場合、例外がスローされます。詳細についてはオブジェクトの作成を参照してください。
エンティティでサポートされている型
現在、次の型のプロパティがサポートされています。
すべてのプリミティブ型とそれらのボックス化された型 (
int
、float
、Integer
、Float
など)列挙型は名前にマップされます。
String
java.util.Date
、java.time.LocalDate
、java.time.LocalDateTime
、java.time.LocalTime
データベースでサポートされている場合、上記の型の配列とコレクションは、配列型の列にマップできます。
データベースドライバーが受け入れるもの。
他のエンティティへの参照。これらは 1 対 1 の関連、または埋め込み型とみなされます。1 対 1 の関連エンティティが
id
属性を持つことはオプションです。参照先エンティティのテーブルには、参照元エンティティに基づく名前を持つ追加の列があることが期待されます ( バックリファレンスを参照)。埋め込みエンティティにはid
は必要ありません。存在する場合、特別な意味を持たずに通常の属性としてマップされます。Set<some entity>
は、1 対多の関連と見なされます。参照エンティティのテーブルには、参照エンティティに基づく名前を持つ追加の列が必要です。バックリファレンスを参照してください。Map<simple type, some entity>
は、限定された 1 対多の関連とみなされます。参照されるエンティティのテーブルには 2 つの追加列があることが期待されます。1 つは外部キーの参照エンティティに基づいて名前が付けられ ( バックリファレンスを参照)、もう 1 つは同じ名前でマップキーに追加の_key
サフィックスが付いています。List<some entity>
はMap<Integer, some entity>
としてマップされます。同じ追加の列が期待されており、使用される名前も同じ方法でカスタマイズできます。
List
、Set
、Map
の場合、後方参照の命名は、それぞれ NamingStrategy.getReverseColumnName(RelationalPersistentEntity<?> owner)
および NamingStrategy.getKeyColumn(RelationalPersistentProperty property)
を実装することで制御できます。あるいは、属性に @MappedCollection(idColumn="your_column_name", keyColumn="your_key_column_name")
のアノテーションを付けることもできます。Set
のキー列を指定しても効果はありません。
マッピングアノテーションの概要
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
プロジェクトで定義されます。JDBC サポートでは、アノテーションベースのメタデータをサポートするために特定のサブクラスが使用されます。(需要があれば) 他の戦略も導入できます。
参照エンティティ
参照エンティティの処理は制限されています。これは、上記の集約ルートの考え方に基づいています。別のエンティティを参照する場合、そのエンティティは定義上、集約の一部です。そのため、参照を削除すると、以前に参照されたエンティティが削除されます。これは、参照が 1-1 または 1-n であるが、n-1 または n-m ではないことも意味します。
n-1 または n-m の参照がある場合、定義上、2 つの別個の集約を扱っています。それらの間の参照は、Spring Data JDBC で適切にマップされる単純な id
値としてエンコードされる場合があります。これらをエンコードするより良い方法は、AggregateReference
のインスタンスにすることです。AggregateReference
は、id 値を別の集約への参照としてマークするラッパーです。また、その集約の型は、型パラメーターでエンコードされます。
バックリファレンス
集約内のすべての参照は、データベース内で逆方向の外部キー関連になります。デフォルトでは、外部キー列の名前は参照エンティティのテーブル名です。
あるいは、@Table
アノテーションを無視して、参照エンティティのエンティティ名で名前を付けることも選択できます。この動作を有効にするには、RelationalMappingContext
で setForeignKeyNaming(ForeignKeyNaming.IGNORE_RENAMING)
を呼び出します。
List
および Map
参照の場合、リストインデックスまたはマップキーを保持するために追加の列が必要です。これは、追加の _KEY
サフィックスを持つ外部キー列に基づいています。
これらの逆参照の名前をまったく別の方法で付けたい場合は、必要に応じて NamingStrategy.getReverseColumnName(RelationalPersistentEntity<?> owner)
を実装できます。
AggregateReference
の宣言と設定 class Person {
@Id long id;
AggregateReference<Person, Long> bestFriend;
}
// ...
Person p1, p2 = // some initialization
p1.bestFriend = AggregateReference.to(p2.id);
後方参照やマップやリストのキー列の実際の値を保持する属性をエンティティに含めないでください。これらの値をドメインモデルで使用できるようにしたい場合は、AfterConvertCallback
でこれを実行し、値を一時値に保存することをお勧めします。
ネーミング戦略
慣例により、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;
}
MappedCollection
(Javadoc) アノテーションは、参照型(1 対 1 の関連)またはセット、リスト、マップ(1 対多の関連)で使用できます。アノテーションの idColumn
要素は、他のテーブルの id 列を参照する外部キー列のカスタム名を提供します。次の例では、MySubEntity
クラスに対応するテーブルに NAME
列と、MyEntity
ID の CUSTOM_MY_ENTITY_ID_COLUMN_NAME
列が関連の理由で含まれています。
class MyEntity {
@Id
Integer id;
@MappedCollection(idColumn = "CUSTOM_MY_ENTITY_ID_COLUMN_NAME")
Set<MySubEntity> subEntities;
}
class MySubEntity {
String name;
}
List
および Map
を使用する場合、List
のデータセットの位置または Map
のエンティティのキー値の追加列が必要です。この追加の列名は、MappedCollection
(Javadoc) アノテーションの keyColumn
要素を使用してカスタマイズできます。
class MyEntity {
@Id
Integer id;
@MappedCollection(idColumn = "CUSTOM_COLUMN_NAME", keyColumn = "CUSTOM_KEY_COLUMN_NAME")
List<MySubEntity> name;
}
class MySubEntity {
String name;
}
Spring Data の SpEL サポートを使用して列名を動的に作成できます。生成された名前はキャッシュされるため、マッピングコンテキストごとにのみ動的になります。
埋め込みエンティティ
埋め込みエンティティは、データベースにテーブルが 1 つしかない場合でも、java データモデルに値オブジェクトを保持するために使用されます。次の例では、MyEntity
が @Embedded
アノテーションでマップされています。この結果、データベースには、id
および name
(EmbeddedEntity
クラスの)の 2 つの列を持つテーブル my_entity
が期待されます。
ただし、結果セット内で name
列が実際に null
である場合、@Embedded
の onEmpty
に従って、プロパティ embeddedEntity
全体が null に設定されます。ネストされたすべてのプロパティが null
である場合、null
はオブジェクトになります。
この動作とは反対に、USE_EMPTY
は、デフォルトコンストラクターまたは結果セットから NULL 可能パラメーター値を受け入れるコンストラクターを使用して、新しいインスタンスを作成しようとします。
class MyEntity {
@Id
Integer id;
@Embedded(onEmpty = USE_NULL) (1)
EmbeddedEntity embeddedEntity;
}
class EmbeddedEntity {
String name;
}
1 | null の name の場合、Null s embeddedEntity 。USE_EMPTY を使用して、name プロパティの潜在的な null 値で embeddedEntity をインスタンス化します。 |
エンティティで複数回値オブジェクトが必要な場合は、@Embedded
アノテーションのオプションの prefix
要素を使用してこれを実現できます。この要素はプレフィックスを表し、埋め込みオブジェクトの各列名の先頭に追加されます。
|
Collection
または Map
を含む埋め込みエンティティは、少なくとも空のコレクションまたはマップを含むため、常に空ではないとみなされます。このようなエンティティは、@Embedded(onEmpty = USE_NULL) を使用する場合でも、null
になることはありません。
読み取り専用プロパティ
@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
}
明示的なコンバーターを使用したマッピングのオーバーライド
Spring Data を使用すると、カスタムコンバーターを登録して、データベース内での値のマッピングメソッドに影響を与えることができます。現在、コンバーターはプロパティレベルでのみ適用されます。つまり、ドメイン内の単一の値をデータベース内の単一の値に変換したり、その逆に変換したりすることしかできません。複雑なオブジェクトと複数の列の間の変換はサポートされていません。
登録済みの Spring コンバーターを使用したプロパティの記述
次の例は、Boolean
オブジェクトから String
値に変換する Converter
の実装を示しています。
import org.springframework.core.convert.converter.Converter;
@WritingConverter
public class BooleanToStringConverter implements Converter<Boolean, String> {
@Override
public String convert(Boolean source) {
return source != null && source ? "T" : "F";
}
}
ここで注意すべき点がいくつかあります。Boolean
と String
はどちらも単純な型であるため、Spring Data はこのコンバーターを適用する方向(読み取りまたは書き込み)のヒントを必要とします。このコンバーターに @WritingConverter
のアノテーションを付けることにより、すべての Boolean
プロパティを String
としてデータベースに書き込むように Spring Data に指示します。
Spring コンバーターを使用した読み取り
次の例は、String
から Boolean
値に変換する Converter
の実装を示しています。
@ReadingConverter
public class StringToBooleanConverter implements Converter<String, Boolean> {
@Override
public Boolean convert(String source) {
return source != null && source.equalsIgnoreCase("T") ? Boolean.TRUE : Boolean.FALSE;
}
}
ここで注意すべき点がいくつかあります。String
と Boolean
はどちらも単純な型であるため、Spring Data はこのコンバーターを適用する方向(読み取りまたは書き込み)のヒントを必要とします。このコンバーターに @ReadingConverter
のアノテーションを付けることにより、Spring Data に、Boolean
プロパティに割り当てる必要があるデータベースからのすべての String
値を変換するように指示します。
JdbcConverter
を使用した Spring コンバーターの登録
class MyJdbcConfiguration extends AbstractJdbcConfiguration {
// …
@Override
protected List<?> userConverters() {
return Arrays.asList(new BooleanToStringConverter(), new StringToBooleanConverter());
}
}
以前のバージョンの Spring Data JDBC では、AbstractJdbcConfiguration.jdbcCustomConversions() を直接上書きすることが推奨されていました。この方法では、すべてのデータベースを対象とした変換、使用された Dialect によって登録された変換、ユーザーによって登録された変換がアセンブルされるため、これは不要になり、推奨もされなくなりました。古いバージョンの Spring Data JDBC から移行していて、AbstractJdbcConfiguration.jdbcCustomConversions() が上書きされた場合、Dialect からの変換は登録されません。 |
Spring Boot に依存して Spring Data JDBC をブートストラップしたいが、構成の特定の側面をオーバーライドしたい場合は、その型の Bean を公開するとよいでしょう。カスタム変換の場合は、次のようにすることができます。Boot インフラストラクチャによって選択される、型 |