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 对象将持久化到名为 ingredientCollection 中。但是您可以通过设置 @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 接口了。

results matching ""

    No results matching ""