对于最新的稳定版本,请使用 Spring Data Relational 3.3.1Spring中文文档

对于最新的稳定版本,请使用 Spring Data Relational 3.3.1Spring中文文档

丰富的映射支持由 . 具有丰富的元数据模型,允许将域对象映射到数据行。 映射元数据模型是通过在域对象上使用注释来填充的。 但是,基础结构并不局限于使用注释作为元数据信息的唯一来源。 还允许您通过遵循一组约定将对象映射到行,而无需提供任何其他元数据。MappingR2dbcConverterMappingR2dbcConverterMappingR2dbcConverterSpring中文文档

本节介绍 的功能,包括如何使用约定将对象映射到行,以及如何使用基于注释的映射元数据覆盖这些约定。MappingR2dbcConverterSpring中文文档

在继续本章之前,请阅读有关对象映射基础知识的基础知识。Spring中文文档

基于约定的映射

MappingR2dbcConverter有一些约定,用于在未提供其他映射元数据时将对象映射到行。 约定是:Spring中文文档

  • 短 Java 类名按以下方式映射到表名。 该类映射到表名。 将字段映射到列名时应用相同的名称映射。 例如,字段映射到列。 您可以通过提供自定义 . 有关详细信息,请参阅映射配置。 默认情况下,从属性或类名派生的表名和列名在 SQL 语句中使用,不带引号。 您可以通过设置 来控制此行为。com.bigbank.SavingsAccountSAVINGS_ACCOUNTfirstNameFIRST_NAMENamingStrategyRelationalMappingContext.setForceQuote(true)Spring中文文档

  • 不支持嵌套对象。Spring中文文档

  • 转换器使用任何注册的 Spring Converter 来覆盖对象属性到行列和值的默认映射。CustomConversionsSpring中文文档

  • 对象的字段用于在行中的列之间相互转换。 不使用公共属性。JavaBeanSpring中文文档

  • 如果有一个非零参数构造函数,其构造函数参数名称与行的顶级列名称匹配,则使用该构造函数。 否则,将使用零参数构造函数。 如果存在多个非零参数构造函数,则会引发异常。 有关详细信息,请参阅对象创建Spring中文文档

映射配置

默认情况下,(除非显式配置)实例是在创建 . 您可以创建自己的 . 通过创建自己的实例,您可以注册 Spring 转换器以将特定类映射到数据库或从数据库映射特定类。MappingR2dbcConverterDatabaseClientMappingR2dbcConverterSpring中文文档

您可以使用基于 Java 的元数据来配置 和 & 以下示例使用 Spring 基于 Java 的配置:MappingR2dbcConverterDatabaseClientConnectionFactorySpring中文文档

如果设置了 true,则从类和属性派生的表名和列名将与数据库特定的引号一起使用。 这意味着可以在这些名称中使用保留的 SQL 字(例如 order)。 您可以通过重写 来执行此操作。 Spring Data 将此类名称的字母大小写转换为未使用引号时配置的数据库也使用的形式。 因此,在创建表时可以使用不带引号的名称,只要名称中不使用关键字或特殊字符即可。 对于遵循 SQL 标准的数据库,这意味着名称将转换为大写。 引号字符和名称大写的方式由使用的 . 请参阅 R2DBC 驱动程序,了解如何配置自定义方言。setForceQuoteR2dbcMappingContext tor2dbcMappingContext(Optional<NamingStrategy>)AbstractR2dbcConfigurationDialectSpring中文文档

@Configuration类来配置 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要求您实现定义 .ConnectionFactorySpring中文文档

您可以通过重写该方法将其他转换器添加到转换器中。r2dbcCustomConversionsSpring中文文档

您可以通过将自定义项注册为 Bean 来配置自定义项。 控制如何将类和属性的名称转换为表和列的名称。NamingStrategyNamingStrategySpring中文文档

AbstractR2dbcConfiguration创建一个实例,并将其注册到名为 的容器中。DatabaseClientdatabaseClient
AbstractR2dbcConfiguration创建一个实例,并将其注册到名为 的容器中。DatabaseClientdatabaseClient

基于元数据的映射

要充分利用Spring Data R2DBC支持中的对象映射功能,应使用注释对映射对象进行注释。 尽管映射框架没有必要具有此注释(即使没有任何注释,您的 POJO 也会正确映射),但它允许类路径扫描程序查找并预处理您的域对象以提取必要的元数据。 如果不使用此批注,则应用程序在首次存储域对象时会受到轻微的性能影响,因为映射框架需要构建其内部元数据模型,以便了解域对象的属性以及如何持久化它们。 下面的示例显示了一个域对象:@TableSpring中文文档

示例域对象
package com.mycompany.domain;

@Table
public class Person {

  @Id
  private Long id;

  private Integer ssn;

  private String firstName;

  private String lastName;
}
注释告诉映射器要将哪个属性用作主键。@Id

默认类型映射

下表说明了实体的属性类型如何影响映射:Spring中文文档

源类型 目标类型 言论

基元类型和包装器类型Spring中文文档

