Rambler's Top100IT • archiv

rus / eng | Логин | Комментарий к колонке | Печать | Почта | Клуб




Колонки


Enterprise JavaBeans. Часть 5

 
(Гопалан Суреш Рай)
Создание многоуровневых приложений - создание Entity-бина

Для реализации примера с созданием виртуального книжного online-магазина "Лошади и конюшня" вначале необходимо будет разработать компонент, который мы назовём entity-бин. Для использования в качестве тележки для покупок мы создадим бин-перечень. Наш entity-бин-перечень должен иметь методы для получения и установки аттрибутов названий книг, которые продаёт наш магазин. Он так же может иметь возможность запрашивать такие данные как цена или индекс ISBN.

Традиционной последовательностью разработки entity-бина считается следующий порядок:

  1. Установить исходные данные.
  2. Определить EJB remote-интерфейс.
  3. Определить EJB home-интерфейс.
  4. Определить исходный ключевой класс.
  5. Реализовать enterprise bean.
  6. Скомпилировать классы EJB.
  7. Задекларировать свойства защиты данных и развертывания бина.
  8. Сгенерировать с помощью предоставленных производителем серевера/контейнера EJB средств классы контейнера, зарегестрировать на сервере factory и затем установить и запустить его.
  9. Написать клиентский код.
  10. Скомпилировать клиентский код.
  11. Запустить клиента.

А теперь детально разберём каждый пункт.

Установите свои исходные данные

Для примера программы нам потребуется таблица базы данных под названием BOOKS (КНИГИ). Соответствующий язык манипуляции данными (DML) для создания такой таблицы приводится в Примере 5:

CREATE TABLE BOOKS

CODE Number (КОД Число)
TITLE Text (НАЗВАНИЕ Текст)
AUTHORS Text (АВТОР Текст)
PRICE Currency (ЦЕНА Валюта)
DISCOUNT Number(СКИДКА Число)

*primary key (CODE)

Пример 5: Язык DML для таблицы BOOKS

Определите свой EJBhome-интерфейс

Клиенты EJB не работают напрямую с entity-бином для создания строк или доступа к данным, а скорее создают экземпляр home-интерфейса. Этот интерфейс расширяет javax.ejb.EJBHome и включает в себя методы, которые определяют, каким образом создаётся и где в контейнере располагается entity-бин. Он также подчиняется правилам RMI в том, что аргументы и возвращаемые типы для каждого из методов должны быть сериализуемыми, а методы должны посылать в качестве одного из своих исключений java.rmi.RemoteException. В Примере 6 приводится определение такого интерфейса:

package com.gopalan.Shop.Books;
import javax.ejb.*;
import java.rmi.*;
public interface BooksHome extends EJBHome {
 
 Books create (int isbn, String title, String authors,
               double price, int discount)
  throws CreateException, RemoteException;
Books findByPrimaryKey (BooksPK book)
  throws FinderException, RemoteException;
}

Пример 6: Определение BooksHome-интерфейса

Метод create() соответствует методу ejbCreate() в entity-бине. Установки параметров в обоих методах идентичны. Когда клиент вызывает BooksHome.create(), контейнер находит экземпляр entity-бина и вызывает BooksHome.ejbCreate(). Для управляемых контейнером entity-бинов метод ejbCreate()возвращает void, в отличие от случаев управляемой контейнером персистентности, когда он возвращает исходный key object. Первое, что делает клиент, это с помощью JNDI находит home-объект для требуемого бина. BooksHome-интерфейс содержит методы create(), которые будут вызываться всегда, когда клиенту понадобится новый бин. Заметьте, что этот метод реализуется в EJBHome и всякий раз при своей инициализации вызывает метод ejbCreate().

Определите свой EJBremote-интерфейс

