Skip to content

3.3.2 Đánh dấu domain là entity

Như bạn đã thấy với Spring Data JDBC, Spring Data có thể làm được nhiều điều tuyệt vời khi tạo các repository. Nhưng đáng tiếc là nó không hỗ trợ nhiều khi bạn cần đánh dấu các đối tượng domain của mình với các annotation ánh xạ của JPA. Bạn sẽ cần mở các lớp Ingredient, Taco, và TacoOrder và thêm vào một vài annotation. Trước tiên là lớp Ingredient, như hiển thị dưới đây.

Liệt kê 3.18 Đánh dấu Ingredient để lưu trữ với JPA

java
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
  }
}

Để khai báo lớp này là một entity JPA, Ingredient phải được đánh dấu bằng @Entity. Và thuộc tính id của nó phải được đánh dấu bằng @Id để xác định rằng nó là thuộc tính sẽ được sử dụng để định danh duy nhất entity trong cơ sở dữ liệu. Lưu ý rằng annotation @Id này là phiên bản của JPA từ package javax.persistence, không phải @Id của Spring Data trong package org.springframework.data.annotation.

Cũng lưu ý rằng chúng ta không còn cần annotation @Table hoặc cần implement Persistable. Mặc dù bạn vẫn có thể sử dụng @Table tại đây, nhưng điều đó là không cần thiết khi làm việc với JPA và nó mặc định ánh xạ tên bảng theo tên lớp ("Ingredient" trong trường hợp này). Về Persistable, nó chỉ cần thiết khi sử dụng với Spring Data JDBC để xác định xem một entity có phải là mới hay không, hoặc cần cập nhật entity đã tồn tại; JPA xử lý điều này tự động.

Ngoài các annotation riêng cho JPA, bạn cũng sẽ thấy rằng chúng ta đã thêm annotation @NoArgsConstructor ở cấp lớp. JPA yêu cầu các entity phải có constructor không tham số, vì vậy @NoArgsConstructor của Lombok sẽ thực hiện điều đó. Bạn không muốn sử dụng nó ở nơi khác, vì vậy bạn đặt access level thành AccessLevel.PRIVATE. Và bởi vì bạn cần gán giá trị cho các thuộc tính final, bạn cũng cần đặt thuộc tính force thành true, điều này giúp constructor được tạo bởi Lombok sẽ gán các giá trị mặc định là null, 0, hoặc false tùy thuộc vào kiểu dữ liệu.

Bạn cũng sẽ thêm annotation @AllArgsConstructor để dễ dàng tạo một đối tượng Ingredient với tất cả các thuộc tính đã được khởi tạo.

Bạn cũng cần một @RequiredArgsConstructor. Annotation @Data ngầm định thêm constructor với các đối số bắt buộc, nhưng khi sử dụng @NoArgsConstructor, constructor đó sẽ bị loại bỏ. Việc khai báo rõ ràng @RequiredArgsConstructor sẽ đảm bảo rằng bạn vẫn có constructor với các đối số cần thiết, bên cạnh constructor không đối số (private).

Bây giờ chúng ta hãy tiếp tục với lớp Taco và xem cách đánh dấu nó như một entity JPA.

Liệt kê 3.19 Đánh dấu Taco là một entity

java
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);
  }
}

Tương tự như Ingredient, lớp Taco giờ đây được đánh dấu bằng @Entity và thuộc tính id của nó được đánh dấu với @Id. Vì bạn dựa vào cơ sở dữ liệu để tự động tạo giá trị ID, bạn cũng cần đánh dấu thuộc tính id với annotation @GeneratedValue, và chỉ định strategyAUTO.

Để khai báo mối quan hệ giữa một Taco và danh sách các Ingredient liên kết, bạn đánh dấu trường ingredients bằng annotation @ManyToMany. Một Taco có thể có nhiều Ingredient, và một Ingredient có thể thuộc về nhiều Taco.

Cuối cùng, chúng ta hãy đánh dấu đối tượng TacoOrder là một entity. Đoạn mã tiếp theo sẽ hiển thị lớp TacoOrder mới.

Liệt kê 3.20 Đánh dấu TacoOrder là một entity JPA

java
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);
  }
}

Như bạn có thể thấy, những thay đổi đối với TacoOrder gần như phản chiếu các thay đổi đối với Taco. Một điểm đáng chú ý là mối quan hệ với danh sách các đối tượng Taco được chú thích bằng @OneToMany, cho biết rằng tất cả các món taco này đều thuộc về một đơn hàng cụ thể. Hơn nữa, thuộc tính cascade được đặt là CascadeType.ALL, điều này có nghĩa là nếu đơn hàng bị xóa, các món taco liên quan cũng sẽ bị xóa theo.

Released under the MIT License.