此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Data Relational 3.4.0spring-doc.cadn.net.cn

映射

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

本节介绍MappingJdbcConverter,包括如何使用约定将对象映射到行,以及如何使用基于注释的映射元数据覆盖这些约定。spring-doc.cadn.net.cn

在继续阅读本章之前,请先阅读有关对象映射基础知识的基础知识。spring-doc.cadn.net.cn

基于约定的映射

MappingJdbcConverter在未提供其他映射元数据时,有一些用于将对象映射到行的约定。 约定是:spring-doc.cadn.net.cn

  • 短 Java 类名按以下方式映射到表名。 这com.bigbank.SavingsAccount类映射到SAVINGS_ACCOUNTtable name 的 相同的名称映射适用于将字段映射到列名称。 例如,firstNamefield 映射到FIRST_NAME列。 您可以通过提供自定义NamingStrategy. 有关更多详细信息,请参阅 映射配置 。 默认情况下,从属性或类名称派生的表名和列名在 SQL 语句中使用,不带引号。 您可以通过设置RelationalMappingContext.setForceQuote(true).spring-doc.cadn.net.cn

  • 该转换器使用任何注册的 Spring ConverterCustomConversions以覆盖对象属性到行列和值的默认映射。spring-doc.cadn.net.cn

  • 对象的字段用于与行中的列相互转换。 公共JavaBean属性。spring-doc.cadn.net.cn

  • 如果你有一个非零参数构造函数,其构造函数参数名称与行的顶级列名匹配,则使用该构造函数。 否则,使用零参数构造函数。 如果有多个非零参数构造函数,则会引发异常。 请参阅 对象创建 以了解更多详细信息。spring-doc.cadn.net.cn

实体中支持的类型

当前支持以下类型的属性:spring-doc.cadn.net.cn

  • 所有基元类型及其装箱类型 (int,float,Integer,Float等)spring-doc.cadn.net.cn

  • 枚举映射到它们的名称。spring-doc.cadn.net.cn

  • Stringspring-doc.cadn.net.cn

  • java.util.Date,java.time.LocalDate,java.time.LocalDateTimejava.time.LocalTimespring-doc.cadn.net.cn

  • 如果你的数据库支持,上述类型的数组和集合可以映射到数组类型的列。spring-doc.cadn.net.cn

  • 您的数据库驱动程序接受的任何内容。spring-doc.cadn.net.cn

  • 对其他实体的引用。 它们被视为一对一关系或嵌入类型。 一对一关系实体可以选择具有id属性。 被引用实体的表应具有一个附加列,其名称基于引用实体,请参阅反向引用。 嵌入的实体不需要id. 如果存在,则将其映射为没有任何特殊含义的普通属性。spring-doc.cadn.net.cn

  • Set<some entity>被视为一对多关系。 被引用实体的表应具有一个附加列,其名称基于引用实体,请参阅反向引用spring-doc.cadn.net.cn

  • Map<simple type, some entity>被视为合格的一对多关系。 被引用实体的表应具有两个附加列:一个基于外键的引用实体命名(请参阅反向引用),另一个具有相同名称和一个附加的列_key映射键的后缀。spring-doc.cadn.net.cn

  • List<some entity>映射为Map<Integer, some entity>.应使用相同的附加列,并且可以以相同的方式自定义使用的名称。spring-doc.cadn.net.cn

List,SetMap可以通过实现NamingStrategy.getReverseColumnName(RelationalPersistentEntity<?> owner)NamingStrategy.getKeyColumn(RelationalPersistentProperty property)分别。 或者,您可以使用@MappedCollection(idColumn="your_column_name", keyColumn="your_key_column_name"). 为Set没有效果。spring-doc.cadn.net.cn

映射注释概述

RelationalConverter可以使用元数据来驱动对象到行的映射。 可以使用以下注释:spring-doc.cadn.net.cn

  • @Id:在字段级别应用以标记主键。spring-doc.cadn.net.cn

  • @Table:在类级别应用,以指示此类是映射到数据库的候选项。 您可以指定存储数据库的表的名称。spring-doc.cadn.net.cn

  • @Transient:默认情况下,所有字段都映射到该行。 此 Comments 将应用它的字段排除在数据库中。 瞬态属性不能在持久性构造函数中使用,因为转换器无法实现构造函数参数的值。spring-doc.cadn.net.cn

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

  • @Value:此注释是 Spring 框架的一部分。 在 Map 框架中,它可以应用于构造函数参数。 这允许你在使用 Spring Expression Language 语句来转换在数据库中检索到的键的值,然后再用于构造域对象。 为了引用给定行的列,必须使用如下表达式:@Value("#root.myProperty")其中 root 引用给定Row.spring-doc.cadn.net.cn

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

  • @Version:在字段级别应用用于乐观锁定,并检查保存作的修改。 值为null (zero)被视为新实体的标记。 初始存储值为zero (one对于原始类型)。 版本在每次更新时都会自动递增。spring-doc.cadn.net.cn

有关进一步的参考,请参见 Optimistic Lockingspring-doc.cadn.net.cn

