# 3.2.2 注解域作为实体

很快就会看到，在创建存储库方面，Spring Data 做了一些惊人的事情。但不幸的是，在使用 JPA 映射注解注解域对象时，它并没有太大的帮助。需要打开 Ingredient、Taco 和 Order 类，并添加一些注解。首先是 Ingredient 类。

{% code title="程序清单 3.16 为 JPA 持久化注解 Ingredient" %}

```java
package tacos;
​
import javax.persistence.Entity;
import javax.persistence.Id;
import lombok.AccessLevel;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
​
@Data
@RequiredArgsConstructor
@NoArgsConstructor(access=AccessLevel.PRIVATE, force=true)
@Entity
public class Ingredient {
    @Id
    private final String id;
    private final String name;
    private final Type type;
    
    public static enum Type {
        WRAP, PROTEIN, VEGGIES, CHEESE, SAUCE
    }
}
```

{% endcode %}

为了将其声明为 JPA 实体，必须使用 @Entity 注解。它的 id 属性必须使用 @Id 进行注解，以便将其指定为惟一标识数据库中实体的属性。

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

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

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

{% code title="程序清单 3.17 把 Taco 注解为实体" %}

```java
package tacos;
​
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.persistence.OneToMany;
import javax.persistence.PrePersist;
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;
    
    @ManyToMany(targetEntity=Ingredient.class)
    @Size(min=1, message="You must choose at least 1 ingredient")
    private List<Ingredient> ingredients;
    
    @PrePersist
    void createdAt() {
        this.createdAt = new Date();
    }
}
```

{% endcode %}

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

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

还有一个新方法 createdAt()，它用 @PrePersist 注解。将使用它将 createdAt 属性设置为保存 Taco 之前的当前日期和时间。最后，让我们将 Order 对象注解为一个实体。下一个程序清单展示了新的 Order 类。

{% code title="程序清单 3.18 把 Order 注解为 JPA 实体" %}

```java
package tacos;
​
import java.io.Serializable;
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.persistence.OneToMany;
import javax.persistence.PrePersist;
import javax.persistence.Table;
import javax.validation.constraints.Digits;
import javax.validation.constraints.Pattern;
import org.hibernate.validator.constraints.CreditCardNumber;
import org.hibernate.validator.constraints.NotBlank;
import lombok.Data;
​
@Data
@Entity
@Table(name="Taco_Order")
public class Order implements Serializable {
    private static final long serialVersionUID = 1L;
    
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Long id;
    
    private Date placedAt;
    
    ...
    
    @ManyToMany(targetEntity=Taco.class)
    private List<Taco> tacos = new ArrayList<>();
    
    public void addDesign(Taco design) {
        this.tacos.add(design);
    }
    
    @PrePersist
    void placedAt() {
        this.placedAt = new Date();
    }
}
```

{% endcode %}

对 Order 的更改与对 Taco 的更改非常相似。但是在类级别有一个新的注解：@Table。这指定订单实体应该持久化到数据库中名为 Taco\_Order 的表中。

尽管可以在任何实体上使用这个注解，但它对于 Order 是必需的。没有它，JPA 将默认将实体持久化到一个名为 Order 的表中，但是 Order 在 SQL 中是一个保留字，会导致问题。现在实体已经得到了正确的注解，该编写 repository 了。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://potoyang.gitbook.io/spring-in-action-v5/di-3-zhang-chu-li-shu-ju/3.2-shi-yong-spring-data-jpa-chi-jiu-hua-shu-ju/3.2.2-zhu-jie-yu-zuo-wei-shi-ti.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
