打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
Spring Data JDBC参考文档二

原标题:Spring认证|Spring Data JDBC参考文档二 (内容来源:Spring中国教育管理中心)

class Person {

private final @Id Long id;

private final String firstname, lastname;

private final LocalDate birthday;

private final int age;

private String comment;

private @AccessType(Type.PROPERTY) String remarks;

static Person of(String firstname, String lastname, LocalDate birthday) {

return new Person(null, firstname, lastname, birthday,

Period.between(birthday, LocalDate.now()).getYears());

}

Person(Long id, String firstname, String lastname, LocalDate birthday, int age) {

this.id = id;

this.firstname = firstname;

this.lastname = lastname;

this.birthday = birthday;

this.age = age;

}

Person withId(Long id) {

return new Person(id, this.firstname, this.lastname, this.birthday, this.age);

}

void setRemarks(String remarks) {

this.remarks = remarks;

}

}

identifier 属性是最终的,但null在构造函数中设置为。该类公开了一个withId(…)用于设置标识符的方法,例如,当一个实例插入到数据存储中并生成一个标识符时。Person创建新实例时,原始实例保持不变。相同的模式通常应用于由存储管理但可能必须为持久性操作更改的其他属性。wither 方法是可选的,因为持久性构造函数(参见 6)实际上是一个复制构造函数,并且设置该属性将被转换为创建一个应用新标识符值的新实例。

的firstname和lastname特性是通过吸气剂可能暴露普通不可变属性。

该age属性是不可变的,但源自该birthday属性。使用所示设计,数据库值将胜过默认值,因为 Spring Data 使用唯一声明的构造函数。即使意图是计算应该是首选,重要的是此构造函数也将age作为参数(可能会忽略它),否则属性填充步骤将尝试设置年龄字段并由于它不可变且没有with…方法而失败在场。

该comment属性是可变的,通过直接设置其字段来填充。

的remarks特性是可变的,并且通过设置填充comment直接字段或通过调用用于setter方法

该类公开了一个工厂方法和一个用于创建对象的构造函数。这里的核心思想是使用工厂方法而不是额外的构造函数,以避免需要通过@PersistenceConstructor. 相反,属性的默认设置是在工厂方法中处理的。

一般建议

尽量坚持不可变对象 ——不可变对象很容易创建,因为具体化一个对象只是调用它的构造函数的问题。此外,这避免了您的域对象被允许客户端代码操作对象状态的 setter 方法所困扰。如果您需要这些,最好将它们打包保护,以便它们只能由有限数量的并置类型调用。仅构造函数实现比属性填充快 30%。

提供一个全参数构造函数 ——即使你不能或不想将你的实体建模为不可变值,提供一个将实体的所有属性(包括可变属性)作为参数的构造函数仍然是有价值的,因为这允许对象映射以跳过属性填充以获得最佳性能。

使用工厂方法而不是重载构造函数来避免@PersistenceConstructor ——为了获得最佳性能需要一个全参数构造函数,我们通常希望公开更多应用程序用例特定的构造函数,这些构造函数省略自动生成的标识符等。这是一种既定的模式,而不是使用静态工厂方法来公开 all-args 构造函数的这些变体。

确保遵守允许使用生成的实例化器和属性访问器类的约束 —— 

对于要生成的标识符,仍然使用 final 字段与全参数持久性构造函数(首选)或with…方法相结合 —— 

使用 Lombok 避免样板代码 ——由于持久性操作通常需要一个接受所有参数的构造函数,因此它们的声明变成了对字段分配的样板参数的乏味重复,而使用 Lombok 的@AllArgsConstructor.

Kotlin 支持

Spring Data 调整了 Kotlin 的细节以允许对象创建和变异。

Kotlin 对象创建

Kotlin 类支持实例化,默认情况下所有类都是不可变的,并且需要显式属性声明来定义可变属性。考虑以下data类Person:

data class Person(val id: String, val name: String)

上面的类编译为具有显式构造函数的典型类。我们可以通过添加另一个构造函数来自定义这个类,并使用注释@PersistenceConstructor来指示构造函数首选项:

data class Person(var id: String, val name: String) {

@PersistenceConstructor

constructor(id: String) : this(id, "unknown")

}