После того как создаётся экземпляр реализации home-интерфейса, сервер EJB создаёт экземпляры реализации remote-интерфейса и самого бина. В качестве методов в этом интерфейсе используются внешние интерфейсы нашего BooksBean. Сигнатуры методов в этом внешнем интерфейсе идентичны тем, что принадлежат бину, за исключением того, что эти методы посылают java.rmi.RemoteException. Наш entity-бин не реализует этот интерфейс напрямую. Реализацией этого интерфейса занимается соответствующий сгенерированный контейнером код EJBObject. EJBObject действует как proxy, пересылая вызовы методов экземпляру бина, установленному на сервере. Данный remote-интерфейс показан в Примере 7:

package com.gopalan.Shop.Books;
import javax.ejb.*;
import java.rmi.*;
public interface Books extends EJBObject {
 
 public int getIsbn () throws RemoteException;
public String getTitle () throws RemoteException;
public String getAuthors () throws RemoteException;
public double getPrice () throws RemoteException;
public int getDiscount () throws RemoteException;
public double getDiscountedPrice () throws RemoteException;
public void setPrice (double cost) throws RemoteException;
public void setDiscount (int disc) throws RemoteException;
}

Пример 7: Определение remote-интерфейса для бина Books

Обратите внимание, что remote-интерфейс бина Books расширяет javax.ejb.EJBObject и объявляет принадлежащие бину BooksBean методы get/set. Он также подчиняется правилам RMI в том, что аргументы и возвращаемые типы каждого метода должны быть Serializable, а методы должны посылать в качестве одного из своих исключений java.rmi.RemoteException.

Определите свой исходный ключевой класс

Сервер EJB требует, чтобы у entity-бина был исходный ключевой класс (primary key class) с открытым элементом (или элементами) исходных данных. Вы можете сделать так, чтобы бин управлялся контейнером, или написать код, чтобы управлять бином лично. В приведенном ниже примере бин управляется контейнером. В Примере 8 показано определение BooksPK Primary Key Class.

package com.gopalan.Shop.Books;
public class BooksPK implements java.io.Serializable {
 public int code;
public BooksPK () {
}
public BooksPK (int isbn) {
  code = isbn;
 }
}

Пример 8: Определение класса BooksPK Primary Key

Исходным ключевым классом в таблице BOOKS выступает CODE, поэтому code определён как открытый элемент в данном классе, которому во время создания класса приписывается какое-либо значение.

Реализация entity-бина

Данный enterprise bean реализует javax.ejb.EntityBean и выбранные разработчиком методы интерфейса. Он должен соответствовать правилам RMI в том, что все аргументы и возвращаемые типы каждого из методов должны сериализовываться, а методы должны в качестве одного их своих исключений посылать java.rmi.RemoteException. Пример 9 показывает реализацию нашего BooksBean:

package com.gopalan.Shop.Books;
import java.rmi.*;
import javax.ejb.*;
public class BooksBean implements EntityBean {
 
 EntityContext entityContext;
public int code;        // CODE
public String title;   // TITLE
public String authors; // AUTHORS
public double price;   // PRICE
public int discount;   // DISCOUNT
// Implementation for all the Remote Interface business methods
public int getIsbn () throws RemoteException {
  return code;
 }
public String getTitle () throws RemoteException {
  return title;
 }
public String getAuthors () throws RemoteException {
  return authors;
 }
public double getPrice () throws RemoteException {
  return price;
 }
public int getDiscount () throws RemoteException {
  return discount;
 }
public double getDiscountedPrice () throws RemoteException {
  double deduct = ((double)discount)/100;
  return ( price*(1-deduct) );
 }
public void setPrice (double cost) throws RemoteException {
  price = cost;
 }
public void setDiscount (int disc) throws RemoteException {
  discount = disc;
 }
// Implementation for all the Home Interface methods
public void ejbCreate (int isbn, String bookTitle, String author,
                           double cost, int disc)
  throws CreateException, RemoteException {
  code = isbn;
  title = bookTitle;
  authors = author;
  price = cost;
  discount= disc;
}
public void ejbPostCreate (int isbn, String bookTitle, String author,
                               double cost, int disc) {
}
// Implement all the mandatory methods required by the EJB Spec
public void ejbActivate () throws RemoteException{}
public void ejbLoad () throws RemoteException{}
public void ejbPassivate () throws RemoteException{}
public void ejbRemove () throws RemoteException, RemoveException{}
public void ejbStore () throws RemoteException{}
public void setEntityContext (EntityContext context)
  throws RemoteException {
  entityContext = context;
 }
public void unsetEntityContext () throws RemoteException {
  entityContext = null;
 }
}

