Главная » Java, JavaBeans » Более сложные вопросы использования СМР

0

В предыдущих разделах были рассмотрены основы использования СМР. Было показано, как нужно работать с полями, управление которыми берет на себя Контейнер, а именно: такие поля нужно объявить как public-поля в классе реализации Компонента, а затем перечислить их в Дескрипторе Поставки. Хотя методы поиска объявлены в home- интерфейсе, их реализация отсутствует в классе Компонента. Описание логики работы этих методов также содержится в Дескрипторе Поставки. Эта информация используется Контейнером EJB для определения полей, сохранение состояния которых он берет на себя, а также для реализации методов поиска.

В этом разделе затрагиваются более сложные вопросы, связанные с использованием СМР, особенно для Inprise-реализации Контейнера EJB. В частности, подробно рассматривается объектная (на уровне Компонентов EJB) модель взаимодействия таблиц реляционных баз данных. Обсуждаются также различные стратегии построения такой модели, которые вы можете задавать при создании Дескриптора Поставки.

Объектная модель взаимодействия реляционных таблиц

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

Во многих случаях таблицы базы данных связаны с другими таблицами. Как правило, таблицы связаны друг с другом отношениями один-к-одному или один-ко-многим (и, соответственно, многие-к- одному). Связь между таблицами поддерживается с помощью удаленного ключа (foreign key).

Давайте посмотрим, как это могло бы выглядеть в реляционной базе данных, которая используется для обслуживания заказов покупателей. Покупатель размещает заказ на некоторые виды товаров, и в одном заказе может содержаться несколько записей для одного и того же товара. Соответственно, база данных может содержать таблицу Order, в которой хранится информация о заказах. С каждой записью этой таблицы сопоставлен ключ, значением которого является значение колонки order_id, которое уникальным образом идентифицирует каждый заказ. Каждая запись также содержит такую информацию, как дата заказа, имя покупателя, общая стоимость и т.п. Каждая запись другой таблицы, которая называется Lineltem, содержит отдельную запись на каждый заказанный товар. Конкретный заказ может иметь несколько записей о товаре. Таблица Lineltem содержит удаленный ключ, который соответствует полю order_id в таблице Order, который позволяет определить, какому заказу принадлежит данный товар.

На рисунке 7.2 показаны два заказа, хранящиеся в таблице Order; значения их ключей равны orderl и order2. Столбец удаленного ключа Lineltem показывает, что заказ1 состоит из трех позиций, а заказ2 – из одной. Каждой записи в таблице Lineltem сопоставлена одна, и только одна, запись в таблице Order.

Рис. 7.2 Отношения между таблицами и записями

Хотя entity-Компоненты могут использовать BMP для представления этих отношений между Компонентами, для этого также возможно использовать СМР. Моделирование таких отношений с использованием Entity-Компонентов с СМР – задача, намного более простая.

Реализация отношения один-к-одному

Entity-Компоненты с СМР реализуют соотношение один-к-одному (одна запись из Lineltem к одной записи в Order) естественным образом. Чтобы посмотреть, как экземпляр ЫпеКет-Компонента может найти сопоставленный с ним экземпляр Order-Компонента, давайте ознакомимся со структурой Компонентов.

Класс OrderBean вместе с классом Order (remote-интерфейс) и OrderHome (home-интерфейс) определяют представление таблицы Order. Таким же образом класс LineltemBean является моделью таблицы Lineltem, и для него определены remote-интерфейс Lineltem и home-интерфейс LineltemHome.

Компонент OrderBean содержит public-поля, которые соответствуют колонкам таблицы Order. Эти поля имеют имена order id, customer, price и т.д. (Обратите внимание, что entity-Компонент может информировать Контейнер о том, что нужно использовать различные имена для полей и для столбцов базы данных – в Дескрипторе Поставки предусмотрены свойства, которые обеспечивают соответствия между SQL-именами столбцов и Java-именами полей). Поле order id является главным ключом таблицы OrderBean. Код Компонента OrderBean содержит следующие объявления полей (см. пример кода 7.10):

Пример Кода 7.10 Объявления полей Компонента OrderBean

