1. Обзор
Apache DeltaSpike — это проект, предоставляющий набор расширений CDI для проектов Java; для этого требуется, чтобы реализация CDI была доступна во время выполнения.
Конечно, он может работать с другой реализацией CDI — JBoss Weld или OpenWebBeans. Он также протестирован на многих серверах приложений.
В этом уроке мы сосредоточимся на одном из самых известных и полезных — модуле данных .
2. Настройка модуля данных DeltaSpike
Модуль Apache DeltaSpike Data используется для упрощения реализации шаблона репозитория . Это позволяет сократить стандартный код за счет централизованной логики создания и выполнения запросов .
Он очень похож на проект Spring Data . Чтобы запросить базу данных, нам нужно определить объявление метода (без реализации), которое соответствует определенному соглашению об именах или содержит аннотацию @Query
. Реализация будет сделана за нас расширением CDI.
В следующих подразделах мы расскажем, как настроить модуль Apache DeltaSpike Data в нашем приложении.
2.1. Требуемые зависимости
Чтобы использовать модуль данных Apache DeltaSpike в приложении, нам необходимо настроить необходимые зависимости.
Когда Maven является нашим инструментом сборки, мы должны использовать:
<dependency>
<groupId>org.apache.deltaspike.modules</groupId>
<artifactId>deltaspike-data-module-api</artifactId>
<version>1.8.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.deltaspike.modules</groupId>
<artifactId>deltaspike-data-module-impl</artifactId>
<version>1.8.2</version>
<scope>runtime</scope>
</dependency>
Когда мы используем Gradle:
runtime 'org.apache.deltaspike.modules:deltaspike-data-module-impl'
compile 'org.apache.deltaspike.modules:deltaspike-data-module-api'
Артефакты модуля Apache DeltaSpike Data доступны на Maven Central:
Чтобы запустить приложение с модулем данных, нам также нужны реализации JPA и CDI, доступные во время выполнения .
Хотя Apache DeltaSpike можно запустить в приложении Java SE, в большинстве случаев он будет развернут на сервере приложений (например, Wildfly или WebSphere).
Серверы приложений имеют полную поддержку Jakarta EE, поэтому нам больше ничего делать не нужно. В случае приложения Java SE мы должны предоставить эти реализации (например, путем добавления зависимостей к Hibernate и JBoss Weld).
Далее мы также рассмотрим необходимую конфигурацию для EntityManager
.
2.2. Конфигурация диспетчера сущностей
Модуль данных требует внедрения EntityManager
через CDI .
Мы можем добиться этого, используя производителя CDI:
public class EntityManagerProducer {
@PersistenceContext(unitName = "primary")
private EntityManager entityManager;
@ApplicationScoped
@Produces
public EntityManager getEntityManager() {
return entityManager;
}
}
Приведенный выше код предполагает, что у нас есть единица персистентности с первичным
именем, определенным в файле persistence.xml .
Давайте посмотрим ниже в качестве примера определения:
<persistence-unit name="primary" transaction-type="JTA">
<jta-data-source>java:jboss/datasources/foreach-jee7-seedDS</jta-data-source>
<properties>
<property name="hibernate.hbm2ddl.auto" value="create-drop" />
<property name="hibernate.show_sql" value="false" />
</properties>
</persistence-unit>
Блок сохраняемости в нашем примере использует тип транзакции JTA, что означает, что мы должны предоставить стратегию транзакции, которую мы собираемся использовать.
2.3. Стратегия сделки
Если мы используем тип транзакции JTA для нашего источника данных, мы должны определить стратегию транзакции, которая будет использоваться в репозиториях Apache DeltaSpike . Мы можем сделать это внутри файла apache-deltaspike.properties (в каталоге
META-INF
):
globalAlternatives.org.apache.deltaspike.jpa.spi.transaction.TransactionStrategy=org.apache.deltaspike.jpa.impl.transaction.ContainerManagedTransactionStrategy
Мы можем определить четыре типа стратегии транзакций:
BeanManagedUserTransactionStrategy
ResourceLocalTransactionStrategy
ContainerManagedTransactionStrategy
EnvironmentAwareTransactionStrategy
Все они реализуют org.apache.deltaspike.jpa.spi.transaction.TransactionStrategy
.
Это была последняя часть конфигурации, необходимой для нашего модуля данных.
Далее мы покажем, как реализовать классы шаблонов репозитория.
3. Классы репозитория
Когда мы используем модуль данных Apache DeltaSpike, любой абстрактный класс или интерфейс может стать классом репозитория.
Все, что нам нужно сделать, это добавить `` аннотацию @Repository
с атрибутом forEntity
, который определяет сущность JPA, которую должен обрабатывать наш репозиторий:
@Entity
public class User {
// ...
}
@Repository(forEntity = User.class)
public interface SimpleUserRepository {
// ...
}
или с абстрактным классом:
@Repository(forEntity = User.class)
public abstract class SimpleUserRepository {
// ...
}
Модуль данных обнаруживает классы (или интерфейсы) с такой аннотацией и обрабатывает методы, находящиеся внутри.
Есть несколько возможностей определить запрос для выполнения. Мы рассмотрим один за другим в следующих разделах.
4. Запрос из имени метода
Первая возможность определить запрос состоит в том, чтобы использовать имя метода, которое соответствует определенному соглашению об именах .
Это выглядит следующим образом:
(Entity|Optional<Entity>|List<Entity>|Stream<Entity>) (prefix)(Property[Comparator]){Operator Property [Comparator]}
Далее мы сосредоточимся на каждой части этого определения.
4.1. Тип возврата
Тип возвращаемого значения в основном определяет, сколько объектов может вернуть наш запрос . Мы не можем определить один тип объекта в качестве возвращаемого значения, если наш запрос может вернуть более одного результата.
Следующий метод вызовет исключение, если существует более одного пользователя
с заданным именем:
public abstract User findByFirstName(String firstName);
Обратное неверно — мы можем определить возвращаемое значение как коллекцию
, даже если результатом будет только один объект.
public abstract Collection<User> findAnyByFirstName(String firstName);
Префикс имени метода, который предлагает одно значение в качестве возвращаемого типа (например, findAny
), подавляется, если мы определяем возвращаемое значение как Collection
.
Приведенный выше запрос вернет всех пользователей
с совпадающим именем, даже если префикс имени метода предлагает что-то другое.
Таких комбинаций ( тип возвращаемого значения коллекции
и префикс, который предполагает возврат одного единственного значения) следует избегать, поскольку код становится неинтуитивным и трудным для понимания.
В следующем разделе приведены дополнительные сведения о префиксе имени метода.
4.2. Префикс для метода запроса
Префикс определяет действие, которое мы хотим выполнить в репозитории . Наиболее полезным является поиск объектов, соответствующих заданным критериям поиска.
Для этого действия существует множество префиксов, таких как findBy
, findAny
, findAll .
Подробный список см. в официальной документации Apache DeltaSpike :
public abstract User findAnyByLastName(String lastName);
Однако существуют и другие шаблоны методов, которые используются для подсчета и удаления сущностей . Мы можем посчитать
все строки в таблице:
public abstract int count();
Кроме того, существует шаблон метода удаления
, который мы можем добавить в наш репозиторий:
public abstract void remove(User user);
Поддержка префиксов методов countBy
и removeBy
будет добавлена в следующей версии Apache DeltaSpike 1.9.0.
В следующем разделе показано, как мы можем добавить дополнительные атрибуты к запросам.
4.3. Запрос со многими свойствами
В запросе мы можем использовать множество свойств в сочетании с операторами и
.
public abstract Collection<User> findByFirstNameAndLastName(
String firstName, String lastName);
public abstract Collection<User> findByFirstNameOrLastName(
String firstName, String lastName);
Мы можем комбинировать столько свойств, сколько захотим. Также доступен поиск вложенных свойств, которые мы покажем далее.
4.4. Запрос с вложенными свойствами
Запрос также может использовать вложенные свойства .
В следующем примере объект « Пользователь
» имеет свойство адреса типа « Адрес
», а объект « Адрес
» имеет свойство города
:
@Entity
public class Address {
private String city;
// ...
}
@Entity
public class User {
@OneToOne
private Address address;
// ...
}
public abstract Collection<User> findByAddress_city(String city);
4.5. Заказ в запросе
DeltaSpike позволяет нам определить порядок, в котором должен быть возвращен результат . Мы можем определить как восходящий, так и нисходящий порядок:
public abstract List<User> findAllOrderByFirstNameAsc();
Как показано выше, все , что нам нужно сделать, это добавить к имени метода часть, содержащую имя свойства, по которому мы хотим выполнить сортировку, и короткое имя для направления заказа .
Мы можем легко комбинировать множество заказов:
public abstract List<User> findAllOrderByFirstNameAscLastNameDesc();
Далее мы покажем, как ограничить размер результата запроса.
4.6. Ограничение размера результата запроса и нумерации страниц
Есть случаи использования, когда мы хотим получить несколько первых строк из всего результата. Это так называемый лимит запросов. Это также просто с модулем данных:
public abstract Collection<User> findTop2OrderByFirstNameAsc();
public abstract Collection<User> findFirst2OrderByFirstNameAsc();
Первый
и верхний
могут использоваться взаимозаменяемо.
Затем мы можем включить разбивку запроса на страницы, указав два дополнительных параметра: @FirstResult
и @MaxResult
:
public abstract Collection<User> findAllOrderByFirstNameAsc(@FirstResult int start, @MaxResults int size);
Мы уже определили множество методов в репозитории. Некоторые из них являются общими и должны быть определены один раз и использоваться каждым репозиторием.
Apache DeltaSpike предоставляет несколько базовых типов, которые мы можем использовать, чтобы иметь множество готовых методов.
В следующем разделе мы сосредоточимся на том, как это сделать.
5. Основные типы репозиториев
Чтобы получить некоторые базовые методы репозитория, наш репозиторий должен расширять базовый тип, предоставляемый Apache DeltaSpike . Есть некоторые из них, такие как EntityRepository
, FullEntityRepository и
т.д.:
@Repository
public interface UserRepository
extends FullEntityRepository<User, Long> {
// ...
}
Или используя абстрактный класс:
@Repository
public abstract class UserRepository extends AbstractEntityRepository<User, Long> {
// ...
}
Приведенная выше реализация дает нам множество методов без написания дополнительных строк кода, поэтому мы получили то, что хотели — мы значительно сократили шаблонный код.
Если мы используем базовый тип репозитория, нет необходимости передавать дополнительное значение атрибута forEntity
в нашу аннотацию @Repository
.
Когда мы используем абстрактные классы вместо интерфейсов для наших репозиториев, мы получаем дополнительную возможность создать собственный запрос.
Классы абстрактного базового репозитория, например, AbstractEntityRepository
, дают нам доступ к полям (через геттеры) или служебным методам, которые мы можем использовать для создания запроса :
public List<User> findByFirstName(String firstName) {
return typedQuery("select u from User u where u.firstName = ?1")
.setParameter(1, firstName)
.getResultList();
}
В приведенном выше примере мы использовали служебный метод typedQuery
для создания пользовательской реализации.
Последняя возможность создать запрос — использовать аннотацию @Query
, которую мы покажем далее.
6. Аннотация @Query
SQL -запрос для выполнения также можно определить с помощью аннотации @Query
. Это очень похоже на решение Spring. Мы должны добавить аннотацию к методу с SQL-запросом в качестве значения.
По умолчанию это запрос JPQL:
@Query("select u from User u where u.firstName = ?1")
public abstract Collection<User> findUsersWithFirstName(String firstName);
Как и в приведенном выше примере, мы можем легко передать параметры в запрос через индекс.
В случае, если мы хотим передавать запрос через собственный SQL вместо JPQL, нам нужно определить дополнительный атрибут запроса — isNative
со значением true:
@Query(value = "select * from User where firstName = ?1", isNative = true)
public abstract Collection<User> findUsersWithFirstNameNative(String firstName);
7. Заключение
В этой статье мы рассмотрели базовое определение Apache DeltaSpike и сосредоточились на захватывающей части — модуле данных. Это очень похоже на Spring Data Project.
Мы рассмотрели, как реализовать шаблон репозитория. Мы также представили три возможности определения запроса для выполнения.
Как всегда, полные примеры кода, используемые в этой статье, доступны на Github .