此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Data Relational 3.4.0! |
映射
丰富的映射支持由 . 具有丰富的元数据模型,允许将域对象映射到数据行。
映射元数据模型是通过在域对象上使用注释来填充的。
但是,基础架构不仅限于使用注释作为元数据信息的唯一来源。
还允许您通过遵循一组约定将对象映射到行,而无需提供任何其他元数据。BasicJdbcConverter
BasicJdbcConverter
BasicJdbcConverter
本节介绍了 的功能,包括如何使用约定将对象映射到行,以及如何使用基于注释的映射元数据覆盖这些约定。BasicJdbcConverter
在继续阅读本章之前,请先阅读有关对象映射基础知识的基础知识。
基于约定的映射
BasicJdbcConverter
在未提供其他映射元数据时,有一些用于将对象映射到行的约定。
约定是:
-
短 Java 类名按以下方式映射到表名。 类映射到表名。 相同的名称映射适用于将字段映射到列名称。 例如,字段映射到列。 您可以通过提供自定义 . 有关更多详细信息,请参阅 映射配置 。 默认情况下,从属性或类名称派生的表名和列名在 SQL 语句中使用,不带引号。 您可以通过设置 来控制此行为。
com.bigbank.SavingsAccount
SAVINGS_ACCOUNT
firstName
FIRST_NAME
NamingStrategy
RelationalMappingContext.setForceQuote(true)
-
该转换器使用任何注册的 Spring 转换器来覆盖对象属性到行列和值的默认 Map。
CustomConversions
-
对象的字段用于与行中的列相互转换。 不使用公共属性。
JavaBean
-
如果你有一个非零参数构造函数,其构造函数参数名称与行的顶级列名匹配,则使用该构造函数。 否则,使用零参数构造函数。 如果有多个非零参数构造函数,则会引发异常。 请参阅 对象创建 以了解更多详细信息。
实体中支持的类型
当前支持以下类型的属性:
-
所有基元类型及其装箱类型(、、 、 等)
int
float
Integer
Float
-
枚举映射到它们的名称。
-
String
-
java.util.Date
和java.time.LocalDate
java.time.LocalDateTime
java.time.LocalTime
-
如果你的数据库支持,上述类型的数组和集合可以映射到数组类型的列。
-
您的数据库驱动程序接受的任何内容。
-
对其他实体的引用。 它们被视为一对一关系或嵌入类型。 一对一关系实体具有属性是可选的。 被引用实体的表应具有一个附加列,其名称基于引用实体,请参阅反向引用。 嵌入的实体不需要 . 如果存在,则将其映射为没有任何特殊含义的普通属性。
id
id
-
Set<some entity>
被视为一对多关系。 被引用实体的表应具有一个附加列,其名称基于引用实体,请参阅反向引用。 -
Map<simple type, some entity>
被视为合格的一对多关系。 被引用实体的表应具有两个附加列:一个基于外键的引用实体命名(请参阅反向引用),另一个具有相同名称和映射键的附加后缀。_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
:默认情况下,所有字段都映射到该行。 此 Comments 将应用它的字段排除在数据库中。 瞬态属性不能在持久性构造函数中使用,因为转换器无法实现构造函数参数的值。 -
@PersistenceCreator
:标记给定的构造函数或静态工厂方法(甚至是受包保护的方法)在从数据库中实例化对象时使用。 构造函数参数按名称映射到检索到的行中的值。 -
@Value
:此注释是 Spring 框架的一部分。 在 Map 框架中,它可以应用于构造函数参数。 这允许你在使用 Spring Expression Language 语句来转换在数据库中检索到的键的值,然后再用于构造域对象。 为了引用给定行的列,必须使用如下表达式: 其中 root 引用给定的根。@Value("#root.myProperty")
Row
-
@Column
:在字段级别应用,以描述列在行中表示的名称,使名称与类的字段名称不同。 在 SQL 语句中使用时,使用 annotation 指定的名称始终被引用。 对于大多数数据库,这意味着这些名称区分大小写。 这也意味着您可以在这些名称中使用特殊字符。 但是,不建议这样做,因为它可能会导致其他工具出现问题。@Column
-
@Version
:在字段级别应用用于乐观锁定,并检查保存操作的修改。 值 is ( for primitive types) 被视为新实体的标记。 最初存储的值是 ( 对于原始类型)。 版本在每次更新时都会自动递增。null
zero
zero
one
有关进一步的参考,请参见 Optimistic Locking。
制图元数据基础设施在与技术无关的单独项目中定义。
JDBC 支持中使用了特定的子类来支持基于 Comments 的元数据。
也可以制定其他策略(如果有需求)。spring-data-commons
引用的实体
对引用实体的处理是有限的。 这是基于上述聚合根的思想。 如果您引用另一个实体,则根据定义,该实体是聚合的一部分。 因此,如果您删除引用,则之前引用的实体将被删除。 这也意味着引用是 1-1 或 1-n,而不是 n-1 或 n-m。
如果您有 n-1 或 n-m 引用,则根据定义,您正在处理两个单独的聚合。
它们之间的引用可以编码为简单值,这些值与 Spring Data JDBC 正确映射。
对这些内容进行编码的更好方法是使它们成为 的实例。
An 是 id 值的包装器,它将该值标记为对其他聚合的引用。
此外,该聚合的类型在 type 参数中编码。id
AggregateReference
AggregateReference
返回引用
聚合中的所有引用在数据库中都会导致相反方向的外键关系。 默认情况下,外键列的名称是引用实体的表名。
或者,您可以选择按引用实体的实体名称命名它们,而忽略注释。
您可以通过调用 .@Table
setForeignKeyNaming(ForeignKeyNaming.IGNORE_RENAMING)
RelationalMappingContext
for 和 references 需要一个附加列来保存 list index 或 map 键。
它基于带有附加后缀的外键列。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);
您不应在实体中包含属性来保存反向引用的实际值,也不应对映射或列表的键列的实际值。
如果您希望这些值在域模型中可用,我们建议在 a 中执行此操作,并将值存储在 transient 值中。AfterConvertCallback
命名策略
按照惯例, Spring Data 应用 a 来确定 table 、 column 和 schema 名称,默认为蛇形大小写。
名为 的对象属性将变为 。
您可以通过在应用程序上下文中提供 NamingStrategy
来调整它。NamingStrategy
firstName
first_name
覆盖表名称
当表命名策略与数据库表名不匹配时,您可以使用 Table
注解覆盖表名。
此注释的元素提供自定义表名称。
以下示例将类映射到数据库中的表:value
MyEntity
CUSTOM_TABLE_NAME
@Table("CUSTOM_TABLE_NAME")
class MyEntity {
@Id
Integer id;
String name;
}
您可以使用 Spring Data 的 SPEL 支持来动态创建表名。 生成后,表名将被缓存,因此它仅根据映射上下文是动态的。
覆盖列名称
当列命名策略与您的数据库表名不匹配时,您可以使用 Column
注解覆盖表名。
此注释的元素提供自定义列名称。
下面的示例将类的属性映射到数据库中的列:value
name
MyEntity
CUSTOM_COLUMN_NAME
class MyEntity {
@Id
Integer id;
@Column("CUSTOM_COLUMN_NAME")
String name;
}
MappedCollection
注释可用于引用类型(一对一关系)或 Sets、Lists 和 Maps(一对多关系)。 元素为引用另一个表中的 id 列的外键列提供自定义名称。
在以下示例中,该类的相应表具有一个列,并且出于关系原因,该 id 的列:idColumn
MySubEntity
NAME
CUSTOM_MY_ENTITY_ID_COLUMN_NAME
MyEntity
class MyEntity {
@Id
Integer id;
@MappedCollection(idColumn = "CUSTOM_MY_ENTITY_ID_COLUMN_NAME")
Set<MySubEntity> subEntities;
}
class MySubEntity {
String name;
}
使用 时,您必须有一个附加列来表示数据集在 中的位置或实体的键值在 .
可以使用 MappedCollection
注释的 Element 自定义此附加列名:List
Map
List
Map
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 支持来动态创建列名。 生成后,名称将被缓存,因此它仅对每个映射上下文是动态的。
嵌入的实体
嵌入实体用于在 Java 数据模型中具有值对象,即使数据库中只有一个表也是如此。
在下面的示例中,您会看到 ThisMap with the Annotation.
这样做的结果是,在数据库中需要一个包含两列 and (来自类) 的表。MyEntity
@Embedded
my_entity
id
name
EmbeddedEntity
但是,如果列实际上在结果集中,则整个属性将根据 of 设置为 null,当所有嵌套属性都为 时,该对象为 。
与此行为相反,尝试使用默认构造函数或接受结果集中可为 null 参数值的构造函数创建新实例。name
null
embeddedEntity
onEmpty
@Embedded
null
null
USE_EMPTY
class MyEntity {
@Id
Integer id;
@Embedded(onEmpty = USE_NULL) (1)
EmbeddedEntity embeddedEntity;
}
class EmbeddedEntity {
String name;
}
1 | Null s 如果在 中。
用于实例化属性的 Potential 值。embeddedEntity name null USE_EMPTY embeddedEntity null name |
如果您在实体中多次需要 value 对象,这可以通过注释的 optional 元素来实现。
此元素表示一个前缀,并预置嵌入对象中的每个列名。prefix
@Embedded
使用快捷方式 & for 和 以减少冗长,同时相应地设置 JSR-305。
|
包含 a 或 a 的嵌入实体将始终被视为非空,因为它们至少包含空 collection 或 map。
因此,在使用 @Embedded (onEmpty = USE_NULL) 时,这样的实体永远不会是 even。Collection
Map
null
只读属性
Spring Data 不会将带有 Comments 的属性写入数据库,但会在加载实体时读取它们。@ReadOnlyProperty
Spring Data 在写入实体后不会自动重新加载实体。 因此,如果要查看数据库中为此类列生成的数据,则必须显式重新加载它。
如果带注释的属性是实体或实体集合,则它由单独表中的一个或多个单独行表示。 Spring Data 不会对这些行执行任何插入、删除或更新。
Insert Only 属性
在插入操作期间,带有 Comments 的属性将仅由 Spring Data 写入数据库。
对于更新,将忽略这些属性。@InsertOnlyProperty
@InsertOnlyProperty
仅支持聚合根。
自定义对象构造
映射子系统允许通过使用注释注释构造函数来自定义对象构造。要用于 constructor 参数的值按以下方式解析:@PersistenceConstructor
-
如果参数使用注释进行注释,则计算给定的表达式,并将结果用作参数值。
@Value
-
如果 Java 类型具有其名称与 input 行的给定字段匹配的属性,则其属性信息用于选择要将 input 字段值传递到的相应构造函数参数。 仅当 Java 文件中存在参数名称信息时,此选项才有效,您可以通过使用调试信息编译源或使用 Java 8 中的命令行开关来实现。
.class
-parameters
javac
-
否则,将引发 a 以指示无法绑定给定的构造函数参数。
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 转换器编写属性
以下示例显示了从对象转换为值的 a 的实现:Converter
Boolean
String
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";
}
}
这里有几件事需要注意:并且都是简单的类型,因此 Spring Data 需要提示此转换器应该应用哪个方向(读取或写入)。
通过注释此转换器,您可以指示 Spring Data 写入数据库中的每个属性。Boolean
String
@WritingConverter
Boolean
String
使用 Spring 转换器读取
以下示例显示了从 a 转换为 value 的 a 的实现:Converter
String
Boolean
@ReadingConverter
public class StringToBooleanConverter implements Converter<String, Boolean> {
@Override
public Boolean convert(String source) {
return source != null && source.equalsIgnoreCase("T") ? Boolean.TRUE : Boolean.FALSE;
}
}
这里有几件事需要注意:并且都是简单的类型,因此 Spring Data 需要提示此转换器应该应用哪个方向(读取或写入)。
通过注释此转换器,指示 Spring Data 转换数据库中应分配给属性的每个值。String
Boolean
@ReadingConverter
String
Boolean
使用JdbcConverter
class MyJdbcConfiguration extends AbstractJdbcConfiguration {
// …
@Override
protected List<?> userConverters() {
return Arrays.asList(new BooleanToStringConverter(), new StringToBooleanConverter());
}
}
在以前版本的 Spring Data JDBC 中,建议 direct overwrite 。
这不再是必要的,甚至不推荐,因为该方法会组合用于所有数据库的转换、已使用数据库注册的转换以及用户注册的转换。
如果您从旧版本的 Spring Data JDBC 迁移,并且覆盖了 API 中的 Converts,则不会注册。AbstractJdbcConfiguration.jdbcCustomConversions() Dialect AbstractJdbcConfiguration.jdbcCustomConversions() Dialect |
如果你想依靠 Spring Boot 来引导 Spring Data JDBC,但仍然想覆盖配置的某些方面,你可能想要公开该类型的 bean。
对于自定义转换,您可以选择注册一个 bean 类型,该 bean 将被 Boot 基础架构选取。
要了解有关此内容的更多信息,请务必阅读 Spring Boot 参考文档。 |