Пример 9: Имплементация класса бина BooksBean

Обратите внимание, что все методы, составляющие remote-интерфейс, и методы для создания и поиска, составляющие home-интерфейс, были реализованы в этом enterprise Bean.

Скомпилируйте свои классы для EJB

Все исходники, которые Вы написали, теперь можно скомпилировать. Приступайте.

javac *.java

Задекларируйте описания развертывания бина

С этого момента всё, что мы здесь обсуждаем, будет относиться к реализации данного конкретного серверного бина. Сервер HomeBase производства Iona требует, чтобы наши описание были выполнены на XML. (Сведения по XML Вы можете почерпнуть из любой посвященной этой теме книги. Детальное описание природы XML не входит в цели данной статьи.) Данные свойства используются как при генерации классов контейнеров, так и для их установки и выполнения. Нам необходимо определить определённое количество свойств, которые помогут сгенерировать классы контейнера. В файле свойств (назовём его Shop.ejbml) введите текст, указанный в Примере 10:

<!---------------------------------------------------------->
<!------- Books Entity Bean ---------------->
<!---------------------------------------------------------->
<ejbml>
<entity-bean
name="Books"
descriptor="Books/BooksDeployment"
package="com.gopalan.Shop.Books"
home="com.gopalan.Shop.Books.BooksHome"
remote="com.gopalan.Shop.Books.Books"
bean="com.gopalan.Shop.Books.BooksBean"
primary-key="com.gopalan.Shop.Books.BooksPK"
tx-attribute="TX_SUPPORTS"
>
<property
name="databasePassword"
value="mouse"
/>
<property
name="dataSourceName"
value="Shop"
/>
<property
name="databaseUser"
value="user"
/>
<property
name="databaseTable"
value="books"
/>
<container-managed
storage-helper=
"com.ejbhome.generator.helpers.RelationalPersistenceCodeHelper"
table="books"
data-source="Shop"
user="user"
password="mouse"
>
<field name="discount" />
<field name="price" />
<field name="authors" />
<field name="title" />
<field name="code" />
</container-managed>
</entity-bean>
</ejbml>

Listing 10: Описание развёртывания Books EJB

Первые несколько строк указывают на имя наших home и remote-интерфейсов и подчеркивают, что это entity-бин. В этом же файле мы также объявили наш Primary Key class. Так как это управляемый контейнером бин, мы определяем поля базы данных, которыми управляет контейнер. А также в транзактных аттрибутах бина мы указываем, что наш бин поддерживает транзакции (TX_SUPPORTS).

Транзактные аттрибуты, которые можно указать, приведены в Таблице 3.

TX_BEAN_MANAGED

Enterprise bean начинает и заканчивает транзакцию. Он может использовать интерфейс javax.jts.UserTransaction для определения границ транзакции.

TX_MANDATORY

Вызывающий должен начать транзакцию. Бин всегда инициируется в рамках клиентской транзакции. Если клиент не имеет таковой, посылается javax.transaction.TransactionRequiredException.

TX_REQUIRED

Enterprise bean запрашивает транзакцию. Если клиент связан с контекстом транзакции, бин инициируется в том же контексте. В противном случае, контейнер начинает новую транзакцию, прежде чем инициировать методы, обращаюшиеся к бину, и фиксирует результат транзакции, прежде чем они отработают.

TX_REQUIRES_NEW

Enterprise bean требует, чтобы на каждый вызов метода была начата новая транзакция.