PassthruSpring中文文档

可以使用显式转换器进行自定义。Spring中文文档

JSR-310 日期/时间类型Spring中文文档

PassthruSpring中文文档

可以使用显式转换器进行自定义。Spring中文文档

StringBigIntegerBigDecimalUUIDSpring中文文档

PassthruSpring中文文档

可以使用显式转换器进行自定义。Spring中文文档

EnumSpring中文文档

字符串Spring中文文档

可以通过注册显式转换器进行自定义。Spring中文文档

BlobClobSpring中文文档

PassthruSpring中文文档

可以使用显式转换器进行自定义。Spring中文文档

byte[],ByteBufferSpring中文文档

PassthruSpring中文文档

被视为二进制有效载荷。Spring中文文档

Collection<T>Spring中文文档

数组TSpring中文文档

如果配置的驱动程序支持,则转换为数组类型,否则不支持。Spring中文文档

基元类型、包装类型和StringSpring中文文档

包装器类型的数组(例如 →int[]Integer[])Spring中文文档

如果配置的驱动程序支持,则转换为数组类型,否则不支持。Spring中文文档

特定于驱动程序的类型Spring中文文档

PassthruSpring中文文档

由使用的 .R2dbcDialectSpring中文文档

复杂对象Spring中文文档

目标类型取决于已注册的 。ConverterSpring中文文档

需要显式转换器,否则不支持。Spring中文文档

列的本机数据类型取决于 R2DBC 驱动程序类型映射。 驱动程序可以提供其他简单类型,例如几何类型。

映射注释概述

可以使用元数据来驱动对象到行的映射。 可以使用以下注释:RelationalConverterSpring中文文档

  • @Id:应用于字段级别以标记主键。Spring中文文档

  • @Table:在类级别应用,以指示此类是映射到数据库的候选项。 您可以指定存储数据库的表的名称。Spring中文文档

  • @Transient:默认情况下,所有字段都映射到该行。 此批注将应用它的字段排除在数据库中。 暂时性属性不能在持久性构造函数中使用,因为转换器无法实现构造函数参数的值。Spring中文文档

  • @PersistenceCreator:标记给定的构造函数或静态工厂方法(甚至是受包保护的方法)在从数据库实例化对象时使用。 构造函数参数按名称映射到检索到的行中的值。Spring中文文档

  • @Value:此注解是 Spring 框架的一部分。 在映射框架中,它可以应用于构造函数参数。 这允许您使用 Spring Expression Language 语句来转换在数据库中检索到的键的值,然后再将其用于构造域对象。 为了引用给定行的列,必须使用如下表达式: 其中 root 引用给定行的根。@Value("#root.myProperty")RowSpring中文文档

  • @Column:在字段级别应用,以描述列的名称,因为它在行中表示,使名称与类的字段名称不同。 在 SQL 语句中使用时,使用批注指定的名称始终加引号。 对于大多数数据库,这意味着这些名称区分大小写。 这也意味着您可以在这些名称中使用特殊字符。 但是,不建议这样做,因为它可能会导致其他工具出现问题。@ColumnSpring中文文档

  • @Version:应用于字段级别用于乐观锁定,并检查保存操作的修改。 该值 ( 对于基元类型) 被视为新实体的标记。 初始存储的值为 ( 对于基元类型)。 每次更新时,版本都会自动递增。 有关进一步参考,请参阅乐观锁定nullzerozerooneSpring中文文档

映射元数据基础结构在与技术无关的单独项目中定义。 R2DBC 支持中使用特定的子类来支持基于注释的元数据。 也可以制定其他策略(如果有需求)。spring-data-commonsSpring中文文档

注释告诉映射器要将哪个属性用作主键。@Id
源类型 目标类型 言论

基元类型和包装器类型Spring中文文档

PassthruSpring中文文档

可以使用显式转换器进行自定义。Spring中文文档

JSR-310 日期/时间类型Spring中文文档

PassthruSpring中文文档

可以使用显式转换器进行自定义。Spring中文文档

StringBigIntegerBigDecimalUUIDSpring中文文档

PassthruSpring中文文档

可以使用显式转换器进行自定义。Spring中文文档

EnumSpring中文文档

字符串Spring中文文档

可以通过注册显式转换器进行自定义。Spring中文文档

BlobClobSpring中文文档

PassthruSpring中文文档

可以使用显式转换器进行自定义。Spring中文文档

byte[],ByteBufferSpring中文文档

PassthruSpring中文文档

被视为二进制有效载荷。Spring中文文档

Collection<T>Spring中文文档

数组TSpring中文文档

如果配置的驱动程序支持,则转换为数组类型,否则不支持。Spring中文文档

基元类型、包装类型和StringSpring中文文档

包装器类型的数组(例如 →int[]Integer[])Spring中文文档

如果配置的驱动程序支持,则转换为数组类型,否则不支持。Spring中文文档

特定于驱动程序的类型Spring中文文档

PassthruSpring中文文档

由使用的 .R2dbcDialectSpring中文文档

复杂对象Spring中文文档

