@Stateless
@LocalBean
public class BookEJB implements BookEJBRemote {
··@Inject
··private EntityManager em;
··public List
····TypedQuery
····return query.getResultList();
··}
··public @NotNull Book createBook(@NotNull Book book) {
····em.persist(book);
····return book;
··}
··public @NotNull Book updateBook(@NotNull Book book) {
····return em.merge(book);
··}
··public void deleteBook(@NotNull Book book) {
····em.remove(em.merge(book));
··}
}
Основные различия между классом Main, определенным в главе 4 (см. листинг 4.4), и классом листинга 8.11 является то, что экземпляр EntityManager внедряется непосредственно в сессионный компонент вместо того, чтобы использовать для его создания EntityManagerFactory. Контейнер EJB работает с жизненным циклом EntityManager, поэтому внедряет его экземпляр и затем закрывает его при уничтожении EJB. Кроме того, вызовы JPA более не оборачиваются между вызовами tx.begin() и tx.commit(), так как методы сессионного компонента неявно считаются транзакционными. Это поведение по умолчанию известно как CMT, мы рассмотрим его в следующей главе.
Поскольку BookEJB вызывается удаленно классом Main, BookEJB должен реализовать удаленный интерфейс. Единственное различие между обычным интерфейсом Java и удаленным интерфейсом заключается в наличии аннотации @Remote, что показано в листинге 8.12.
@Remote
public interface BookEJBRemote {
··List
··Book createBook(Book book);
··void deleteBook(Book book);
··Book updateBook(Book book);
}
Написание CDI DatabaseProducer
В листинге 8.11 в BookEJB внедрен объект типа EntityManager с помощью аннотации @Inject. Как вы теперь знаете, JPA позволяет приложению иметь несколько блоков хранения, которые можно различить по именам (в данном случае "chapter08PU"). Поскольку аннотация @Inject не может принять строку в качестве параметра (вы не можете написать @Inject("chapter08PU")), единственный способ внедрить объект типа EntityManager — написать следующий код: @PersistenceContext(unitName = "chapter08PU"). Благодаря CDI-производителям (представленным в главе 2) компонент в листинге 8.13 производит объект типа EntityManager, который может быть внедрен с помощью аннотации @Inject.
public class DatabaseProducer {
··@Produces
··@PersistenceContext(unitName = "chapter08PU")
··private EntityManager em;
}
Блок хранения для BookEJB
В главе 4 модуль хранения (см. листинг 4.5) должен был определить драйвер JDBC, URL для JDBC, пользователя и пароль для подключения к базе данных Derby, поскольку транзакциями управляло приложение (transaction-type = "RESOURCE_LOCAL"). В среде, управляемой контейнером, не приложение, а контейнер управляет EJB и транзакциями. Поэтому transaction-type для блока хранения (листинг 8.14) имеет значение "JTA". Другое отличие заключается в том, что в листинге 4.5 нам приходилось вручную указывать, какой поставщик JPA следует использовать (EclipseLink), а также список всех сущностей, которыми должен управлять поставщик. В среде, управляемой контейнером, мы работаем со стандартным поставщиком JPA (он идет в комплекте с контейнером EJB). Во время развертывания контейнер анализирует архив и находит все сущности, которыми он должен управлять (не нужно явно использовать элемент
·············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"
·············version="2.1">
··
····
····
······
······