映射元数据基础架构在单独的spring-data-commons与技术无关的项目。 JDBC 支持中使用了特定的子类来支持基于 Comments 的元数据。 也可以制定其他策略(如果有需求)。spring-doc.cadn.net.cn

引用的实体

对引用实体的处理是有限的。 这是基于上述聚合根的思想。 如果您引用另一个实体,则根据定义,该实体是聚合的一部分。 因此,如果您删除引用,则之前引用的实体将被删除。 这也意味着引用是 1-1 或 1-n,而不是 n-1 或 n-m。spring-doc.cadn.net.cn

如果您有 n-1 或 n-m 引用,则根据定义,您正在处理两个单独的聚合。 它们之间的引用可以编码为简单id值,这些值与 Spring Data JDBC 正确映射。 对这些进行编码的更好方法是使它们成为AggregateReference. 一AggregateReference是 ID 值的包装器,它将该值标记为对其他聚合的引用。 此外,该聚合的类型在 type 参数中编码。spring-doc.cadn.net.cn

返回引用

聚合中的所有引用在数据库中都会导致相反方向的外键关系。 默认情况下,外键列的名称是引用实体的表名。spring-doc.cadn.net.cn

或者,您可以选择让它们以引用实体的实体名称命名,忽略@Table附注。 您可以通过调用setForeignKeyNaming(ForeignKeyNaming.IGNORE_RENAMING)RelationalMappingContext.spring-doc.cadn.net.cn

ListMap引用 需要额外的列来保存 list index 或 map 键。 它基于外键列,带有一个额外的_KEY后缀。spring-doc.cadn.net.cn

如果你想要一种完全不同的方式来命名这些反向引用,你可以实现NamingStrategy.getReverseColumnName(RelationalPersistentEntity<?> owner)以适合您需求的方式。spring-doc.cadn.net.cn

声明和设置AggregateReference
class Person {
	@Id long id;
	AggregateReference<Person, Long> bestFriend;
}

// ...

Person p1, p2 = // some initialization

p1.bestFriend = AggregateReference.to(p2.id);

您不应在实体中包含属性来保存反向引用的实际值,也不应对映射或列表的键列的实际值。 如果您希望这些值在域模型中可用,我们建议在AfterConvertCallback并将值存储在 transient 值中。spring-doc.cadn.net.cn

命名策略

按照惯例, Spring Data 应用NamingStrategy确定默认为 Snake 大小写的表、列和架构名称。 名为firstName成为first_name. 您可以通过提供NamingStrategy在您的应用程序上下文中。spring-doc.cadn.net.cn

覆盖表名称

当表命名策略与数据库表名不匹配时,您可以使用Table注解。 元素value提供自定义表名。 以下示例映射MyEntity类添加到CUSTOM_TABLE_NAME表中:spring-doc.cadn.net.cn

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

    String name;
}

您可以使用 Spring Data 的 SPEL 支持来动态创建表名。 生成后,表名将被缓存,因此它仅根据映射上下文是动态的。spring-doc.cadn.net.cn

覆盖列名称

当列命名策略与数据库表名不匹配时,您可以使用Column注解。 元素value提供自定义列名称。 以下示例映射name属性的MyEntity类添加到CUSTOM_COLUMN_NAME列中:spring-doc.cadn.net.cn

class MyEntity {
    @Id
    Integer id;

    @Column("CUSTOM_COLUMN_NAME")
    String name;
}

MappedCollection注释可用于引用类型 (一对一关系) 或集、列表和映射 (一对多关系)。idColumn元素为引用另一个表中的 id 列的外键列提供自定义名称。 在以下示例中,MySubEntity类具有NAME列和CUSTOM_MY_ENTITY_ID_COLUMN_NAME列的MyEntityid 的 Id 原因:spring-doc.cadn.net.cn

class MyEntity {
    @Id
    Integer id;

    @MappedCollection(idColumn = "CUSTOM_MY_ENTITY_ID_COLUMN_NAME")
    Set<MySubEntity> subEntities;
}

class MySubEntity {
    String name;
}

使用ListMap您必须在List或实体在Map. 这个额外的列名可以使用keyColumn元素的MappedCollection注解:spring-doc.cadn.net.cn

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 支持来动态创建列名。 生成后,名称将被缓存,因此它仅对每个映射上下文是动态的。spring-doc.cadn.net.cn

嵌入的实体

嵌入实体用于在 Java 数据模型中具有值对象,即使数据库中只有一个表也是如此。 在下面的示例中,您将看到MyEntity映射到@Embedded注解。 这样做的结果是,在数据库中,一个表my_entity替换为 2 列idname(来自EmbeddedEntity类)是预期的。spring-doc.cadn.net.cn