目标类型取决于已注册的 。ConverterSpring中文文档

需要显式转换器,否则不支持。Spring中文文档

列的本机数据类型取决于 R2DBC 驱动程序类型映射。 驱动程序可以提供其他简单类型,例如几何类型。

命名策略

按照惯例,Spring Data 应用 a 来确定默认为蛇形大小写的表、列和架构名称。 名为 的对象属性变为 。 您可以通过在应用程序上下文中提供 NamingStrategy 来调整它。NamingStrategyfirstNamefirst_nameSpring中文文档

覆盖表名称

当表命名策略与数据库表名称不匹配时,可以使用@Table注释覆盖表名称。 此批注的元素提供自定义表名。 下面的示例将类映射到数据库中的表:valueMyEntityCUSTOM_TABLE_NAMESpring中文文档

@Table("CUSTOM_TABLE_NAME")
class MyEntity {
    @Id
    Integer id;

    String name;
}

覆盖列名称

当列命名策略与数据库表名称不匹配时,可以使用@Column注释覆盖表名称。 此批注的元素提供自定义列名。 下面的示例将类的属性映射到数据库中的列:valuenameMyEntityCUSTOM_COLUMN_NAMESpring中文文档

class MyEntity {
    @Id
    Integer id;

    @Column("CUSTOM_COLUMN_NAME")
    String name;
}

只读属性

Spring Data 不会将带有注释的属性写入数据库,但在加载实体时会读取它们。@ReadOnlyPropertySpring中文文档

Spring Data 在写入实体后不会自动重新加载实体。 因此,如果要查看数据库中为此类列生成的数据,则必须显式重新加载它。Spring中文文档

如果带批注的属性是实体或实体集合,则它由单独表中的一个或多个单独行表示。 Spring Data 不会对这些行执行任何插入、删除或更新。Spring中文文档

“仅插入”属性

注释的属性只会在插入操作期间由 Spring Data 写入数据库。 对于更新,这些属性将被忽略。@InsertOnlyPropertySpring中文文档

@InsertOnlyProperty仅聚合根支持。Spring中文文档

客制化对象构造

映射子系统允许通过使用注释注释构造函数来自定义对象构造。要用于构造函数参数的值按以下方式解析:@PersistenceConstructorSpring中文文档

  • 如果使用注释对参数进行注释,则对给定的表达式进行计算,并将结果用作参数值。@ValueSpring中文文档

  • 如果 Java 类型的属性的名称与输入行的给定字段匹配,则其属性信息将用于选择要将输入字段值传递到的相应构造函数参数。 仅当 Java 文件中存在参数名称信息时,这才有效,您可以通过使用调试信息编译源代码或使用 Java 8 中的命令行开关来实现。.class-parametersjavacSpring中文文档

  • 否则,将抛出 a 以指示无法绑定给定的构造函数参数。MappingExceptionSpring中文文档

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
}

使用显式转换器重写映射

在存储和查询对象时,通常有一个实例来处理所有 Java 类型到实例的映射会很方便。 但是,您有时可能希望实例完成大部分工作,但允许您有选择地处理特定类型的转换,也许是为了优化性能。R2dbcConverterOutboundRowR2dbcConverterSpring中文文档

要自己有选择地处理转换,请向 注册一个或多个实例。org.springframework.core.convert.converter.ConverterR2dbcConverterSpring中文文档

您可以使用 中的方法配置转换器。 本章开头的示例展示了如何使用 Java 执行配置。r2dbcCustomConversionsAbstractR2dbcConfigurationSpring中文文档

自定义顶级实体转换需要非对称类型进行转换。 入站数据是从 R2DBC 的 . 出站数据(与 / 语句一起使用)表示为语句,然后组合到语句中。RowINSERTUPDATEOutboundRow

以下 Spring Converter 实现示例从 a 转换为 POJO:RowPersonSpring中文文档

@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>>, OutboundRowSpring中文文档

R2DBC 使用盒装基元(而不是 )返回基元值。Integer.classint.class

以下示例从 a 转换为 a:PersonOutboundRowSpring中文文档

@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)可以使用其特定于数据库的枚举列类型本机写入枚举值。 Spring Data 默认将值转换为值,以实现最大的可移植性。 若要保留实际枚举值,请注册一个转换器,其源和目标类型使用实际枚举类型,以避免使用转换。 此外,还需要在驱动程序级别配置枚举类型,以便驱动程序知道如何表示枚举类型。EnumString@WritingEnum.name()Spring中文文档

以下示例显示了以本机方式读取和写入枚举值的相关组件:ColorSpring中文文档

enum Color {
    Grey, Blue
}

class ColorConverter extends EnumWriteSupport<Color> {

}


class Product {
    @Id long id;
    Color color;

    // …
}
自定义顶级实体转换需要非对称类型进行转换。 入站数据是从 R2DBC 的 . 出站数据(与 / 语句一起使用)表示为语句,然后组合到语句中。RowINSERTUPDATEOutboundRow
R2DBC 使用盒装基元(而不是 )返回基元值。Integer.classint.class