Выбрать главу

3.2.1 Добавление Spring Data JPA в проект

Spring  Data JPA доступны для Spring  Boot приложений с JPA starter-ом. Эта зависимость стартера не только приносит в Spring Data JPA, но и транзитивно включает Hibernate как реализацию JPA:

<dependency>

 <groupId>org.springframework.boot</groupId>

 <artifactId>spring-boot-starter-data-jpa</artifactId>

</dependency>

Если вы хотите использовать другую реализацию JPA, то вам нужно, по крайней мере, исключить зависимость Hibernate и включить библиотеку JPA по вашему выбору. Например, чтобы использовать EclipseLink вместо Hibernate, необходимо изменить сборку следующим образом:

<dependency>

 <groupId>org.springframework.boot</groupId>

 <artifactId>spring-boot-starter-data-jpa</artifactId>

 <exclusions>

   <exclusion>

     <artifactId>hibernate-entitymanager</artifactId>

     <groupId>org.hibernate</groupId>

   </exclusion>

 </exclusions>

</dependency>

<dependency>

 <groupId>org.eclipse.persistence</groupId>

 <artifactId>eclipselink</artifactId>

 <version>2.5.2</version>

</dependency>

Обратите внимание, что могут потребоваться другие изменения в зависимости от выбранного варианта реализации JPA. Обратитесь к документации к выбранной реализации JPA для деталей. Теперь давайте вернемся к вашим доменным объектам и аннотируем их для сохранения JPA.

3.2.2 Аннотирование домена как сущностей

Как вы скоро увидите, Spring Data делает удивительные вещи, когда дело доходит до создания репозиториев. Но, к сожалению, это не очень помогает, когда дело доходит до аннотирования объектов домена аннотациями сопоставления JPA. Вам нужно будет открыть Ingredient, Taco и Order классы и добавить в них несколько аннотаций. Первым изменим класс Ingredient.

Листинг 3.16 аннотирование Ingredient для сохранения JPA

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

 }

}

Чтобы объявить это как сущность JPA, Ingredient должен быть аннотирован @Entity. И его свойство id должно быть аннотировано @Id, чтобы обозначить его как свойство, которое будет однозначно идентифицировать сущность в базе данных.

В дополнение к аннотациям, специфичным для JPA, вы также заметили, что вы добавили аннотацию @NoArgsConstructor на уровне класса. JPA требует, чтобы сущности имели конструктор без аргументов, поэтому @NoArgsConstructor Lombok-а делает это за вас. Однако, вы не хотите, чтобы имелась возможность использовать его, поэтому сделайте его private, установив атрибут доступа AccessLevel.PRIVATE. И поскольку есть final свойства, которые должны быть установлены, вы также устанавливаете атрибут force в true, что приводит к тому, что конструктор, сгенерированный Lombok-ом, устанавливает их в null.

Также добавляется @RequiredArgsConstructor. @Data неявно добавляет конструктор обязательных аргументов, но при использовании @NoArgsConstructor этот конструктор удаляется. Явный @RequiredArgsConstructor гарантирует, что у вас все еще будет конструктор обязательных аргументов в дополнение к закрытому конструктору без аргументов.

Теперь давайте перейдем к классу Taco и посмотрим, как аннотировать его как сущность JPA.

Листинг 3.17 аннотирование Taco как сущности

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();

 }

}

Как и в случае с Ingredient, класс Taco теперь аннотируется @Entity и имеет свойство id, аннотированное @Id. Поскольку вы полагаетесь на базу данных для автоматического создания значения идентификатора, вы также аннотируете свойство id с помощью @GeneratedValue, указывая стратегию AUTO.