public class OrderBean implements EntityBean{ public String order id; public String customer;

public float price;

Компонент LineltemBean также содержит public-поля, которые соответствуют колонкам таблицы Lineltem. Хотя поле order id является удаленным ключом, оно не реализовано с помощью рптагу-кеу-класса, зато оно реализовано с помощью типа remote-интерфейса Order. Другими словами, оно представляет собой объектную ссылку на объект типа Order. Для Inprise-реализации EJB, любое поле entity-Компонента с СМР может использоваться как колонка удаленного ключа в таблице базы данных. Контейнер EJB трактует такие поля как ссылку на EJBObject. Компонент LineltemBean также объявляет метод getOrder(), который возвращает ассоциированный с ним огёег-объект. Все это показано в примере кода 7.11.

Пример Кода 7.11 Объявление полей Компонента с СМР

public class LineltemBean implements EntityBean { public itemno; public Order order;

public Order getOrder () { return order;

}

Дескриптор Поставки Компонента LineltemBean содержит свойство, которое хранит значение JNDI-имени для интерфейса OrderHome. Контейнер использует это имя для поиска home-интерфейса, как это показано на рисунке 7.3.

Рис. 7.3 Реализация отношения один-к-одному с помощью entity-Компонента с СМР.

Одним из возможных путей работы является следующий: Контейнер получает значение главного ключа для экземпляра Компонента Lineltem, а затем вызывает метод findByPrimaryKey () home-интерфейса OrderHome для получения ссылки на экземпляр OrderBean.

Inprise-Контейнер EJB использует значение удаленного ключа – объектную ссылку на remote-интерфейс Order – в классе Компонента LineltemBean для непосредственного получения ссылки на нужный экземпляр OrderBean. Используя ссылку на OrderHome в Дескрипторе Поставки и ссылку как удаленный ключ, Inprise-Контейнер EJB позволяет обойтись без вызова метода findByPrimaryKey () интерфейса OrderHome. Такой способ позволяет существенно повысить производительность приложения, так как обычно этот метод обращается к базе данных.

Контейнер создает объектную ссылку на remote-интерфейс Order, т.е. на конкретный экземпляр OrderBean, непосредственно. Тем не менее, Контейнер не считывает состояние экземпляра OrderBean, пока этот объект не будет использован. Другими словами, когда Контейнер считывает состояние экземпляра LineltemBean, он не считывает состояние OrderBean. Иногда это называют "lasy reference", и это позволяет существенно увеличить производительность системы.

Реализация отношения один-ко-многим

Inprise-Контейнер EJB также способен моделировать отношения один- ко-многим или (что то же самое) многие-к-одному при использовании Компонентов с СМР. Используя рассмотренные в предыдущем примере реализации Order и Lineltem, Контейнер легко может найти все данные, сопоставленные с конкретным заказом.

Для выполнения этого Контейнер ищет соответствия между ссылками на EJB-object, которые моделируют значения удаленного ключа для entity-Компонента на стороне "многие", с полем главного ключа Компонента на стороне "один". В рассмотренном выше примере, Компонент Order содержит главный ключ, который идентифицирует каждый конкретный заказ, и он имеет значения, такие как orderl и order2. Каждый экземпляр Компонента LineltemBean – т.е. каждая позиция, относящаяся к заказу – содержит удаленный ключ, чье значение идентифицирует заказ, с которым сопоставлена данная запись. Иллюстрация процесса приведена на рисунке 7.4, за которым следуют пояснения.

Рис. 7.4 Реализация отношения один-ко-многим при использовании СМР

Чтобы все это работало, реализация Компонента на стороне "один" – т.е. OrderBean – должна содержать метод, который возвращает все относящиеся к нему "многие" экземпляры – т.е. Компоненты типа LineltemBean. Этот метод, который мы назвали getLineltems (), базируется на использовании методов поиска home-интерфейса LineltemHome, который ищет все entity-объекты, значения удаленных ключей которых соответствуют значению главного ключа Компонента OrderBean. Поскольку мы работаем с СМР, метод поиска в home- интерфейсе представляет собой просто декларацию метода. Его логика описана в Дескрипторе Поставки. Свойство Дескриптора Поставки информирует Контейнер EJB о том, как нужно сконструировать предложение WHERE SQL-запроса для поиска тех объектов LineltemBean, значения удаленных ключей которых равны значению главного ключа Компонента OrderBean.

В нашем примере класс OrderBean содержит метод, который возвращает java.util.Enumeration или java.util.Collection – т.е. набор – всех нужных экземпляров LineltemBean. (Используйте интерфейс Collection при работе с Java 2. Используйте интерфейс Enumeration, если вы работаете с более ранними версиями JDK. Вы можете продолжать использовать интерфейс Enumeration даже при работе с Java 2; интерфейс Collection недоступен в более ранних версиях JDK.) Например, вы могли бы использовать следующее объявление метода:

java.util.Enumeration getLineltems() throws java.rmi.RemoteException;

или:

java.util.Collection getLineltems() throws java.rmi.RemoteException; Этот метод использует контекст Компонента, который становится известен при вызове метода setEntityContext (), для выполнения поиска home-интерфейса LineltemHome. Пример кода 7.12 показывает наиболее важные фрагменты кода этого метода.

Пример Кода 7.12 Метод getLineltems() Компонента OrderBean

public class OrderBean implements EntityBean { private LineltemHome lineltemHome;

public void setEntityContext(EntityContext context) throws java.rmi.RemoteException { _context = context;

}

public java.util.Collection getLineltems() throws java.rmi.RemoteException {

try {

Order self = (Order) _context.getEJBObject(); return lineltemHome.findByOrder(self); catch (javax.ejb.FinderException e) {…}

}

Класс OrderBean содержит private-поле lineltemHome для хранения ссылки на интерфейс LineltemHome. Метод setEntityContext () сохраняет контекст Компонента в поле _context. В методе getLineltems () происходит обращение к методу context.getEJBObject () для получения ссылки на его remote-интерфейс Order. После получения этой ссылки можно обратиться к методу findByOrder (), объявленному в интерфейсе LineltemHome, и передать ему в качестве аргумента ссылку на этот remote-интерфейс.

Для выполнения всех необходимых действий объявление метода findByOrder () могло бы выглядеть так:

java.util.Collection findByOrder (Order order) throws j ava.rmi.RemoteException, j ava.ej b.FinderException;

Контейнер EJB генерирует реализацию метода findByOrder () в соответствии с информацией в Дескрипторе Поставки. Свойства Дескриптора Поставки содержат часть SQL-оператора для выборки всех Компонентов Lineltem, значение поля order id которых равно значению параметра о, ключевому значению для OrderBean-Компонента. Этот оператор мог бы выглядеть так:

WHERE Lineltem = :о

В Дескрипторе Поставки также содержится свойство, которое хранит значение JNDI-имени для интерфейса OrderHome, чтобы Контейнер EJB мог найти этот интерфейс.

Объектное представление реляционных соотношений с точки зрения клиента

Код клиентского приложения может непосредственно использовать реализацию только что рассмотренной объектной модели.

Приложение OrderClient показывает, как могут выглядеть соотношения "один-к-одному" и "один-ко-многим" (в Примере Кода 7.13 приведены наиболее интересные фрагменты). Имея определенную строку, относящуюся к заказу, клиент получает номер этого заказа. Затем, используя этот номер, клиент получает список всех позиций по заказу. В приведенном примере используется интерфейс Collection (приведены только наиболее важные фрагменты кода).

Пример Кода 7.13 Фрагмент кода приложения OrderClient

import java.util.*; public class OrderClient {

public static void main(String[] args) throws Exception {

Order myOrder; Lineltem myltem;

// получение номера заказа по записи о товаре System out println (" Line item: " + myltem.getPrimaryKey() + " belongs to Order " + myltem.getOrder().getPrimaryKey());

// получение списка (Collection) товаров для данного заказа System.out.print("The order " + myOrder getPrimaryKey!) + "

contains these items:"); Iterator iterator = myOrder.getLineltems().iterator(); while (iterator.hasNext()) { Lineltem item = (Lineltem)

j avax.rmi.PortableRemoteObj ect.narrow (iterator.next (), Lineltem.class); System.out.print(" " + item.getPrimaryKey());

}

Источник: Руководство программиста Enterprise JavaBeans

По теме:

  • Комментарии