但是,如果name列实际上是null在结果集中,整个属性embeddedEntity将根据onEmpty@Embeddednulls 对象(当所有嵌套属性都为null.
与此行为相反
USE_EMPTY尝试使用默认构造函数或接受结果集中可为 null 参数值的构造函数创建新实例。spring-doc.cadn.net.cn

示例 1.嵌入对象示例代码
class MyEntity {

    @Id
    Integer id;

    @Embedded(onEmpty = USE_NULL) (1)
    EmbeddedEntity embeddedEntity;
}

class EmbeddedEntity {
    String name;
}
1 NullsembeddedEntity如果namenull. 用USE_EMPTY实例化embeddedEntity具有潜力nullname财产。

如果您在实体中多次需要 value 对象,这可以通过可选的prefix元素的@Embedded注解。 此元素表示一个前缀,并预置嵌入对象中的每个列名。spring-doc.cadn.net.cn

使用快捷方式@Embedded.Nullable & @Embedded.Empty@Embedded(onEmpty = USE_NULL)@Embedded(onEmpty = USE_EMPTY)减少冗长并同时设置 JSR-305@javax.annotation.Nonnull因此。spring-doc.cadn.net.cn

class MyEntity {

    @Id
    Integer id;

    @Embedded.Nullable (1)
    EmbeddedEntity embeddedEntity;
}
1 的快捷方式@Embedded(onEmpty = USE_NULL).

包含CollectionMap将始终被视为非空,因为它们至少包含空的 collection 或 map。 因此,这样的实体永远不会null即使使用 @Embedded(onEmpty = USE_NULL)。spring-doc.cadn.net.cn

只读属性

注释@ReadOnlyProperty不会被 Spring Data 写入数据库,但它们会在加载实体时被读取。spring-doc.cadn.net.cn

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

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

Insert Only 属性

注释@InsertOnlyProperty将仅在插入作期间由 Spring Data 写入数据库。 对于更新,将忽略这些属性。spring-doc.cadn.net.cn

@InsertOnlyProperty仅支持聚合根。spring-doc.cadn.net.cn

自定义对象构造

映射子系统允许通过使用@PersistenceConstructor注解。要用于 constructor 参数的值按以下方式解析:spring-doc.cadn.net.cn

  • 如果参数使用@Valueannotation 中,将计算给定的表达式,并将结果用作参数值。spring-doc.cadn.net.cn

  • 如果 Java 类型具有其名称与 input 行的给定字段匹配的属性,则其属性信息用于选择要将 input 字段值传递到的相应构造函数参数。 仅当 Java 中存在参数名称信息时,此选项才有效.class文件,您可以通过使用调试信息编译源或使用-parameters的命令行开关javac在 Java 8 中。spring-doc.cadn.net.cn

  • 否则,一个MappingException的 S MissAV.com 的 S Alpha 参数,以指示无法绑定给定的 constructor 参数。spring-doc.cadn.net.cn

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-doc.cadn.net.cn

使用已注册的 Spring 转换器编写属性

以下示例显示了ConverterBooleanobject 设置为String价值:spring-doc.cadn.net.cn

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";
    }
}

这里有几点需要注意:BooleanString都是简单类型,因此 Spring Data 需要提示此转换器应应用的方向(读取或写入)。 通过用@WritingConverter您指示 Spring Data 编写每个Booleanproperty 设置为String在数据库中。spring-doc.cadn.net.cn

使用 Spring 转换器读取

以下示例显示了ConverterString更改为Boolean价值:spring-doc.cadn.net.cn

@ReadingConverter
public class StringToBooleanConverter implements Converter<String, Boolean> {

    @Override
    public Boolean convert(String source) {
        return source != null && source.equalsIgnoreCase("T") ? Boolean.TRUE : Boolean.FALSE;
    }
}

这里有几点需要注意:StringBoolean都是简单类型,因此 Spring Data 需要提示此转换器应应用的方向(读取或写入)。 通过用@ReadingConverter您指示 Spring Data 将每个String值,该值应分配给Boolean财产。spring-doc.cadn.net.cn

使用JdbcConverter

class MyJdbcConfiguration extends AbstractJdbcConfiguration {

    // …

    @Override
    protected List<?> userConverters() {
	return Arrays.asList(new BooleanToStringConverter(), new StringToBooleanConverter());
    }

}
在早期版本的 Spring Data JDBC 中,建议直接覆盖AbstractJdbcConfiguration.jdbcCustomConversions(). 这不再是必需的,甚至不建议这样做,因为该方法会组装用于所有数据库的转换,由Dialectused 和用户注册的转化。 如果您从旧版本的 Spring Data JDBC 迁移,并且具有AbstractJdbcConfiguration.jdbcCustomConversions()覆盖Dialect不会注册。

如果你想依靠 Spring Boot 来引导 Spring Data JDBC,但仍然想覆盖配置的某些方面,你可能想要公开该类型的 bean。 对于自定义转换,您可以选择注册JdbcCustomConversions这将被 Boot 基础设施获取。 要了解有关此内容的更多信息,请务必阅读 Spring Boot 参考文档spring-doc.cadn.net.cn

Jdbc值

价值转化用途JdbcValue来丰富传播到 JDBC作的值,请使用java.sql.Types类型。 如果您需要指定特定于 JDBC 的类型而不是使用类型派生,请注册自定义写入转换器。 此转换器应将值转换为JdbcValue它有一个 value 和 actualJDBCType.spring-doc.cadn.net.cn