Kotlin 通过允许在未提供参数的情况下使用默认值来支持参数可选性。当 Spring Data 检测到具有参数默认值的构造函数时,如果数据存储不提供值(或简单地返回null),它就会使这些参数不存在,因此 Kotlin 可以应用参数默认值。考虑以下应用参数默认值的类name

data class Person(var id: String, val name: String = "unknown")

每次name参数不是结果的一部分或其值为 时null,则name默认为unknown。

Kotlin 数据类的属性填充

在 Kotlin 中,默认情况下所有类都是不可变的,并且需要明确的属性声明来定义可变属性。考虑以下data类Person:

data class Person(val id: String, val name: String)

这个类实际上是不可变的。它允许创建新实例,因为 Kotlin 生成copy(…)创建新对象实例的方法,该方法从现有对象复制所有属性值并将作为参数提供的属性值应用到该方法。

9.6.2. 您的实体中支持的类型

目前支持以下类型的属性:

所有原始类型及其装箱类型(int、float、Integer、Float等)

枚举被映射到他们的名字。

String

java.util.Date, java.time.LocalDate, java.time.LocalDateTime, 和java.time.LocalTime

如果您的数据库支持,上述类型的数组和集合可以映射到数组类型的列。

您的数据库驱动程序接受的任何内容。

对其他实体的引用。它们被认为是一对一的关系,或嵌入类型。一对一关系实体是否具有id属性是可选的。被引用实体的表应该有一个与引用实体表同名的附加列。您可以通过实施来更改此名称
NamingStrategy.getReverseColumnName(PersistentPropertyPathExtension path)。嵌入式实体不需要id. 如果存在,它将被忽略。

Set被认为是一对多的关系。被引用实体的表应该有一个与引用实体表同名的附加列。您可以通过实施来更改此名称
NamingStrategy.getReverseColumnName(PersistentPropertyPathExtension path)。

Map被认为是合格的一对多关系。被引用实体的表应该有两个额外的列:一个与外键的引用实体的表命名相同,另一个具有相同的名称和_key映射键的附加后缀。您可以通过分别实现
NamingStrategy.getReverseColumnName(PersistentPropertyPathExtension path)和来更改此行为NamingStrategy.getKeyColumn(RelationalPersistentProperty property)。或者,您可以使用@MappedCollection(idColumn="your_column_name", keyColumn="your_key_column_name")

List映射为Map.

引用实体的处理是有限的。这是基于上述聚合根的思想。如果您引用另一个实体,则根据定义,该实体是您的聚合的一部分。因此,如果您删除引用,则先前引用的实体将被删除。这也意味着参考是 1-1 或 1-n,但不是 n-1 或 nm。

如果您有 n-1 或 nm 引用,根据定义,您正在处理两个单独的聚合。这些之间的引用应该被编码为简单的id值,应该与 Spring Data JDBC 正确映射。

9.6.3. 自定义转换器

对于默认情况下不支持的类型,可以通过继承您的配置AbstractJdbcConfiguration并覆盖方法来注册自定义转换器jdbcCustomConversions()。

@Configuration

public class DataJdbcConfiguration extends AbstractJdbcConfiguration {

@Override

public JdbcCustomConversions jdbcCustomConversions() {

return new JdbcCustomConversions(Collections.singletonList(TimestampTzToDateConverter.INSTANCE));

}

@ReadingConverter

enum TimestampTzToDateConverter implements Converter {

INSTANCE;

@Override

public Date convert(TIMESTAMPTZ source) {

//...

}

}

}

的构造函数JdbcCustomConversions接受 的列表
org.springframework.core.convert.converter.Converter。

转换器应该用@ReadingConverter或进行注释,@WritingConverter以控制它们仅从数据库读取或写入数据库的适用性。

TIMESTAMPTZ 示例中的数据库特定数据类型需要转换为更适合域模型的数据类型。

数据库值

值转换用于JdbcValue丰富传播到具有java.sql.Types类型的JDBC 操作的值。如果您需要指定特定于 JDBC 的类型而不是使用类型派生,请注册自定义写入转换器。此转换器应将值转换JdbcValue为具有值和实际JDBCType.

9.6.4. NamingStrategy

当您使用CrudRepositorySpring Data JDBC 提供的标准实现时,它们需要特定的表结构。您可以通过NamingStrategy在应用程序上下文中提供 a 来调整它。

