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
public void addDesign(Taco design) {
this.tacos.add(design);
}
@PrePersist
void placedAt() {
this.placedAt = new Date();
}
}
Как вы можете видеть, изменения Order похожи на изменения в Taco. Но есть одна новая аннотация на уровне класса: @Table. Это указывает, что сущности Order должны сохраняться в таблице с именем Taco_Order в базе данных.
Хотя вы могли бы использовать эту аннотацию на любом из объектов, это необходимо с Order. Без него JPA по умолчанию сохранит сущности в таблице с именем Order, но order является зарезервированным словом в SQL и вызовет проблемы. Теперь, когда сущности должным образом аннотированы, пришло время написать ваши репозитории.
3.2.3 Объявление репозиториев JPA
В версиях репозиториев JDBC вы явным образом объявили методы, которые должен предоставить репозиторий. Но с Spring Data вместо этого можно расширить интерфейс CrudRepository. Например, вот новый интерфейс IngredientRepository:
package tacos.data;
import org.springframework.data.repository.CrudRepository;
import tacos.Ingredient;
public interface IngredientRepository
extends CrudRepository
}
CrudRepository объявляет около десятка методов для операций CRUD (create, read, update, delete). Обратите внимание, что он параметризован, при этом первым параметром является тип сущности, который должен сохраняться в репозитории, а вторым параметром тип свойства entity ID. Для IngredientRepository параметрами должны быть Ingredient и String.
Аналогичным образом можно определить TacoRepository следующим образом:
package tacos.data;
import org.springframework.data.repository.CrudRepository;
import tacos.Taco;
public interface TacoRepository
extends CrudRepository
}
Единственные существенные различия между IngredientRepositoryв и TacoRepository - это параметры CrudRepository. Здесь они установлены как Taco и Long, чтобы указать сущность Taco (и ее тип идентификатора) в качестве единицы сохранения для этого интерфейса репозитория. Наконец, те же изменения могут быть применены к OrderRepository:
package tacos.data;
import org.springframework.data.repository.CrudRepository;
import tacos.Order;
public interface OrderRepository
extends CrudRepository
}
И теперь у вас есть три репозитория. Вы можете подумать, что вам нужно написать реализации для всех трех, включая дюжину методов для каждой реализации. Но в Spring Data JPA - нет необходимости писать реализацию! При запуске приложения Spring Data JPA автоматически создает реализацию на лету. Это означает, что репозитории готовы к использованию с самого начала. Просто вставьте их в контроллеры, как вы сделали для реализаций на основе JDBC, и все готово.
Методы, предоставляемые CrudRepository обширны и отлично подходят для большинства наиболее распространенных задач. Но что, если у вас есть некоторые требования, выходящие за рамки базовой настойчивости? Давайте посмотрим как настроить репозитории для выполнения запросов, уникальных для вашего домена.
3.2.4 Настройка репозиториев JPA
Представьте, что в дополнение к основным операциям CRUD, предоставляемым CrudRepository, вам также необходимо получить все заказы, доставленные по заданному почтовому индексу. Как оказалось, это можно легко решить, добавив следующее объявление метода в OrderRepository:
List
При создании реализации репозитория Spring Data проверяет все методы в интерфейсе репозитория, анализирует имя метода и пытается понять назначение метода в контексте сохраняемого объекта (в данном случае-порядок). По сути, Spring Data определяет своего рода миниатюрный доменный язык (DSL), в котором сведения о сохраняемости выражаются в сигнатурах методов репозитория.
Spring Data знает, что этот метод предназначен для поиска заказов, потому что вы параметризовали CrudRepository с Order. Имя метода, findByDeliveryZip(), дает понять, что этот метод должен найти все сущности Order, сопоставив их свойство deliveryZip со значением, переданным в качестве параметра в метод.
Метод findByDeliveryZip() достаточно прост, но Spring Data может обрабатывать еще более интересные имена методов. Методы репозитория состоят из глагола, необязательного субъекта, слова By и предиката. В случае findByDeliveryZip() глагол find и предикат DeliveryZip; субъект не указан и подразумевается как Order.
Рассмотрим другой, более сложный пример. Предположим, что вам нужно запросить все заказы, доставленные по заданному почтовому индексу в заданном диапазоне дат. В этом случае может оказаться полезным следующий метод в OrderRepository:
List
На рис. 3.2 показано, как Spring Data анализирует и понимает readOrdersByDeliveryZipAndPlacedAtBetween() при генерации реализации репозитория. Как вы можете видеть, глагол в readOrdersByDeliveryZipAndPlacedAtBetween() read. Spring Data также понимает find, read и get как синонимы для извлечения одной или нескольких сущностей. Кроме того, можно также использовать count в качестве глагола, если требуется, чтобы метод возвращал значение int с числом совпадающих сущностей.
-Этот метод будет считывать (read) данные (”get “и” find " также разрешены здесь).
-Обозначает начало свойств для соответствия
-...and…
-Значение должно находиться между заданными значениями.
-Соотвествие .deliveryZip или -.delivery.zip свойство
-Соотвествие .placedAt или .placed.at свойство
Рис. 3.2. Spring Data анализирует сигнатуры методов репозитория для определения запроса, который необходимо выполнить.
Хотя объект метода является необязательным, здесь он Orders. Spring Data игнорирует большинство слов в объекте, поэтому вы можете назвать метод readPuppiesBy... и он все равно найдет сущности Order, так как это тип, с которым параметризуется CrudRepository.
Сказуемое следует за словом в имени метода и является наиболее интересной частью сигнатуры метода. В этом случае предикат ссылается на два свойства заказа: deliveryZip и placedAt. Свойство deliveryZip должно быть равно значению, переданному в первый параметр метода. Ключевое слово Between указывает, что значение deliveryZip должно находиться между значениями, переданными в последние два параметра метода.
В дополнение к неявной операции Equals и Between, подписи метода Spring Data могут также включать любой из этих операторов:
-IsAfter,After,IsGreaterThan,GreaterThan
-IsGreaterThanEqual,GreaterThanEqual
-IsBefore,Before,IsLessThan,LessThan
-IsLessThanEqual,LessThanEqual
-IsBetween,Between
-IsNull,Null
-IsNotNull,NotNull
-IsIn,In
-IsNotIn,NotIn
-IsStartingWith,StartingWith,StartsWith
-IsEndingWith,EndingWith,EndsWith
-IsContaining,Containing,Contains
-IsLike,Like
-IsNotLike,NotLike
-IsTrue,True
-IsFalse,False
-Is,Equals
-IsNot,Not
-IgnoringCase,IgnoresCase
В качестве альтернативы IgnoringCase и IgnoresCase можно использовать AllIgnoringCase либо AllIgnoresCase в методе, чтобы игнорировать регистр для всех сравнений строк. Например, рассмотрим следующий метод:
List
Наконец, можно также разместить OrderBy в конце имени метода для сортировки результатов по указанному столбцу. Например, deliveryTo в order:
List
Хотя правила именования могут быть полезны для относительно простых запросов, это не займет много воображения, чтобы увидеть, что имена методов могут выйти из-под контроля для более сложных запросы. В этом случае не стесняйтесь называть метод как угодно и аннотировать его @Query, чтобы явно указать запрос, который будет выполняться при вызове метода, как показано в этом примере:
@Query("Order o where o.deliveryCity='Seattle'")
List
В этом простом примере @Query вы запрашиваете все заказы, доставленные в Seattle. Но вы можете использовать @Query для выполнения практически любого запроса, который вы можете придумать, даже если трудно или невозможно выполнить запрос, следуя соглашению об именовании.
ИТОГ
-Spring JdbcTemplate значительно упрощает работу с JDBC.
-PreparedStatementCreator и KeyHolder можно использовать вместе, если необходимо узнать значение идентификатора, созданного базой данных.
-Для простого выполнения вставок данных, используйте SimpleJdbcInsert.
-Spring Data JPA делает сохранение JPA таким же простым, как написание интерфейса репозитория.
Spring in Action Covers Spring 5.0 перевод на русский. Глава 4