Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Распределенные сервис-ориентированные системы..pdf
Скачиваний:
16
Добавлен:
05.02.2023
Размер:
9.2 Mб
Скачать

3.3 Транзакции управляемые контейнером

Наиболее важные направления современного развития технологии JPA связаны с транзакциями управляемыми контейнерами JTA и объектно-ориентированным языком запросов JPQL.

В предыдущем подразделе технология JPA была продемонстрирована на простом примере чтения всех записей объектов сущности Letter из таблицы t_letter базы lab4db. При этом использовался тип транзакций не-JTA и простая форма запроса языка JPQL.

Несмотря на максимальную простоту использованного примера, использованные методы имеют ряд принципиальных недостатков:

тип транзакций не-JTA, заданный в определении юнита «lab4-unit1» как параметр transaction-type="RESOURCE_LOCAL", требует использование фабрики менеджера сущностей и «ручное» управление транзакциями;

простая форма JPQL-запроса требует явного использование языка SQL и ошибки, которые допустил программист, будут обнаружены только во время выполнения самого запроса.

Учебная цель данного подраздела — демонстрация использования объектных JPQLзапросов Criteria API и менеджера сущностей, использующего тип транзакций контейнера transaction-type="JTA".

Демонстрационный характер изложения учебного материала обоснован большим объемом технологических решений рассматриваемого направления и стремлением завершить полную реализацию JPA-сервлета.

3.3.1 Объектно-ориентированные запросы Criteria API

Язык JPQL (Java Persistence Query Language) обеспечивает пять типов запросов.

Формулировка целевых действий, осуществляемых программистом с использованием технологии JPA, обеспечивается пятью типами JPQL-запросов:

1.Динамические запросы — простейшая форма JPQL-запроса, формулируемая в виде текстовой строки и динамически генерируемая во время его выполнения. Такой тип запроса был использован в тестовом примере предыдущего подраздела (см. пункт 3.2.4, листинг 3.21).

2.Именованные запросы — форма JPQL-запросов, формулируемая в виде текстовых строк во время описания класса сущности с помощью аннота-

162

ций @NamedQueries и @NamedQuery. В отличие от динамических запросов, они являются статическими (неизменяемыми), поскольку генерируются во время компиляции класса сущности.

3.Criteria API — полностью объектно-ориентированная концепция запросов Query API, исключающая применение строковых операторов SQLподобных языков.

4.Родные запросы — запросы, которые вместо JPQL-операторов, формулируются на основе «родных» SQL-операторов SELECT, UPDATE или DELETE. В частности, они могут быть именованными запросами, для чего используется аннотация @NamedNativeQuery.

5.Запросы к хранимым процедурам — новый API для вызова хранимых процедур, введенный в JPA 2.1 и обеспечивающий более высокую производительность JPQL-запросов, благодаря предварительной компиляции хранимой процедуры в базе данных.

Criteria API — типобезопасный вариант JPQL-запросов.

Мы ограничимся кратким рассмотрением только одного типа JPQLзапросов, известного как Criteria API. Поскольку он использует только методы и аргументы языка Java, которые проходят синтаксический анализ во время компиляции, то такой подход называется типобезопасным.

Каждый Criteria-запрос к базе данных формируется и осуществляется за несколько этапов. Рассмотрим эти этапы, используя для наглядности в качестве сущности класс Letter.

Этап 1. Используя объект em менеджера EntityManager создаем объект builder класса CriteriaBuilder:

CriteriaBuilder builder = em.getCriteriaBuilder();

Этап 2. Создаем объект criteria, используя один из многочисленных методов объекта builder. В зависимости от прикладного назначения запроса, обычно используются следующие методы:

CriteriaQuery<Letter> criteria = builder.createQuery(Letter.class);

CriteriaDelete<Letter> criteria = builder.createCriteriaDelete(Letter.class);

CriteriaUpdate<Letter> criteria =

163

builder.createCriteriaUpdate(Letter.class);

Этап 3. Создаем объект root типа Root<T>, который соответствует объектному представлению класса в JPQL и который можно использовать для обозначения атрибутов класса:

Root<Letter> root = criteria.from(Letter.class);

