4.2.2 MongoDB 持久化实体映射
Spring Data MongoDB 提供了一组注解,用于将实体类映射到 MongoDB 的文档结构上。这组 Spring Data MongoDB 注解有好几个,其中有几个是最常用的:
@Id
—— 将属性指定为文档的 Id(在 Spring Data Commons 中)@Document
—— 声明实体类型要持久化为 MongoDB 文档@Field
—— 指定持久化文档中存储这个属性的字段名称(以及可选的顺序)@Transient
—— 指定一下属性不需要持久化
在这几个注释中,只有 @Id
和 @Document
注解是必须有的。除非另行指定,否则没有使用 @Field
或 @Transient
注解的属性,将假定字段名等于属性名。
为 Ingredient 类添加这些注解后,可以得到以下结果:
package tacos;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Document
@AllArgsConstructor
@NoArgsConstructor(access=AccessLevel.PRIVATE, force=true)
public class Ingredient {
@Id
private String id;
private String name;
private Type type;
public static enum Type {
WRAP, PROTEIN, VEGGIES, CHEESE, SAUCE
}
}
如您所见,将 @Document
注解放置在类级别上,以指示 Ingredient 是一个文档实体,可以写入 Mongo 数据库并从中读取。默认情况下,Collection
名称(Mongo 中类似于关系数据库的 Table)基于类名,第一个字母小写。因为您没有进行指定,Ingredient 对象将持久化到名为 ingredient
的 Collection
中。但是您可以通过设置 @Document 的 collection
属性来改变这一点:
@Data
@AllArgsConstructor
@NoArgsConstructor(access=AccessLevel.PRIVATE, force=true)
@Document(collection="ingredients")
public class Ingredient {
...
}
您还注意到 id 属性已经添加了 @id
注解,这指定了此属性作为持久化文档的 ID 。您可以在任何属性上使用 @Id
注解,只要其类型是可序列化的,这包括 String 和 Long。在现在这种情况下,您的 id 属性是 String 类型,所以没有必要将其更改为其他类型。
到目前为止,进展还不错。您可能还记得在本章前面的内容中,为 Cassandra 添加映射时,Ingredient 类是容易处理的实体类型。其他类型,如 Taco,更复杂一些。让我们看看现在如何映射 Taco 类。
MongoDB 的文档持久化方法非常适合领域驱动的设计,在聚合根级别应用持久性的方法。MongoDB 中的文档通常定义为聚合根,聚合的成员作为子文档。
这对 Taco Cloud 意味着,因为 Taco 只作为 TacoOrder 根聚合时进行持久化,所以 Taco 类不需要注释为 @Document,也不需要 添加 @Id 属性。Taco 类可以没有任何持久化注解:
package tacos;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import lombok.Data;
@Data
public class Taco {
@NotNull
@Size(min=5, message="Name must be at least 5 characters long")
private String name;
private Date createdAt = new Date();
@Size(min=1, message="You must choose at least 1 ingredient")
private List<Ingredient> ingredients = new ArrayList<>();
public void addIngredient(Ingredient ingredient) {
this.ingredients.add(ingredient);
}
}
但是,作为聚合根的 TacoOrder 类需要用 @Document
,并有 @Id
的属性。
package tacos;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.validation.constraints.Digits;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
import org.hibernate.validator.constraints.CreditCardNumber;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import lombok.Data;
@Data
@Document
public class TacoOrder implements Serializable {
private static final long serialVersionUID = 1L;
@Id
private String id;
private Date placedAt = new Date();
// other properties omitted for brevity's sake
private List<Taco> tacos = new ArrayList<>();
public void addTaco(Taco taco) {
this.tacos.add(taco);
}
}
为了简洁起见,我简略掉delivery和credit card字段。但是从剩下的属性来看,很明显,您只需要 @Document
和 @Id
注解,就像其他实体类型那样。
然而,请注意,id 属性已更改为 String(与 JPA 版本中的 Long 或 Casandra 版本中的 UUID 类型不同)。正如我前面所说,@Id
可以应用于任何 Serializable 类型。但是,如果您选择使用 String 类型属性作为 ID,您将获得以下好处:Mongo 在保存时自动为其赋值(如果字段为 null)。通过选择 String,将分配一个数据库管理的 ID,并且无需手动设置该属性。
尽管有一些更高级和不寻常的情况,需要额外的映射配置。但在大多数情况下,@Document
和 @Id
以及偶尔使用 @Field
或 @Transient
,对于 MongoDB 的映射就足够了。这几个注解就完全满足了 Taco Cloud 应用的类型映射工作。
剩下的就是编写 Repository 接口了。