9.6.5. Custom table names

当 NamingStrategy 与您的数据库表名称不匹配时,您可以使用@Table注释自定义名称。value此注释的元素提供自定义表名称。以下示例将MyEntity类映射到CUSTOM_TABLE_NAME数据库中的表:

@Table("CUSTOM_TABLE_NAME")

public class MyEntity {

@Id

Integer id;

String name;

}

9.6.6. Custom column names

当 NamingStrategy 与您的数据库列名称不匹配时,您可以使用@Column注释自定义名称。value此注释的元素提供自定义列名称。以下示例将类的name属性映射MyEntity到CUSTOM_COLUMN_NAME数据库中的列:

public class MyEntity {

@Id

Integer id;

@Column("CUSTOM_COLUMN_NAME")

String name;

}

该@MappedCollection 注释可以对引用类型(一对一的关系)或集合,列表被使用,并且图(一个一对多关系)。 idColumn注释的元素为引用另一个表中的 id 列的外键列提供自定义名称。在下面的例子中,MySubEntity类的对应表有一个NAME列,以及出于关系原因
CUSTOM_MY_ENTITY_ID_COLUMN_NAME的MyEntityid列:

public class MyEntity {

@Id

Integer id;

@MappedCollection(idColumn = "CUSTOM_MY_ENTITY_ID_COLUMN_NAME")

Set subEntities;

}

public class MySubEntity {

String name;

}

当使用List和Map您必须在数据集中的位置的附加列List或实体的键值Map。可以使用注释的keyColumn元素自定义此附加列名称@MappedCollection:

public class MyEntity {

@Id

Integer id;

@MappedCollection(idColumn = "CUSTOM_COLUMN_NAME", keyColumn = "CUSTOM_KEY_COLUMN_NAME")

List name;

}

public class MySubEntity {

String name;

}

9.6.7. 嵌入实体

嵌入式实体用于在 Java 数据模型中包含值对象,即使数据库中只有一张表。在下面的示例中,您会看到它MyEntity与@Embedded注释映射。这样做的结果是,在数据库中应该有一个my_entity包含两列id和name(来自EmbeddedEntity类)的表。

但是,如果该name列实际上null在结果集中,则整个属性embeddedEntity将根据onEmptyof设置为 null @Embedded,null当所有嵌套属性都为 时,该s 对象null。

与此行为相反,USE_EMPTY尝试使用默认构造函数或从结果集中接受可为空参数值的构造函数创建新实例。

Example 57. 嵌入对象的示例代码

public class MyEntity {

@Id

Integer id;

@Embedded(onEmpty = USE_NULL)

EmbeddedEntity embeddedEntity;

}

public class EmbeddedEntity {

String name;

}

NullŠembeddedEntity如果name在null。用于使用属性的潜在值进行USE_EMPTY实例化。embeddedEntitynullname

如果您在一个实体中多次需要一个值对象,这可以通过注释的可选prefix元素来实现@Embedded。此元素表示一个前缀,并为嵌入对象中的每个列名称添加前缀。

利用快捷方式@Embedded.Nullable& @Embedded.Emptyfor@Embedded(onEmpty = USE_NULL)和@Embedded(onEmpty = USE_EMPTY)来减少冗长,同时相应地设置 JSR-305 @javax.annotation.Nonnull。

public class MyEntity {

@Id

Integer id;

@Embedded.Nullable

EmbeddedEntity embeddedEntity;

}

的快捷方式@Embedded(onEmpty = USE_NULL)。

包含 aCollection或 a 的嵌入实体Map将始终被视为非空,因为它们至少将包含空集合或映射。因此,null即使在使用 @Embedded(onEmpty = USE_NULL) 时,这样的实体也永远不会出现。

9.6.8. 实体状态检测策略

下表描述了 Spring Data 提供的用于检测实体是否为新实体的策略:

内容提示:本文(Spring Data JDBC参考文档)未完待续......

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
通用的高度可扩展的Excel导入实现(附Demo)
hibernate学习系列SET集合
AS3自写类整理笔记:ClassLoader类
What the f**k JavaScript 中文版
最新和改进的 C# 6.0
[.net学习笔记]C#新特性
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服