TX_NOT_SUPPORTED

Транзакция вызывающего будет приостановлена до инициации бина. Бин инициируется за пределами транзакции.

TX_SUPPORTS

Транзакция вызывающего просто пересылается контейнером EJB. Если вызывающий не имеет транзактного контекста, методы бина инициируются без транзактного контекста.

Таблица 3: Транзактные аттрибуты

Так как enterprise bean управляется контейнером, мы указываем поля, которые управляются контейнером, чтобы средство разработки могло правильно сгенерировать код для JDBC. Затем мы указываем имя источника данных, имя таблицы и пароль, который может быть полезен при обрашениях нашего бина через JDBC.

Зарегистрируйте и установите бин

Специфика данного пунка будет зависеть от типа сервера/контейнера EJB, которым Вы пользуетесь. Сервер EJBHome отсылает к файлам datasource.properties и ejbhome.properties, расположенным в директории под именем conf.

Вам нужно модифицировать файл datasource.properties, как показано в Примере 11:

# This is a list of datasource names with their respective JDBC databases URLs.
#
# For example:
#
# To map a datasource name of jdbc/Inventory to an Oracle lite database called test,
# you would use the following:
#
# jdbc/Inventory=jdbc:polite:test
#
Books=jdbc:odbc:Books,user=user,password=mouse
Music=jdbc:odbc:Books,user=user,password=mouse

Пример 11: Определение исходных данных для установки и применеия бина на HomeBase

Сгенерируйте классы контейнера

Теперь с помощью прилагаемого к серверу/контейнеру средства нужно сгенерировать классы контейнера. Когда entity-бин и session-бин установлены, создаётся несколько файлов для исходников и классов контейнера, которые получают следующие префиксы. Контейнер - это набор классов, сгенерированных средством выполнения, управляющим персистентностью бина, свойствами транзакций и системой защиты данных. Теперь, когда наш Shop.ejbml готов, мы можем сгенерировать и скомпилировать классы контейнера. Для этого нам нужно запустить установленное в HomeBase средство выполнения. Это делается следующим образом:

java com.ejbhome.Deployer .\conf\Shop.ejbml

Приведенный ниже пример показывает, как будет выглядеть консоль нашего экрана, когда мы будем компилировать и разворачивать наши файлы. Наши команды выделены жирным шрифтом.

E:\>javac cup\chap8\Shop\Books\*.java
E:\>java com.ejbhome.Deployer .\conf\Shop.ejbml
EJBHome EJB Deployer version 0.5.1 (Scunthorpe)
(c) Copyright IONA Technologies PLC 1999. All Rights Reserved.
Windows NT x86 4.0
A nonfatal internal JIT (3.00.072b(x)) error 'regvarHI' has occurred in :
'com/ejbhome/Deployer.<init> (Ljava/util/Properties;Ljava/util/Vector;)V': Interpreting method.
Please report this error in detail to http://java.sun.com/cgi-bin/bugreport.cgi
Deploying: Books...
Generating: IonaBooksHome...done.
Generating: IonaRemoteBooks...done.
Generating: IonaBooksBean...done.
Generating: IonaBooksContext...done.
Implementing Communications: Home Stubs & Skels...done.
Implementing Communications: Remote Stubs & Skels...done.
Compiling: IonaBooksHome.java...done.
Compiling: IonaRemoteBooks.java...done.
Compiling: IonaBooksBean.java...done.
Compiling: IonaBooksContext.java...done.
Compiling: IonaBooksHome_Stub.java...done.
Compiling: IonaBooksHome_Skel.java...done.
Compiling: IonaRemoteBooks_Stub.java...done.
Compiling: IonaRemoteBooks_Skel.java...done.
Deploying: Music...
Generating: IonaMusicHome...done.
Generating: IonaRemoteMusic...done.
Generating: IonaMusicBean...done.
Generating: IonaMusicContext...done.
Implementing Communications: Home Stubs & Skels...done.
Implementing Communications: Remote Stubs & Skels...done.
Compiling: IonaMusicHome.java...done.
Compiling: IonaRemoteMusic.java...done.
Compiling: IonaMusicBean.java...done.
Compiling: IonaMusicContext.java...done.
Compiling: IonaMusicHome_Stub.java...done.
Compiling: IonaMusicHome_Skel.java...done.
Compiling: IonaRemoteMusic_Stub.java...done.
Compiling: IonaRemoteMusic_Skel.java...done.
Deploying: Cart...
Generating: IonaCartHome...done.
Generating: IonaRemoteCart...done.
Generating: IonaCartBean...done.
Generating: IonaCartContext...done.
Implementing Communications: Home Stubs & Skels...done.
Implementing Communications: Remote Stubs & Skels...done.
Compiling: IonaCartHome.java...done.
Compiling: IonaRemoteCart.java...done.
Compiling: IonaCartBean.java...done.
Compiling: IonaCartContext.java...done.
Compiling: IonaCartHome_Stub.java...done.
Compiling: IonaCartHome_Skel.java...done.
Compiling: IonaRemoteCart_Stub.java...done.
Compiling: IonaRemoteCart_Skel.java...done.
E:\>

