Java Platform, Enterprise Edition (Java EE) 8
Учебник по Java EE

Назад Вперёд Содержание

Приложение roster

Приложение roster поддерживает списки команд для игроков в спортивных лигах. Приложение имеет четыре компонента: сущности Java Persistence API (Player, Team и League), сессионный компонент с состоянием (RequestBean), клиентское приложение (RosterClient) и три вспомогательных класса (PlayerDetails, TeamDetails и LeagueDetails).

Функционально roster аналогичен приложению order с тремя новыми функциями: отношение «многие ко многим», наследование сущностей и автоматическое создание таблицы при развёртывании.

Схема базы данных в Apache Derby для приложения roster показана на рис. 41-2.

Рис. 41-2. Схема базы данных для приложения roster

Диаграмма, показывающая схему базы данных для приложения roster

Примечание:

В этой диаграмме для простоты префикс PERSISTENCE_ROSTER_ опущен в именах таблиц.

Отношения в roster

Система оздоровительного спорта имеет следующие отношения.

  • Игрок может быть во многих командах.

  • В команде может быть много игроков.

  • Команда находится в одной лиге.

  • В лиге много команд.

В roster эта система отражается следующими отношениями между сущностями Player, Team и League.

  • Между Player и Team существует отношение «многие ко многим».

  • Между Team и League существует отношение «многие к одному».

Отношения «многие ко многим» в roster

Отношение «многие ко многим» между 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

Приложение 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 { ... }

Запросы Criteria в roster

Приложение roster использует запросы Criteria, в отличие от запросов JPQL, используемых в order. Запросы Criteria записаны на Java, типобезопасны, определены в слое бизнес-логики приложения roster в сессионном компоненте с состоянием RequestBean.

Здесь рассматриваются следующие темы:

Классы метамодели в roster

Классы метамодели моделируют атрибуты объекта и используются запросами 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 в RequestBean

Интерфейс 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-бина.

Создание запросов Criteria в бизнес-методах RequestBean

Многие бизнес-методы в 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>.

Автоматическая генерация таблиц в roster

При развёртывании сервер 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>

Запуск roster

Вы можете использовать IDE NetBeans или Maven для сборки, упаковки, развёртывания и запуска приложения roster.

Здесь рассматриваются следующие темы:

Запуск roster с использованием IDE NetBeans

  1. Удостоверьтесь, чтобы GlassFish Server был запущен (см. Запуск и остановка сервера GlassFish).

  2. Если сервер базы данных ещё не запущен, запустите его, следуя инструкциям в Запуск и остановка Apache Derby.

  3. В меню «Файл» выберите «Открыть проект».

  4. В диалоговом окне «Открыть проект» перейдите к:

    tut-install/examples/persistence
  5. Выберите каталог roster.

  6. Установите флажок Открыть требуемые проекты.

  7. Нажмите Открыть проект.

  8. На вкладке «Проекты» кликните правой кнопкой мыши проект 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

Запуск roster с использованием Maven

  1. Удостоверьтесь, чтобы GlassFish Server был запущен (см. Запуск и остановка сервера GlassFish).

  2. Если сервер базы данных ещё не запущен, запустите его, следуя инструкциям в Запуск и остановка Apache Derby.

  3. В окне терминала перейдите в:

    tut-install/examples/persistence/roster/roster-ear/
  4. Введите следующую команду:

    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

Назад Вперёд Содержание
Логотип Oracle  Copyright © 2017, Oracle и/или её дочерних компаний. Все права защищены. Версия перевода 1.0.5 (Java EE Tutorial — русскоязычная версия)