3.3.2 注解领域实体

正如您已经在 Spring Data JDBC 中看到的,Sprin gData 在运行时做了一些惊人的事情,自动创建存储库实现。但不幸的是,这对领域对象添加 JPA 注解并没有多大帮助。您需要打开 Ingredient、Taco 和 TacoOrder 类,并添加一些注解。首先是 Ingredient 类:

程序清单 3.18 为 JPA 持久化注解 Ingredient 类

package tacos;

import javax.persistence.Entity;
import javax.persistence.Id;

import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@Entity
@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
  }
}

为了将其声明为 JPA 实体,Ingredient 类必须使用 @Entity 注解。它的 id 属性必须使用 @Id 进行注解,以便将其指定为惟一标识数据库中实体的属性。注意,这是来自 javax.persistence 包的 @Id 注解,而不是 org.springframework.data.annotation 包中的 @Id 注解。

还要注意,我们不再需要 @Table 注解,也不需要实现 Persistable。虽然我们仍然可以在这里使用 @Table 注解,但在使用 JPA 时是不需要这么做的,表名默认设置为类的名称(本例中为“"Ingredient”)。至于 Persistable,它只需要在使用 Spring Data JDBC 时,来确定是否要创建新实体还是更新现有实体。JPA 会自动测别出来。

除了特定于 JPA 的注解之外,还在类级别上添加了 @NoArgsConstructor 注解。JPA 要求实体有一个无参构造函数,所以 Lombok 的 @NoArgsConstructor 实现了这一点。但是要是不想使用它,可以通过将 access 属性设置为 AccessLevel.PRIVATE 来将其设置为私有。因为必须设置 final 属性,所以还要将 force 属性设置为 true,这将导致 Lombok 生成的构造函数,依据属性类型的不同会将它们设置为 null、0 或者 false。

您还将添加一个 @AllArgsConstructor,以便于使用所有属性初始化值创建 Ingredient 对象。

还添加了一个 @RequiredArgsConstructor@Data 隐式地添加了一个必需的有参构造函数,但是当使用 @NoArgsConstructor 时,该构造函数将被删除。显式的 @RequiredArgsConstructor 确保除了私有无参数构造函数外,仍然有一个必需有参构造函数。

现在让我们转到 Taco 类,看看如何将其注解为 JPA 实体。

程序清单 3.19 把 Taco 注解为实体

package tacos;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

import lombok.Data;

@Data
@Entity
public class Taco {

  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  private Long id;

  @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")
  @ManyToMany()
  private List<Ingredient> ingredients = new ArrayList<>();

  public void addIngredient(Ingredient ingredient) {
    this.ingredients.add(ingredient);
  }
}

与 Ingredient 一样,Taco 类现在使用 @Entity 注解,其 id 属性使用 @Id 注解。因为依赖于数据库自动生成 id 值,所以还使用 @GeneratedValue 注解 id 属性,指定自动生成策略。

要声明 Taco 及其相关 Ingredient 列表之间的关系,可以使用 @ManyToMany 注解 ingredient 属性。一个 Taco 可以有很多 Ingredient,一个 Ingredient 可以是很多 Taco 的一部分。

最后,让我们将 Order 对象注解为一个实体。下一个程序清单展示了新的 Order 类。

程序清单 3.20 把 Order 注解为 JPA 实体

package tacos;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.validation.constraints.Digits;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;

import org.hibernate.validator.constraints.CreditCardNumber;

import lombok.Data;

@Data
@Entity
public class TacoOrder implements Serializable {

  private static final long serialVersionUID = 1L;

  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  private Long id;

  private Date placedAt = new Date();

  ...

  @OneToMany(cascade = CascadeType.ALL)
  private List<Taco> tacos = new ArrayList<>();

  public void addTaco(Taco taco) {
    this.tacos.add(taco);
  }
}

如您所见,对 Order 的更改与对 Taco 的更改非常相似。值得注意的是,与 Taco 对象列表的关系用的是 @OneToMany 注解,表示玉米卷都是针对这一订单的。此外,级联参数设置为 CascadeType.ALL,这样,如果订单被删除,其相关的玉米卷也将被删除。

results matching ""

    No results matching ""