Теперь с помощью этой команды вы можете запустить сервер:

java com.ejbhome.Server

Напишите клиентский код

В примере, приведенном ниже, предлагается образец клиентского кода для entity-бина. Клиент обычно работает самостоятельно и вносит в базу данных записи и обновляет цены.

В начале клиент производит Naming.lookup()и находит ссылку на интерфейс BooksHome. Затем он создаёт набор экземпляров entity-бина и вносит соответствующие записи в базу данных. Кроме этого клиент выполняет различные методы нашего ShopBean, типа поиска или установки цены или названия книги. В Примере 12 показано приложение BooksTest, которое является клиентом нашего entity-бина Books.

package com.gopalan.Shop.Books;
import java.rmi.*;
import java.util.*;
public class BooksTest {
 
 static final int NUMBOOKS = 4;
public static void main(String[] args) throws Exception {
  
  BooksHome home = (BooksHome)Naming.lookup ("Books");
  System.out.println ( "Naming.lookup successful..." );
  if (home == null) {
   System.out.println( "null BooksHome returned..." );
  }
  String title = "Book";
  String author= "Author";
  Vector v = new Vector ();
  for (int i = 0; i < BooksTest.NUMBOOKS; i++) {
   System.out.println ("ISBN = " +(i+1) +
                       " Book = " +title+(i+1) +
                       " Author = " +author+(i+1) +
                       " Creating home.create...");
   v.addElement (home.create ((i+1),title+(i+1),
                              author+(i+1),100.00,10));
  }
  for (int i = 0; i < BooksTest.NUMBOOKS; i++) {
   Books books= (Books) (v.elementAt (i));
   books.setPrice (books.getPrice ()+1);
   System.out.println ("Final Price of " + books.getTitle () +
                       " is "+ books.getPrice ());
  }
  System.out.println ("Books.setBooksPrice successful...");
  for (int i = 0; i < BooksTest.NUMBOOKS; i++) {
   Books books= (Books)(home.findByPrimaryKey (new BooksPK (i+1)));
   books.setPrice (books.getPrice ()+1);
   System.out.println ("Final Price of " + books.getTitle () +
                       " is "+ books.getPrice ());
  }
  System.out.println ("Books.findByPrimaryKey successful...");
}
}

Пример 12: Клиентское приложение BooksTest нашего entity-бина

Теперь скомпилируйте и запустите клиента. Клиент создаст четыре записи в базе данных. Когда мы будем обсуждать session-бины, Вы увидите, как смоделировать session-бин, чтобы он мог выступать клиентом нашего entity-бина.

TOC | Часть 6 >




Справка | Условия Copyright © 1999 — 2008, IT • archiv.
В начало | Логин | Комментарий к колонке | Поиск | Почта