Mapping
MappingR2dbcConverter
提供丰富的映射支持。 MappingR2dbcConverter
具有丰富的元数据模型,可将域对象映射到数据行。使用域对象上的注释填充映射元数据模型。但是,该基础设施不限于使用注释作为元数据信息的唯一来源。 MappingR2dbcConverter
还允许您将对象映射到行,而无需提供任何其他元数据,方法是遵循一组约定。
Rich mapping support is provided by the MappingR2dbcConverter
. MappingR2dbcConverter
has a rich metadata model that allows mapping domain objects to a data row.
The mapping metadata model is populated by using annotations on your domain objects.
However, the infrastructure is not limited to using annotations as the only source of metadata information.
The MappingR2dbcConverter
also lets you map objects to rows without providing any additional metadata, by following a set of conventions.
本节介绍 MappingR2dbcConverter
的功能,包括如何使用约定将对象映射到行以及如何使用基于注释的映射元数据覆盖这些约定。
This section describes the features of the MappingR2dbcConverter
, including how to use conventions for mapping objects to rows and how to override those conventions with annotation-based mapping metadata.
在继续本章之前,阅读有关 object-mapping.adoc的基础知识。
Read on the basics about object-mapping.adoc before continuing with this chapter.
Convention-based Mapping
当未提供其他映射元数据时, MappingR2dbcConverter
有一些约定用于将对象映射到行。这些约定是:
MappingR2dbcConverter
has a few conventions for mapping objects to rows when no additional mapping metadata is provided.
The conventions are:
-
The short Java class name is mapped to the table name in the following manner. The
com.bigbank.SavingsAccount
class maps to theSAVINGS_ACCOUNT
table name. The same name mapping is applied for mapping fields to column names. For example, thefirstName
field maps to theFIRST_NAME
column. You can control this mapping by providing a customNamingStrategy
. See mapping.configuration for more detail. Table and column names that are derived from property or class names are used in SQL statements without quotes by default. You can control this behavior by settingRelationalMappingContext.setForceQuote(true)
. -
Nested objects are not supported.
-
The converter uses any Spring Converters registered with
CustomConversions
to override the default mapping of object properties to row columns and values. -
The fields of an object are used to convert to and from columns in the row. Public
JavaBean
properties are not used. -
If you have a single non-zero-argument constructor whose constructor argument names match top-level column names of the row, that constructor is used. Otherwise, the zero-argument constructor is used. If there is more than one non-zero-argument constructor, an exception is thrown. Refer to Object Creation for further details.
Mapping Configuration
默认情况下(除非明确配置),当您创建 DatabaseClient
时,将创建 MappingR2dbcConverter
的实例。您可以创建自己的 MappingR2dbcConverter
实例。通过创建自己的实例,您可以注册 Spring 转换器以将特定类映射到数据库并从数据库映射。
By default, (unless explicitly configured) an instance of MappingR2dbcConverter
is created when you create a DatabaseClient
.
You can create your own instance of the MappingR2dbcConverter
.
By creating your own instance, you can register Spring converters to map specific classes to and from the database.
您可以使用基于 Java 的元数据配置 MappingR2dbcConverter
以及 DatabaseClient
和 ConnectionFactory
。以下示例使用基于 Spring 的 Java 配置:
You can configure the MappingR2dbcConverter
as well as DatabaseClient
and ConnectionFactory
by using Java-based metadata.
The following example uses Spring’s Java-based configuration:
如果您将 R2dbcMappingContext to
的 setForceQuote
设置为 true,则从类和属性派生的表和列名将与特定数据库的引号一起使用。这意味着在这些名称中可以使用保留的 SQL 单词(例如 order)。您可以通过覆盖 AbstractR2dbcConfiguration
的 r2dbcMappingContext(Optional<NamingStrategy>)
来实现。Spring Data 将此类名称的字母大小写转换为在不使用引号时配置的数据库也使用的形式。因此,只要您的名称中不使用关键字或特殊字符,您就可以在创建表时使用不带引号的名称。对于遵守 SQL 标准的数据库,这意味着名称将转换为大写。引号字符和名称的大写方式由所用的 Dialect
控制。有关如何配置自定义方言,请参见 R2DBC Drivers。
If you set setForceQuote
of the R2dbcMappingContext to
true, table and column names derived from classes and properties are used with database specific quotes.
This means that it is OK to use reserved SQL words (such as order) in these names.
You can do so by overriding r2dbcMappingContext(Optional<NamingStrategy>)
of AbstractR2dbcConfiguration
.
Spring Data converts the letter casing of such a name to that form which is also used by the configured database when no quoting is used.
Therefore, you can use unquoted names when creating tables, as long as you do not use keywords or special characters in your names.
For databases that adhere to the SQL standard, this means that names are converted to upper case.
The quoting character and the way names get capitalized is controlled by the used Dialect
.
See R2DBC Drivers 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
的方法。
AbstractR2dbcConfiguration
requires you to implement a method that defines a ConnectionFactory
.
可以通过覆盖 r2dbcCustomConversions
方法向转换器添加其他转换器。
You can add additional converters to the converter by overriding the r2dbcCustomConversions
method.
您可以通过将其注册为 Bean 来配置自定义 NamingStrategy
。 NamingStrategy
控制类和属性的名称如何转换为表和列的名称。
You can configure a custom NamingStrategy
by registering it as a bean.
The NamingStrategy
controls how the names of classes and properties get converted to the names of tables and columns.
|
|
Metadata-based Mapping
要充分利用 Spring Data R2DBC 支持中的对象映射功能,您应该使用 @Table
注释为您的映射对象进行注释。虽然映射框架不需要此注释(即使没有任何注释,您的 POJO 也映射正确),但它可以让类路径扫描器查找和预处理您的域对象以提取必要的元数据。如果您不使用此注释,则在您首次存储域对象时,您的应用程序会略微受到性能影响,因为映射框架需要构建其内部元数据模型,以便它知道您的域对象的属性以及如何持久它们。以下示例显示了一个域对象:
To take full advantage of the object mapping functionality inside the Spring Data R2DBC support, you should annotate your mapped objects with the @Table
annotation.
Although it is not necessary for the mapping framework to have this annotation (your POJOs are mapped correctly, even without any annotations), it lets the classpath scanner find and pre-process your domain objects to extract the necessary metadata.
If you do not use this annotation, your application takes a slight performance hit the first time you store a domain object, because the mapping framework needs to build up its internal metadata model so that it knows about the properties of your domain object and how to persist them.
The following example shows a domain object:
package com.mycompany.domain;
@Table
public class Person {
@Id
private Long id;
private Integer ssn;
private String firstName;
private String lastName;
}
@Id
注释告诉映射器您希望使用哪个属性作为主键。
The @Id
annotation tells the mapper which property you want to use as the primary key.
Default Type Mapping
下表解释了实体的属性类型如何影响映射:
The following table explains how property types of an entity affect mapping:
Source Type | Target Type | Remarks |
---|---|---|
Primitive types and wrapper types |
Passthru |
Can be customized using mapping.explicit.converters. |
JSR-310 Date/Time types |
Passthru |
Can be customized using mapping.explicit.converters. |
|
Passthru |
Can be customized using mapping.explicit.converters. |
|
String |
Can be customized by registering mapping.explicit.converters. |
|
Passthru |
Can be customized using mapping.explicit.converters. |
|
Passthru |
Considered a binary payload. |
|
Array of |
Conversion to Array type if supported by the configured driver, not supported otherwise. |
Arrays of primitive types, wrapper types and |
Array of wrapper type (e.g. |
Conversion to Array type if supported by the configured driver, not supported otherwise. |
Driver-specific types |
Passthru |
Contributed as a simple type by the used |
Complex objects |
Target type depends on registered |
Requires a mapping.explicit.converters, not supported otherwise. |
列的本机数据类型取决于 R2DBC 驱动程序类型映射。驱动程序可以提供其他简单类型,例如 Geometry 类型。 |
The native data type for a column depends on the R2DBC driver type mapping. Drivers can contribute additional simple types such as Geometry types. |
Mapping Annotation Overview
`RelationalConverter`可以使用元数据来驱动对象到行的映射。下列注解是可用的:
The RelationalConverter
can use metadata to drive the mapping of objects to rows.
The following annotations are available:
-
@Id
: Applied at the field level to mark the primary key. -
@Table
: Applied at the class level to indicate this class is a candidate for mapping to the database. You can specify the name of the table where the database is stored. -
@Transient
: By default, all fields are mapped to the row. This annotation excludes the field where it is applied from being stored in the database. Transient properties cannot be used within a persistence constructor as the converter cannot materialize a value for the constructor argument. -
@PersistenceCreator
: Marks a given constructor or static factory method — even a package protected one — to use when instantiating the object from the database. Constructor arguments are mapped by name to the values in the retrieved row. -
@Value
: This annotation is part of the Spring Framework. Within the mapping framework it can be applied to constructor arguments. This lets you use a Spring Expression Language statement to transform a key’s value retrieved in the database before it is used to construct a domain object. In order to reference a column of a given row one has to use expressions like:@Value("#root.myProperty")
where root refers to the root of the givenRow
. -
@Column
: Applied at the field level to describe the name of the column as it is represented in the row, letting the name be different from the field name of the class. Names specified with a@Column
annotation are always quoted when used in SQL statements. For most databases, this means that these names are case-sensitive. It also means that you can use special characters in these names. However, this is not recommended, since it may cause problems with other tools. -
@Version
: Applied at field level is used for optimistic locking and checked for modification on save operations. The value isnull
(zero
for primitive types) is considered as marker for entities to be new. The initially stored value iszero
(one
for primitive types). The version gets incremented automatically on every update.
`RelationalConverter`可以使用元数据来驱动对象到行的映射。下列注解是可用的:
The RelationalConverter
can use metadata to drive the mapping of objects to rows.
The following annotations are available:
-
@Id
: Applied at the field level to mark the primary key. -
@Table
: Applied at the class level to indicate this class is a candidate for mapping to the database. You can specify the name of the table where the database is stored. -
@Transient
: By default, all fields are mapped to the row. This annotation excludes the field where it is applied from being stored in the database. Transient properties cannot be used within a persistence constructor as the converter cannot materialize a value for the constructor argument. -
@PersistenceCreator
: Marks a given constructor or static factory method — even a package protected one — to use when instantiating the object from the database. Constructor arguments are mapped by name to the values in the retrieved row. -
@Value
: This annotation is part of the Spring Framework. Within the mapping framework it can be applied to constructor arguments. This lets you use a Spring Expression Language statement to transform a key’s value retrieved in the database before it is used to construct a domain object. In order to reference a column of a given row one has to use expressions like:@Value("#root.myProperty")
where root refers to the root of the givenRow
. -
@Column
: Applied at the field level to describe the name of the column as it is represented in the row, letting the name be different from the field name of the class. Names specified with a@Column
annotation are always quoted when used in SQL statements. For most databases, this means that these names are case-sensitive. It also means that you can use special characters in these names. However, this is not recommended, since it may cause problems with other tools. -
@Version
: Applied at field level is used for optimistic locking and checked for modification on save operations. The value isnull
(zero
for primitive types) is considered as marker for entities to be new. The initially stored value iszero
(one
for primitive types). The version gets incremented automatically on every update. See Optimistic Locking for further reference.
映射元数据基础架构在独立的 spring-data-commons
项目中定义,该项目与技术无关。R2DBC 支持中使用了特定的子类以支持基于注释的元数据。还可以制定其他策略(如果有需求)。
The mapping metadata infrastructure is defined in the separate spring-data-commons
project that is technology-agnostic.
Specific subclasses are used in the R2DBC support to support annotation based metadata.
Other strategies can also be put in place (if there is demand).
Naming Strategy
Naming Strategy
根据习惯,Spring Data 应用了一个 NamingStrategy
,以确定表、列和模式名称,其默认为 snake case。名为 firstName`的对象属性会成为 `first_name
。您可以通过在应用程序上下文中提供 {spring-data-jdbc-javadoc}org/springframework/data/relational/core/mapping/NamingStrategy.html[NamingStrategy
] 来调整它。
By convention, Spring Data applies a NamingStrategy
to determine table, column, and schema names defaulting to snake case.
An object property named firstName
becomes first_name
.
You can tweak that by providing a {spring-data-jdbc-javadoc}org/springframework/data/relational/core/mapping/NamingStrategy.html[NamingStrategy
] in your application context.
Override table names
当表命名策略不匹配您的数据库表名称时,您可以使用 {spring-data-jdbc-javadoc}org/springframework/data/relational/core/mapping/Table.html[@Table
] 注解覆盖表名称。此注解的 `value`元素提供了自定义表名称。以下示例将 `MyEntity`类映射到数据库中的 `CUSTOM_TABLE_NAME`表:
When the table naming strategy does not match your database table names, you can override the table name with the {spring-data-jdbc-javadoc}org/springframework/data/relational/core/mapping/Table.html[@Table
] annotation.
The element value
of this annotation provides the custom table name.
The following example maps the MyEntity
class to the CUSTOM_TABLE_NAME
table in the database:
@Table("CUSTOM_TABLE_NAME")
class MyEntity {
@Id
Integer id;
String name;
}
Override column names
当列命名策略不匹配您的数据库表名称时,您可以使用 {spring-data-jdbc-javadoc}org/springframework/data/relational/core/mapping/Column.html[@Column
] 注解覆盖表名称。此注解的 `value`元素提供了自定义列名称。以下示例将 `MyEntity`类的 `name`属性映射到数据库中的 `CUSTOM_COLUMN_NAME`列:
When the column naming strategy does not match your database table names, you can override the table name with the {spring-data-jdbc-javadoc}org/springframework/data/relational/core/mapping/Column.html[@Column
] annotation.
The element value
of this annotation provides the custom column name.
The following example maps the name
property of the MyEntity
class to the CUSTOM_COLUMN_NAME
column in the database:
class MyEntity {
@Id
Integer id;
@Column("CUSTOM_COLUMN_NAME")
String name;
}
Read Only Properties
使用 @ReadOnlyProperty
注解的属性不会被 Spring Data 写入到数据库中,但当加载实体时,这些属性将会被读取。
Attributes annotated with @ReadOnlyProperty
will not be written to the database by Spring Data, but they will be read when an entity gets loaded.
Spring Data 不会自动在写入实体后重新加载此实体。因此,如果你想要查看在数据库中为这些列生成的数据,那么就必须显式地重新加载此实体。
Spring Data will not automatically reload an entity after writing it. Therefore, you have to reload it explicitly if you want to see data that was generated in the database for such columns.
如果带注解的属性是实体或实体集合,则它将由各单独表中的一个或者若干单独行来表示。Spring Data 不会针对这些行执行任何插入、删除或者更新。
If the annotated attribute is an entity or collection of entities, it is represented by one or more separate rows in separate tables. Spring Data will not perform any insert, delete or update for these rows.
Insert Only Properties
使用 @InsertOnlyProperty
注解的属性仅在插入操作期间由 Spring Data 写入到数据库中。对于更新,将忽略这些属性。
Attributes annotated with @InsertOnlyProperty
will only be written to the database by Spring Data during insert operations.
For updates these properties will be ignored.
@InsertOnlyProperty
仅受支持于聚合根。
@InsertOnlyProperty
is only supported for the aggregate root.
Customized Object Construction
映射子系统允许通过使用 @PersistenceConstructor
注解注解构造函数来自定义对象构造。用于构造函数参数的值将按照以下方式解析:
The mapping subsystem allows the customization of the object construction by annotating a constructor with the @PersistenceConstructor
annotation.The values to be used for the constructor parameters are resolved in the following way:
-
If a parameter is annotated with the
@Value
annotation, the given expression is evaluated, and the result is used as the parameter value. -
If the Java type has a property whose name matches the given field of the input row, then its property information is used to select the appropriate constructor parameter to which to pass the input field value. This works only if the parameter name information is present in the Java
.class
files, which you can achieve by compiling the source with debug information or using the-parameters
command-line switch forjavac
in Java 8. -
Otherwise, a
MappingException
is thrown to indicate that the given constructor parameter could not be bound.
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
}
Overriding Mapping with Explicit Converters
在存储和查询对象时,通常方便地使用 R2dbcConverter
实例来处理所有 Java 类型到 OutboundRow
实例的映射。但是,您有时可能希望 R2dbcConverter
实例完成大部分工作,但让您有选择地处理特定类型的转换——也许是为了优化性能。
When storing and querying your objects, it is often convenient to have a R2dbcConverter
instance to handle the mapping of all Java types to OutboundRow
instances.
However, you may sometimes want the R2dbcConverter
instances to do most of the work but let you selectively handle the conversion for a particular type — perhaps to optimize performance.
要自己有选择地处理转换,请使用 org.springframework.core.convert.converter.Converter
实例向 R2dbcConverter
注册一个或多个实例。
To selectively handle the conversion yourself, register one or more one or more org.springframework.core.convert.converter.Converter
instances with the R2dbcConverter
.
您可以在 AbstractR2dbcConfiguration
中使用 r2dbcCustomConversions
方法配置转换器。示例 at the beginning of this chapter 展示了如何使用 Java 执行配置。
You can use the r2dbcCustomConversions
method in AbstractR2dbcConfiguration
to configure converters.
The examples at the beginning of this chapter show how to perform the configuration with Java.
自定义顶级实体转换需要不对称类型进行转换。入站数据从 R2DBC 的 |
Custom top-level entity conversion requires asymmetric types for conversion.
Inbound data is extracted from R2DBC’s |
以下 Spring 转换器实现的示例将 Row
转换为 Person
POJO:
The following example of a Spring Converter implementation converts from a Row
to a 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
)。
Please note that converters get applied on singular properties.
Collection properties (e.g. Collection<Person>
) are iterated and converted element-wise.
Collection converters (e.g. Converter<List<Person>>, OutboundRow
) are not supported.
R2DBC 使用装箱原语( |
R2DBC uses boxed primitives ( |
以下示例将 Person
转换为 OutboundRow
:
The following example converts from a Person
to a 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;
}
}
Overriding Enum Mapping with Explicit Converters
某些数据库,例如 Postgres,可以使用其特定于数据库的枚举列类型本机编写枚举值。Spring Data 默认将 `Enum`值转换为 `String`值以实现最大的可移植性。要保留实际枚举值,请注册一个 `@Writing`转换器,其源类型和目标类型使用实际枚举类型以避免使用 `Enum.name()`转换。此外,你需要在驱动程序级别配置枚举类型,以便驱动程序知道如何表示枚举类型。
Some databases, such as Postgres, 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 @Writing
converter 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
枚举值的组件:
The following example shows the involved components to read and write Color
enum values natively:
enum Color {
Grey, Blue
}
class ColorConverter extends EnumWriteSupport<Color> {
}
class Product {
@Id long id;
Color color;
// …
}