共计 7484 个字符,预计需要花费 19 分钟才能阅读完成。
原题目:Spring 认证中国教育管理中心 -Spring Data Couchbase 教程二(Spring 中国教育管理中心)
Spring Data Couchbase 教程二
2.1.3 个别倡议
尝试保持应用不可变对象 ——不可变对象很容易创立,因为实现对象只需调用其构造函数即可。此外,这能够防止您的域对象被容许客户端代码操纵对象状态的 setter 办法乱扔垃圾。如果您须要这些,最好将它们包爱护起来,以便它们只能被无限数量的并置类型调用。仅构造函数实现比属性填充快 30%。
提供一个全参数的构造函数 ——即便你不能或不想将你的实体建模为不可变值,提供一个将实体的所有属性作为参数(包含可变属性)的构造函数依然有价值,因为这容许对象映射以跳过属性填充以获得最佳性能。
应用工厂办法而不是重载的构造函数来防止 @PersistenceConstructor - 应用最佳性能所需的全参数构造函数,咱们通常心愿公开更多特定于应用程序用例的构造函数,这些构造函数省略主动生成的标识符等内容。这是一种既定的模式,而不是应用动态工厂办法来公开这些全参数构造函数的变体。
确保恪守容许应用生成的实例化器和属性拜访器类的束缚 ——
对于要生成的标识符,仍将 final 字段与全参数持久性构造函数(首选)或 with…办法联合应用 ——
应用 Lombok 防止样板代码 - 因为持久性操作通常须要一个构造函数来获取所有参数,因而它们的申明变成了对字段调配的样板参数的繁琐反复,应用 Lombok 能够最好地防止这种状况 @AllArgsConstructor。
笼罩属性
Java 容许灵便设计域类,其中子类能够定义一个已在其超类中以雷同名称申明的属性。思考以下示例:
public class SuperType {
private CharSequence field;
public SuperType(CharSequence field) {
this.field = field;
}
public CharSequence getField() {
return this.field;
}
public void setField(CharSequence field) {
this.field = field;
}
}
public class SubType extends SuperType {
private String field;
public SubType(String field) {
super(field);
this.field = field;
}
@Override
public String getField() {
return this.field;
}
public void setField(String field) {
this.field = field;
// optional
super.setField(field);
}
}
Spring Data Couchbase 教程二
这两个类都定义了一个 fieldusing 可调配类型。SubType 然而暗影 SuperType.field。依据类设计,应用构造函数可能是设置的惟一默认办法 SuperType.field。或者,调用 super.setField(…)setter 能够设置 fieldin SuperType。所有这些机制都会在某种程度上产生抵触,因为属性共享雷同的名称但可能代表两个不同的值。如果类型不可调配,Spring Data 会跳过超类型属性。也就是说,被笼罩的属性的类型必须能够调配给它的超类型属性类型能力注册为笼罩,否则超类型属性被认为是瞬态的。咱们通常倡议应用不同的属性名称。
Spring Data 模块通常反对笼罩不同值的属性。从编程模型的角度来看,有几点须要思考:
应该保留哪个属性(默认为所有申明的属性)?您能够通过应用 正文这些属性来排除属性 @Transient。
如何在数据存储中示意属性?对不同的值应用雷同的字段 / 列名称通常会导致数据损坏,因而您应该应用明确的字段 / 列名称正文至多一个属性。
@AccessType(PROPERTY)不能应用 using,因为如果不对 setter 实现进行任何进一步的假如,通常无奈设置超属性。
2.1.4。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(…)创立新对象实例的办法,该办法从现有对象复制所有属性值并将作为参数提供的属性值利用到该办法。
Kotlin 笼罩属性
Kotlin 容许申明属性笼罩以更改子类中的属性。
open class SuperType(open var field: Int)
class SubType(override var field: Int = 1) :
SuperType(field) {
}
这样的安顿出现了两个名为 的属性 field。Kotlin 为每个类中的每个属性生成属性拜访器(getter 和 setter)。实际上,代码如下所示:
public class SuperType {
private int field;
public SuperType(int field) {
this.field = field;
}
public int getField() {
return this.field;
}
public void setField(int field) {
this.field = field;
}
}
public final class SubType extends SuperType {
private int field;
public SubType(int field) {
super(field);
this.field = field;
}
public int getField() {
return this.field;
}
public void setField(int field) {
this.field = field;
}
}
Spring Data Couchbase 教程二
getter 和 setterSubType 仅在 set 上 SubType.field,而不在 SuperType.field. 在这种安顿中,应用构造函数是 set 的惟一默认办法 SuperType.field。增加一个办法来 SubType 设置
SuperType.fieldviathis.SuperType.field = …是可能的,但不属于反对的约定。属性笼罩在某种程度上会产生抵触,因为属性共享雷同的名称但可能代表两个不同的值。咱们通常倡议应用不同的属性名称。
Spring Data 模块通常反对笼罩不同值的属性。从编程模型的角度来看,有几点须要思考:
应该保留哪个属性(默认为所有申明的属性)?您能够通过应用 正文这些属性来排除属性 @Transient。
如何在数据存储中示意属性?对不同的值应用雷同的字段 / 列名称通常会导致数据损坏,因而您应该应用明确的字段 / 列名称正文至多一个属性。
@AccessType(PROPERTY)因为无奈设置超属性,因而无奈应用 using。
2.2. 文档和字段
所有实体都应应用正文进行 @Document 正文,但这不是必须的。
此外,实体中的每个字段都应应用正文进行 @Field 正文。尽管这是 – 严格来说 – 可选的,但它有助于缩小边缘状况并分明地显示实体的用意和设计。它还能够用于以不同的名称存储字段。
还有一个非凡的 @Id 正文须要始终到位。最佳做法是同时命名属性 id。
这是一个非常简单的 User 实体:
示例 6. 带有字段的简略文档
import org.springframework.data.annotation.Id;
import org.springframework.data.couchbase.core.mapping.Field;
import org.springframework.data.couchbase.core.mapping.Document;
@Document
public class User {
@Id
private String id;
@Field
private String firstname;
@Field
private String lastname;
public User(String id, String firstname, String lastname) {
this.id = id;
this.firstname = firstname;
this.lastname = lastname;
}
public String getId() {return id;}
public String getFirstname() {return firstname;}
public String getLastname() {return lastname;}
}
Spring Data Couchbase 教程二
Couchbase Server 反对文档主动过期。该库通过 @Document 正文实现对它的反对。您能够设置一个 expiry 值,该值转换为文档被主动删除之前的秒数。如果你想让它在渐变后 10 秒内过期,请将其设置为 @Document(expiry = 10). 或者,您能够应用 Spring 的属性反对和 expiryExpression 参数配置到期,以容许动静更改到期值。例如:@Document(expiryExpression = “${valid.document.expiry}”)。该属性必须可解析为 int 值,并且不能混合应用这两种办法。
如果您想要文档中的字段名称与实体中应用的字段名称不同的示意模式,您能够在 @Field 正文上设置不同的名称。例如,如果您想放弃文档较小,您能够将 firstname 字段设置为 @Field(“fname”)。在 JSON 文件,你会看到{“fname”: “..”},而不是{“firstname”: “..”}。
在 @Id 正文中须要存在,因为 Couchbase 每个文件须要一个惟一的密钥。该键必须是长度不超过 250 个字符的任意字符串。随便应用适宜您用例的任何内容,无论是 UUID、电子邮件地址还是其余任何内容。
2.3. 数据类型和转换器
抉择的存储格局是 JSON。这很棒,但与许多数据表示一样,它容许的数据类型比您间接用 Java 表白的要少。因而,对于所有非原始类型,须要进行某种模式的与反对类型之间的转换。
对于以下实体字段类型,无需增加非凡解决:
Spring Data Couchbase 教程二
因为 JSON 反对对象(“映射”)和列表,Map 和 List 类型能够天然被转换。如果它们只蕴含最初一段中的原始字段类型,则您也不须要增加非凡解决。这是一个例子:
示例 7. 带有地图和列表的文档
@Document
public class User {
@Id
private String id;
@Field
private List<String> firstnames;
@Field
private Map<String, Integer> childrenAges;
public User(String id, List<String> firstnames, Map<String, Integer> childrenAges) {
this.id = id;
this.firstnames = firstnames;
this.childrenAges = childrenAges;
}
}
用一些示例数据存储用户可能看起来像这样的 JSON 示意:
示例 8. 带有地图和列表的文档 – JSON
{
"_class": "foo.User",
"childrenAges": {
"Alice": 10,
"Bob": 5
},
"firstnames": [
"Foo",
"Bar",
"Baz"
]
}
您不须要始终将所有内容合成为原始类型和列表 / 映射。当然,您也能够用这些原始值组合其余对象。让咱们批改最初一个示例,以便咱们要存储 a Listof Children:
示例 9. 蕴含组合对象的文档
@Document
public class User {
@Id
private String id;
@Field
private List<String> firstnames;
@Field
private List<Child> children;
public User(String id, List<String> firstnames, List<Child> children) {
this.id = id;
this.firstnames = firstnames;
this.children = children;
}
static class Child {
private String name;
private int age;
Child(String name, int age) {
this.name = name;
this.age = age;
}
}
}
填充的对象可能如下所示:
示例 10. 蕴含组合对象的文档 – JSON
{
“_class”: “foo.User”,
“children”: [
{
"age": 4,
"name": "Alice"
},
{
"age": 3,
"name": "Bob"
}
],
“firstnames”: [
"Foo",
"Bar",
"Baz"
]
}
大多数状况下,您还须要存储一个工夫值,例如 a Date。因为它不能间接存储在 JSON 中,因而须要进行转换。该库实现默认的转换器 Date,Calendar 以及 JodaTime 类型(如果在 classpath)。所有这些在文档中默认示意为一个 unix 工夫戳(数字)。您始终能够应用自定义转换器笼罩默认行为,如下所示。这是一个例子:
示例 11. 带有日期和日历的文档
@Document
public class BlogPost {
@Id
private String id;
@Field
private Date created;
@Field
private Calendar updated;
@Field
private String title;
public BlogPost(String id, Date created, Calendar updated, String title) {
this.id = id;
this.created = created;
this.updated = updated;
this.title = title;
}
}
填充的对象可能如下所示:
示例 12. 带有日期和日历的文档 – JSON
{
“title”: “a blog post title”,
“_class”: “foo.BlogPost”,
“updated”: 1394610843,
“created”: 1394610843897
}
可选地,能够通过将零碎属性设置
org.springframework.data.couchbase.useISOStringConverterForDate 为 true 来将日期转换为合乎 ISO-8601 的字符串。如果您想笼罩转换器或实现本人的转换器,这也是可能的。该库实现了个别的 Spring Converter 模式。您能够在配置中的 bean 创立工夫插入自定义转换器。这是您能够配置它的办法(在您的笼罩中 AbstractCouchbaseConfiguration):
示例 13. 自定义转换器
@Override
public CustomConversions customConversions() {
return new CustomConversions(Arrays.asList(FooToBarConverter.INSTANCE, BarToFooConverter.INSTANCE));
}
@WritingConverter
public static enum FooToBarConverter implements Converter<Foo, Bar> {
INSTANCE;
@Override
public Bar convert(Foo source) {return /* do your conversion here */;}
}
@ReadingConverter
public static enum BarToFooConverter implements Converter<Bar, Foo> {
INSTANCE;
@Override
public Foo convert(Bar source) {return /* do your conversion here */;}
}
自定义转换须要留神以下几点:
为了明确起见,请始终在转换器上应用 @WritingConverter 和 @ReadingConverter 正文。特地是如果您正在解决原始类型转换,这将有助于缩小可能的谬误转换。
如果你实现了一个写入转换器,请确保只解码为原始类型、映射和列表。如果您须要更简单的对象类型,请应用 CouchbaseDocument 和 CouchbaseList 类型,底层翻译引擎也能够了解这些类型。您最好的抉择是保持尽可能简略的转换。
始终在通用转换器之前搁置更多非凡转换器,以防止执行谬误转换器的状况。
对于日期,读取转换器应该可能从任何 Number(不仅仅是 Long)读取。这是 N1QL 反对所必须的。