原标题:Spring认证中国教育管理中心-Spring Data Couchbase教程二(Spring中国教育管理中心)
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);
}
}
这两个类都定义了一个fieldusing 可分配类型。SubType然而阴影SuperType.field。根据类设计,使用构造函数可能是设置的唯一默认方法SuperType.field。或者,调用super.setField(…)setter 可以设置fieldin SuperType。所有这些机制都会在某种程度上产生冲突,因为属性共享相同的名称但可能代表两个不同的值。如果类型不可分配,Spring Data 会跳过超类型属性。也就是说,被覆盖的属性的类型必须可以分配给它的超类型属性类型才能注册为覆盖,否则超类型属性被认为是瞬态的。我们通常建议使用不同的属性名称。
Spring Data 模块通常支持覆盖不同值的属性。从编程模型的角度来看,有几点需要考虑:
Spring Data 调整了 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 中,默认情况下所有类都是不可变的,并且需要显式的属性声明来定义可变属性。考虑以下data类Person:
data class Person(val id: String, val name: String)
这个类实际上是不可变的。它允许创建新实例,因为 Kotlin 生成一个copy(…)创建新对象实例的方法,该方法从现有对象复制所有属性值并将作为参数提供的属性值应用到该方法。
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;
}
}
getter 和 setterSubType仅在set 上SubType.field,而不在SuperType.field. 在这种安排中,使用构造函数是 set 的唯一默认方法SuperType.field。添加一个方法来SubType设置 SuperType.fieldviathis.SuperType.field = …是可能的,但不属于支持的约定。属性覆盖在某种程度上会产生冲突,因为属性共享相同的名称但可能代表两个不同的值。我们通常建议使用不同的属性名称。
Spring Data 模块通常支持覆盖不同值的属性。从编程模型的角度来看,有几点需要考虑:
所有实体都应使用注释进行@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;
}
}
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、电子邮件地址还是其他任何内容。
选择的存储格式是 JSON。这很棒,但与许多数据表示一样,它允许的数据类型比您直接用 Java 表达的要少。因此,对于所有非原始类型,需要进行某种形式的与支持类型之间的转换。
对于以下实体字段类型,无需添加特殊处理:
由于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 */;
}
}
自定义转换需要注意以下几点:
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。