Например, указание на атрибут date сущности Letter будет выглядеть как: root.get("date").

Этап 4. Связываем объект criteria с объектом root:

criteria.select(root);

Этап 5. Добавляем объекту criteria различные ограничения с помощью собственных методов, таких как: where(...), orderBy(...), groupBy(...) и having(...).

Этап 6. Формируем объект запроса query с помощью метода объекта em

типа EntityManager:

TypedQuery<Letter> query = em.createQuery(criteria);

Этап 7. Получаем результат, в зависимости от прикладного назначения сформированного запроса:

List<Letter> list = query.getResultList();

int count = query.executeUpdate();

Этап 8. Обрабатываем полученный результат.

Среды разработки типа Eclipse EE достаточно качественно обеспечиват сопровождение первых семи этапов формирования Criteria-запросов.

Первоначально может показаться, что использование Criteria API — гораздо сложнее динамических, именованных или «родных» запросов. Это — действительно так, когда запросы не содержат сложных условий, но если получить определенный навык, то результат оправдает себя.

164

3.3.2 Реализация EJB-компонента с JTA-типом транзакций

Stateless EJB-контейнер способен инкапсулировать в себя объект менеджера сущностей EntityManager и самостоятельно управлять транзакциями JTA-типа.

В предыдущем подразделе использовалась среда управляемая приложением, что соответствовало единице сохранения «lab4-unit1». Если приложение разрабатывается как сервлет или EJB-контейнер, то возможно использование

среды управляемой контейнером.

Приложение, функционирующее в среде управляемой контейнером, может сразу инкапсулировать менеджер сущностей EntityManager с помошью аннотации @PersistenceContext (name="имя юнита").

В нашей учебной среде за связь с технологией JPA, управляемой транзакциями типа transaction-type="JTA", отвечает юнит «lab4-unit2», описанный в файле persistence.xml. Проведем демонстрацию работы Stateless EJB-контейне- ра, реализовав EJB-компоненту с именем Lets2, которая в свою очередь реализует интерфейсы LocalLetter и RemoteLetter.

Фактически реализация EJB-компоненты с именем Lets2 — это полная реализация EJB-компоненты Lets1, с учетом новых технологий запросов к учебной базе данных: JTA-тип транзакций и Criteria-запросы. Результат такой реализации представлен на листинге 3.24.

Листинг 3.24 — Исходный текст EJB-компоненты Lets2.java проекта lab4

package rsos.lab4; import java.util.List;

import javax.ejb.LocalBean; import javax.ejb.Stateless;

import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.TypedQuery;

import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Root;

@Stateless

@LocalBean

public class Lets2 implements LocalLetter, RemoteLetter

{

@PersistenceContext(name = "lab4-unit2") private EntityManager em;

// Получение списка записей таблицы t_letter public List<Letter> getList()

{

165

// Этап 1. Создаем объект builder CriteriaBuilder builder =

em.getCriteriaBuilder();

// Этап 2. Создаем объект criteria CriteriaQuery<Letter> criteria =

builder.createQuery(Letter.class);

//Этап 3. Создаем объект root Root<Letter> root =

criteria.from(Letter.class);

//Этап 4. Преобразовываем объект criteria,

//включая использование объекта root criteria.select(root);

//Этап 5. Добавляем сортировку

criteria.orderBy(builder.desc(root.get("date")));

//Этап 6. Формируем запрос в виде объекта query TypedQuery<Letter> query = em.createQuery(criteria);

//Этап 7. Получаем результат

List<Letter> list = query.getResultList();

// Этап 8. Обрабатываем результат return list;

}

// Получение объекта по ключу public Letter getLetter(int id) {

Letter l =

em.find(Letter.class, new Integer(id)); return l;

}

//Добавить объект в базу данных public void addLetter(Letter letter) {

em.persist(letter);

}

//Удалить объект из базы данных по ключу public void deleteLetter(int id) {

Letter letter =

em.find(Letter.class, new Integer(id)); if(letter == null)

return; em.remove(letter);

}

// Модифицировать объект в базе данных public void modLetter(Letter letter) {

if(letter == null) return;

Integer Id = letter.getId();

166