|
对于最新稳定版本,请使用Spring Data Neo4j 8.0.0! |
Spring 数据对象映射基础
本节涵盖了 Spring Data 对象映射、对象创建、字段和属性访问、可变性和不可变性的基础知识。
Spring Data 对象映射的核心职责是创建域对象实例,并将存储原生数据结构映射到这些实例上。 这意味着我们需要两个基本步骤:
-
通过使用暴露的构造体创建实例。
-
实例人口以实现所有暴露的属性。
对象创建
Spring Data 会自动检测持久实体的构造函数,用于实现该类型的对象。 解析算法的工作原理如下:
-
如果存在无参数构造子,则会使用该构造子。 其他车队会被忽略。
-
如果存在单个构造函数接受参数,则会使用该构造函数。
-
如果有多个构造函数接受参数,Spring Data将使用该构造函数必须注释为
@PersistenceCreator.
值解析假设构造函数的参数名称与实体的属性名称相匹配,即解析将以填充该属性的方式执行,包括映射中的所有自定义(不同数据存储列或字段名等)。
这还需要参数名称、类文件中的信息,或者@ConstructorProperties在构造子上有注释。
财产人口
一旦创建了实体实例,Spring Data 会填充该类的所有剩余持久性属性。 除非已被实体的构造子填充(即通过其构造函数参数列表被消耗),否则标识符属性将优先填充,以便解析循环对象引用。 之后,所有尚未被构造函数填充的非瞬态属性都设置在实体实例上。 为此,我们使用以下算法:
-
如果属性是不可变但暴露了凋零方法(见下文),我们利用凋零创建一个带有新属性值的新实体实例。
-
如果定义了属性访问(即通过获取器和设置者访问),我们就调用了设置方法。
-
默认情况下,我们直接设置字段值。
让我们来看看以下这个实体:
class Person {
private final @Id Long id; (1)
private final String firstname, lastname; (2)
private final LocalDate birthday;
private final int age; (3)
private String comment; (4)
private @AccessType(Type.PROPERTY) String remarks; (5)
static Person of(String firstname, String lastname, LocalDate birthday) { (6)
return new Person(null, firstname, lastname, birthday,
Period.between(birthday, LocalDate.now()).getYears());
}
Person(Long id, String firstname, String lastname, LocalDate birthday, int age) { (6)
this.id = id;
this.firstname = firstname;
this.lastname = lastname;
this.birthday = birthday;
this.age = age;
}
Person withId(Long id) { (1)
return new Person(id, this.firstname, this.lastname, this.birthday);
}
void setRemarks(String remarks) { (5)
this.remarks = remarks;
}
}
| 1 | 标识符属性是终结的,但设置为零在构造器中。
该类暴露了withId(...)用于设置标识符的方法,例如当实例插入数据存储并生成标识符时。
原版顶点实例在创建新实例时保持不变。
同样的模式通常也适用于其他存储管理属性,但可能需要为持久化作做修改。 |
| 2 | 这名称和姓氏性质是普通且不可变的性质,可能通过获取器暴露。 |
| 3 | 这年龄性质是不可变但由生日财产。
展示设计后,数据库值将优先于默认,因为 Spring Data 使用的唯一声明构造函数。
即使意图是优先考虑计算,也必须让该构造子年龄作为参数(可能忽略它),否则属性人口步骤将尝试设置年龄字段,但由于其不可变且不存在凋零,会失败。 |
| 4 | 这评论属性可变是通过直接设置其字段来填充的。 |
| 5 | 这言论属性可变,并通过设置评论直接字段,或通过调用 的设定器方法 |
| 6 | 该类暴露了工厂方法和用于对象创建的构造函数。
这里的核心思想是使用工厂方法而非额外的构造函数,以避免通过@PersistenceCreator.
相反,属性的默认设置在工厂方法中处理。 |
一般建议
-
尽量坚持使用不可变对象——不可变对象很容易创建,因为实现对象只需调用其构造函数即可。 此外,这也防止了你的域对象被各种设置器方法堆积,从而允许客户端代码作对象状态。 如果你需要这些,最好让它们有包保护,这样只能被有限数量的共址类型调用。 仅建造者的物质化速度比物业人口快多30%。
-
提供全 args 构造函数——即使你不能或不想将实体建模为不可变值,提供一个构造函数,将实体的所有属性作为参数,包括可变的,仍有价值,因为这样对象映射可以跳过属性人口以实现最佳性能。
-
用工厂方法代替超载构造器来避免
@PersistenceCreator——对于最佳性能需要全参数构造器,我们通常希望暴露更多应用用例特定的构造函数,省略自动生成标识符等功能。 现在的做法是用静态工厂方法来暴露这些全ARG构造器的变体。 -
确保你遵守允许使用生成实例化器和属性访问类的约束
-
要生成标识符,仍需使用最后字段与凋零方法结合
-
使用Lombok避免样板代码——由于持久化作通常需要构造函数接受所有参数,其声明会变成对字段分配的繁琐重复,最好通过使用Lombok的参数
@AllArgsConstructor.
Kotlin 支持
Spring Data 调整了 Kotlin 的具体功能,以支持对象创建和变异。
Kotlin 对象创建
Kotlin 类支持实例化,所有类默认不可变,且需要明确的属性声明来定义可变属性。
请考虑以下内容数据类顶点:
data class Person(val id: String, val name: String)
上述类编译为带有显式构造子的典型类。
我们可以通过添加另一个构造函数并注释该类来定制@PersistenceCreator用来表示构造商偏好:
data class Person(var id: String, val name: String) {
@PersistenceCreator
constructor(id: String) : this(id, "unknown")
}
Kotlin 支持参数可选,允许在未提供参数时使用默认值。
当 Spring Data 检测到参数默认的构造函数时,如果数据存储未提供值(或仅返回),则这些参数将不存在零) 因此 Kotlin 可以应用参数默认。
考虑以下应用参数默认的类名称
data class Person(var id: String, val name: String = "unknown")
每当名称参数要么不是结果的一部分,要么其值是零,然后名称默认为未知.