Здравствуйте, подскажите пожалуйста где можно достать материалы по курсу Кросс-платформенные и многозвенные технологии, о которых говориться, к примеру, в Лекции 2. Пример "Служба мгновенных сообщений" |
Технология Enterprise Java Beans. Часть 2
Объектные компоненты
Как уже говорилось ранее, объектные компоненты моделируют прикладные понятия, которые могут быть описаны существительными. Это не закон установленный раз и навсегда, скорее эмпирическое правило, которое позволяет определять, когда прикладное понятие следует реализовать как объектный компонент, а когда - как сеансовый или вообще управляемый состоянием компонент. Объектные компоненты описывают и состояние, и поведение объектов из жизни. Разработчики могут добавлять к объектным компонентам данные и правила, характеризующие компоненты.
Объектные компоненты представляют записи в базе данных. При изменении объектных компонентов происходят изменения в базе данных. В предыдущих примерах уже рассматривалась возможность внесения изменения в базу данных непосредственно через сеансовые компоненты, но это было не очень удобно - приходилось, к примеру, для создания заказа в базе данных выполнять несколько запросов SQL.При использовании объектных компонентов можно было бы просто использовать метод Order.createOrder (List commoditiesList). Компонент Order представляет приложению полный доступ к информации о заказах, а также возможность управлять ими -создавать, модифицировать и удалять. При помощи объектных компонентов разработку можно упростить и сделать более рентабельной.
Когда объектный компонент создается, в базу данных должна быть вставлена новая запись и создан экземпляр компонента, связанный с этими данными. По мере того, как компонент изменяется, его состояние меняется, и изменения должны быть синхронизированы с данными в базе данных. За процесс координации данных, представляющий экземпляр компонента, с базой данных, отвечает служба Persistence.
Существует два основных типа объектных компонентов, различающихся реализацией механизма постоянства: компоненты с постоянством, управляемым контейнером (container-managed persistence или CMP),и компоненты поддерживающие постоянство самостоятельно (bean-managedpersistence или BMP).
В случае с CMP,контейнер знает, как постоянство экземпляра и поля отношений с другими компонентами отображаются в базе данных, и автоматически вставляет, модифицирует и удаляет данные. В случае BMP,вся работа выполняется разработчиком самостоятельно. Ему необходимо написать код манипуляции базой данных. Контейнер EJB сообщает экземпляру компонента, когда безопасно вставлять, модифицировать и удалять его данные из базы данных. Экземпляр выполняет всю работу по обеспечению своего постоянства самостоятельно.
Начиная со спецификации EJB 2.0, CMP подверглось очень сильным изменениям. В результате CMP теперь больше не является обратно совместимым с предыдущими спецификациями. Поэтому довольно часто используют сокращение CMP 2.0.
В CMP 2.0 контейнер объектных компонентов автоматически управляет их состоянием. Контейнер заботится о регистрации компонентов в транзакциях и поддерживает их состояние в базе данных. Разработчик компонентов описывает атрибуты и связи компонентов при помощи виртуальных (virtual) полей постоянства и отношений. Разработчик не определяет их явно. Вместо этого в классе создаются абстрактные методы доступа ( set и get ).
Несмотря на то, что BMP требует от разработчиков большего количества усилий, чем CMP,использование его в некоторых случаях является оправданным. CMP не предоставляет возможностей работы с разными источниками данных, поэтому если компонент должен работать сразу с несколькими базами данных или же получать информацию из других источников, необходимо использование BMP.
При создании объектного компонента (BMP или CMP),контейнер EJB выполняет следующую последовательность действий:
- Контейнер вызывает метод Class.newInstance() у класса объектного компонента. До этого момента объектный компонент существовал только как набор файлов (класса компонента, интерфейсов и дескриптора развертывания). У компонента вызывается метод setEntityContext (EntityContext). После этого созданный компонент помещается в пул объектных компонентов. Обычно контейнер хранит несколько экземпляров компонента в пуле. Они помещаются туда при запуске контейнера. В дальнейшем, в зависимости от оьстоятельств, контейнер может увеличивать или уменьшать количество экземпляров компонентов в пуле. Компонент, находящийся в пуле, способен выполнять запросы ejbFind<суффикс> и ejbSelect<суффикс> (рассмотрим далее).
- При вызове методов ejbCreate() а затем метода ejbPostCreate() у домашнего интерфейса, или же когда контейнер активирует компонент посредством метода ejbActivate(), компонент переходит в состояние готовности. После завершения метода ejbCreate() создается первичный ключ - идентификатор созданного объекта, а затем уже вызывается метод ejbPostCreate(). В состоянии готовности компонент способен обслуживать запросы от клиента. Когда клиент вызывает метод удаленного интерфейса getName(), то этот метод вызывается посредством RMI у компонента на сервере. В состоянии готовности могут также вызываться методы ejbLoad() и ejbStore(). Они вызываются в любом порядке -порядок определяется производителем компонента. Некоторые производители вызывают эти методы перед каждым выполнением прикладного метода.
- Компонент может переходить обратно из состояния готовности в пул в случае пассивизации (посредством метода ejbPassivate() ). Эта операция может выполняться для более эффективного управления ресурсами. Также компонент может переходить обратно при вызове метода ejbRemove() - в этом случае все данные о компоненте удаляются из базы данных.
- Когда контейнер решит уменьшить количество экземпляров в пуле, он удаляет некоторые из них. Теперь сборщик мусора может их окончательно удалить из памяти. После удаления из пула, у компонента вызывается метод unsetEntityContext().
Пример "Космические корабли"
Компонент
Разработаем объектный компонент BMP. Он будет использоваться для хранения информации о космических кораблях. Параметры космических кораблей компонент будет получать из базы данных, посредством его set и get методов клиентская программа или другие компоненты смогут изменять эти параметры. Компонент будет называться WidgetBean.
Создание таблицы в базе данных
Как показано на Рис. 4.1, база данных космических кораблей состоит из одной таблицы. В ней каждому кораблю дается текстовое описание и цена. ID последнего добавленного корабля также сохраняется в пакете, что делает его доступным для прикладного приложения.
create or replace package WIDGET_ID_PKG as LAST_ID integer; function GET_LAST_ID return integer; end WIDGET_ID_PKG; / create or replace package body WIDGET ID PKG as function GET_LAST_ID return integer is begin return LAST_ID; end GET_LAST_ID; end WIDGET_ID_PKG; / drop table WIDGETS; create table WIDGETS ( ID integer not null, DESCRIPTION varchar(50) not null, PRICE float not null, primary key(ID) ); drop sequence WIDGETS_SEQ; create sequence WIDGETS_SEQ start with 1 increment by 1 nomaxvalue; create trigger WIDGETS_TRIGGER before insert on WIDGETS for each row begin select WIDGETS_SEQ.nextval into WIDGET_ID_PKG.LAST_ID from dual; select WIDGET_ID_PKG.LAST_ID into :new.id from dual; end; / insert into WIDGETS(DESCRIPTION, PRICE) values('Buran', 1000); insert into WIDGETS(DESCRIPTION, PRICE) values('Shuttle', 2000); insert into WIDGETS(DESCRIPTION, PRICE) values('Space Ship One', 3000);
Удаленный интерфейс
Вначале создадим для компонента удаленный интерфейс, который будет состоять только из get и set методов. Эти методы будут соответствовать полям в только что созданной таблице WIDGETS.
public interface WidgetRemote extends EJBObject { public Integer getId() throws RemoteException; public double getPrice() throws RemoteException; public String getDescription() throws RemoteException; public void setPrice(double price) throws RemoteException; public void setDescription(String description) throws RemoteException; }
Стоит отметить, что для ID определен только get метод. ID - это первичный ключ, создаваемый базой данных, он должен быть уникальным. Если позволить прикладному приложению его изменять, то может нарушиться условие уникальности первичных ключей. К тому же практический смысл операции изменения этого первичного ключа невелик.
Домашний интерфейс
Домашний интерфейс компонента WidgetBean несколько отличается от аналогичных домашних интерфейсов сеансовых компонентов. В нем помимо create методов определены find методы, которые позволяют осуществлять поиск компонентов по определенным параметрам. К примеру, можно искать все космические корабли с ценою не выше заданной.
Для компонента WidgetBean определен create метод, который создает новый экземпляр компонента с заданным описанием и ценой. В качестве find методов определены два метода, которые обычно присутствуют у всех домашних интерфейсов объектных компонентов -
- public Collection findAll() - возвращает коллекцию из всех компонентов, определенных на данный момент в базе данных. В качестве объектов коллекции выступают удаленные интерфейсы компонента.
- public WidgetRemote findByPrimaryKey(Integer widgetId) -возвращает удаленный интерфейс компонента с заданным первичным ключом. В случае, если компонента с таким первичным ключом не существует, генерируется исключение javax.ejb.FinderException.
public interface WidgetHome extends EJBHome { public WidgetRemote create(String description, double price) throws RemoteException, CreateException; public WidgetRemote findByPrimaryKey(Integer widgetId) throws FinderException, RemoteException; public Collection findAll() throws FinderException, RemoteException; }