Примечание:
В этой диаграмме для простоты префикс PERSISTENCE_ROSTER_
опущен в именах таблиц.
Java Platform, Enterprise Edition (Java EE) 8 Учебник по Java EE |
Назад | Вперёд | Содержание |
Приложение roster
поддерживает списки команд для игроков в спортивных лигах. Приложение имеет четыре компонента: сущности Java Persistence API (Player
, Team
и League
), сессионный компонент с состоянием (RequestBean
), клиентское приложение (RosterClient
) и три вспомогательных класса (PlayerDetails
, TeamDetails
и LeagueDetails
).
Функционально roster
аналогичен приложению order
с тремя новыми функциями: отношение «многие ко многим», наследование сущностей и автоматическое создание таблицы при развёртывании.
Схема базы данных в Apache Derby для приложения roster
показана на рис. 41-2.
Примечание: В этой диаграмме для простоты префикс |
Система оздоровительного спорта имеет следующие отношения.
Игрок может быть во многих командах.
В команде может быть много игроков.
Команда находится в одной лиге.
В лиге много команд.
В roster
эта система отражается следующими отношениями между сущностями Player
, Team
и League
.
Между Player
и Team
существует отношение «многие ко многим».
Между Team
и League
существует отношение «многие к одному».
Отношение «многие ко многим» между Player
и Team
определяется с помощью аннотации @ManyToMany
. В Team.java
метод getPlayers
аннотирован @ManyToMany
:
@ManyToMany
@JoinTable(
name="PERSISTENCE_ROSTER_TEAM_PLAYER",
joinColumns=
@JoinColumn(name="TEAM_ID", referencedColumnName="ID"),
inverseJoinColumns=
@JoinColumn(name="PLAYER_ID", referencedColumnName="ID")
)
public Collection<Player> getPlayers() {
return players;
}
Аннотация @JoinTable
используется для указания таблицы базы данных, которая будет связывать идентификаторы игроков с идентификаторами команд. Сущность, которая определяет @JoinTable
, является владельцем связи, поэтому сущность Team
является владельцем связи с сущностью Player
. Поскольку roster
использует автоматическое создание таблицы во время развёртывания, контейнер создаст таблицу соединения с именем PERSISTENCE_ROSTER_TEAM_PLAYER
.
Player
— это обратная сторона (не владелец) отношения с Team
. Как отношения один-к-одному и многие-к-одному, обратная сторона отмечена элементом mappedBy
в аннотации отношения. Поскольку отношения между Player
и Team
являются двунаправленными, выбор объекта-владельца отношения произвольный.
В Player.java
метод getTeams
аннотирован @ManyToMany
:
@ManyToMany(mappedBy="players")
public Collection<Team> getTeams() {
return teams;
}
Приложение roster
показывает использование наследования сущностей, как описано в Наследование сущностей.
Сущность League
в roster
является абстрактной сущностью с двумя конкретными дочерними классами: SummerLeague
и WinterLeague
. Поскольку League
является абстрактным классом, его объект не может быть создан:
@Entity
@Table(name = "PERSISTENCE_ROSTER_LEAGUE")
public abstract class League implements Serializable { ... }
Вместо этого при создании лиги клиенты используют SummerLeague
или WinterLeague
. SummerLeague
и WinterLeague
наследуют персистентные свойства, определённые в League
, и добавляют только конструктор, который проверяет, что спортивный параметр соответствует типу спорта, разрешённому этой сезонной лигой. Например, вот сущность SummerLeague
:
...
@Entity
public class SummerLeague extends League implements Serializable {
/** Инстанцирование объекта типа SummerLeague */
public SummerLeague() {
}
public SummerLeague(String id, String name, String sport)
throws IncorrectSportException {
this.id = id;
this.name = name;
if (sport.equalsIgnoreCase("swimming") ||
sport.equalsIgnoreCase("soccer") ||
sport.equalsIgnoreCase("basketball") ||
sport.equalsIgnoreCase("baseball")) {
this.sport = sport;
} else {
throw new IncorrectSportException("Sport is not a summer sport.");
}
}
}
Приложение roster
использует стратегию отображения по умолчанию InheritanceType.SINGLE_TABLE
, поэтому аннотация @Inheritance
не требуется. Если вы хотите использовать другую стратегию отображения, укажите для League
аннотацию @Inheritance
и укажите стратегию отображения в элементе strategy
:
@Entity
@Inheritance(strategy=JOINED)
@Table(name="PERSISTENCE_ROSTER_LEAGUE")
public abstract class League implements Serializable { ... }
Приложение roster
использует для столбца дискриминатора имя по умолчанию, поэтому аннотация @DiscriminatorColumn
не требуется. Поскольку используется автоматическое создание таблиц в roster
, Persistence provider создаст столбец дискриминатора с именем DTYPE
в таблице PERSISTENCE_ROSTER_LEAGUE
, в котором будут храниться имя унаследованного лица, используемого для создания лиги. При желании использовать другое имя для столбца дискриминатора, укажите для League
аннотацию @DiscriminatorColumn
и установите элемент name
:
@Entity
@DiscriminatorColumn(name="DISCRIMINATOR")
@Table(name="PERSISTENCE_ROSTER_LEAGUE")
public abstract class League implements Serializable { ... }
Приложение roster
использует запросы Criteria, в отличие от запросов JPQL, используемых в order
. Запросы Criteria записаны на Java, типобезопасны, определены в слое бизнес-логики приложения roster
в сессионном компоненте с состоянием RequestBean
.
Здесь рассматриваются следующие темы:
Классы метамодели моделируют атрибуты объекта и используются запросами Criteria для перехода к атрибутам объекта. Каждый класс сущности в roster
имеет соответствующий класс метамодели, сгенерированный во время компиляции, с тем же именем пакета, что и у сущности, и с добавлением символа подчеркивания (_). Например, сущность roster.entity.Player
имеет соответствующий класс метамодели roster.entity.Player_
.
Каждое персистентное поле или свойство в классе сущности имеет соответствующий атрибут в классе метамодели сущности. Для сущности Player
соответствующий класс метамодели выглядит следующим образом:
@StaticMetamodel(Player.class)
public class Player_ {
public static volatile SingularAttribute<Player, String> id;
public static volatile SingularAttribute<Player, String> name;
public static volatile SingularAttribute<Player, String> position;
public static volatile SingularAttribute<Player, Double> salary;
public static volatile CollectionAttribute<Player, Team> teams;
}
Интерфейс CriteriaBuilder
определяет методы для создания объектов запроса Criteria и создания выражений для изменения этих объектов запроса. RequestBean
создаёт объект CriteriaBuilder
с помощью метода init
, аннотированного @PostConstruct
:
@PersistenceContext
private EntityManager em;
private CriteriaBuilder cb;
@PostConstruct
private void init() {
cb = em.getCriteriaBuilder();
}
Объект EntityManager
инъецируется во время выполнения, а затем этот объект EntityManager
используется для создания объекта CriteriaBuilder
путём вызова getCriteriaBuilder
. Объект CriteriaBuilder
создаётся в методе @PostConstruct
, чтобы гарантировать, что объект EntityManager
был инъецирован контейнером Enterprise-бина.
Многие бизнес-методы в RequestBean
определяют запросы Criteria. Один бизнес-метод, getPlayersByPosition
, возвращает список игроков, которые занимают определённую позицию в команде:
public List<PlayerDetails> getPlayersByPosition(String position) {
logger.info("getPlayersByPosition");
List<Player> players = null;
try {
CriteriaQuery<Player> cq = cb.createQuery(Player.class);
if (cq != null) {
Root<Player> player = cq.from(Player.class);
// установка условия where
cq.where(cb.equal(player.get(Player_.position), position));
cq.select(player);
TypedQuery<Player> q = em.createQuery(cq);
players = q.getResultList();
}
return copyPlayersToDetails(players);
} catch (Exception ex) {
throw new EJBException(ex);
}
}
Объект запроса создаётся путём вызова метода createQuery
объекта CriteriaBuilder
с типом, установленным в Player
, поскольку запрос вернёт список игроков.
Корень запроса, базовая сущность, по которой запрос будет перемещаться для поиска атрибутов сущности и связанных сущностей, создаётся путём вызова метода from
объекта запроса. Это устанавливает предложение FROM
запроса.
Предложение WHERE
, установленное путём вызова метода where
объекта запроса, ограничивает результаты запроса в соответствии с условиями выражения. Метод CriteriaBuilder.equal
сравнивает два выражения. В getPlayersByPosition
, атрибут position
класса метамодели Player_
, доступ к которому вызывается методом get
корня запроса , сравнивается с параметром position
, переданным в getPlayersByPosition
.
Предложение SELECT
запроса устанавливается путём вызова метода select
объекта запроса. Запрос вернёт сущности Player
, поэтому корневой объект запроса передаётся в качестве параметра select
.
Объект запроса подготавливается к выполнению путём вызова EntityManager.createQuery
, который возвращает объект TypedQuery<T>
с типом запроса, в данном случае Player
. Этот типизированный объект запроса используется для выполнения запроса, который происходит при вызове метода getResultList
и возвращении коллекции List<Player>
.
При развёртывании сервер GlassFish автоматически удалит и создаст таблицы базы данных, используемые roster
. Это можно сделать, установив для свойства javax.persistence.schema-generation.database.action
значение drop-and-create
в persistence.xml
:
<persistence version="2.1"
xmlns="http://xmlns.jcp.org/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="em" transaction-type="JTA">
<jta-data-source>java:comp/DefaultDataSource</jta-data-source>
<properties>
<property name="javax.persistence.schema-generation.database.action"
value="drop-and-create"/>
</properties>
</persistence-unit>
</persistence>
Вы можете использовать IDE NetBeans или Maven для сборки, упаковки, развёртывания и запуска приложения roster
.
Здесь рассматриваются следующие темы:
Удостоверьтесь, чтобы GlassFish Server был запущен (см. Запуск и остановка сервера GlassFish).
Если сервер базы данных ещё не запущен, запустите его, следуя инструкциям в Запуск и остановка Apache Derby.
В меню «Файл» выберите «Открыть проект».
В диалоговом окне «Открыть проект» перейдите к:
tut-install/examples/persistence
Выберите каталог roster
.
Установите флажок Открыть требуемые проекты.
Нажмите Открыть проект.
На вкладке «Проекты» кликните правой кнопкой мыши проект roster
и выберите «Сборка».
Это скомпилирует, упакует и развернёт EAR на сервере GlassFish.
На вкладке «Вывод» вы увидите следующий частичный вывод клиентского приложения:
List all players in team T2:
P6 Ian Carlyle goalkeeper 555.0
P7 Rebecca Struthers midfielder 777.0
P8 Anne Anderson forward 65.0
P9 Jan Wesley defender 100.0
P10 Terry Smithson midfielder 100.0
List all teams in league L1:
T1 Honey Bees Visalia
T2 Gophers Manteca
T5 Crows Orland
List all defenders:
P2 Alice Smith defender 505.0
P5 Barney Bold defender 100.0
P9 Jan Wesley defender 100.0
P22 Janice Walker defender 857.0
P25 Frank Fletcher defender 399.0
Удостоверьтесь, чтобы GlassFish Server был запущен (см. Запуск и остановка сервера GlassFish).
Если сервер базы данных ещё не запущен, запустите его, следуя инструкциям в Запуск и остановка Apache Derby.
В окне терминала перейдите в:
tut-install/examples/persistence/roster/roster-ear/
Введите следующую команду:
mvn install
Это компилирует исходные файлы и упаковывает приложение в файл EAR, расположенный в tut-install/examples/persistence/roster/target/roster.ear
. Затем файл EAR развёртывается на сервере GlassFish. GlassFish Server затем удалит и создаст таблицы базы данных во время развёртывания, как указано в persistence.xml
.
После успешного развёртывания EAR клиентские заглушки извлекаются, а клиентское приложение запускается с помощью приложения appclient, входящего в состав GlassFish Server.
Вы увидите вывод, который начинается следующим образом:
[echo] running application client container.
[exec] List all players in team T2:
[exec] P6 Ian Carlyle goalkeeper 555.0
[exec] P7 Rebecca Struthers midfielder 777.0
[exec] P8 Anne Anderson forward 65.0
[exec] P9 Jan Wesley defender 100.0
[exec] P10 Terry Smithson midfielder 100.0
[exec] List all teams in league L1:
[exec] T1 Honey Bees Visalia
[exec] T2 Gophers Manteca
[exec] T5 Crows Orland
[exec] List all defenders:
[exec] P2 Alice Smith defender 505.0
[exec] P5 Barney Bold defender 100.0
[exec] P9 Jan Wesley defender 100.0
[exec] P22 Janice Walker defender 857.0
[exec] P25 Frank Fletcher defender 399.0
Назад | Вперёд | Содержание |