Технология Клиент-Сервер 2001'4 |
|||||||
|
При общении с разработчиками, использующими технологию EJB, довольно часто возникает вопрос о разумности и эффективности использования entity-компонентов. Поскольку обсуждаемая тема может быть интересна и программистам, только начинающим использовать технологии EJB и J2EE, имеет смысл остановиться на том, что же из себя представляют entity-компоненты.
Понятие «entity-компонент» может относиться к двум различным вещам:
во-первых, entity-компонентом можно назвать набор классов и интерфейсов Java, а также XML-документ (или фрагмент XML-документа), которые создаются программистом и которые определяют все аспекты поведения будущих экземпляров этого компонента. Короче, это классический тип данных – разумеется, применительно к компонентной модели. В таком понимании можно говорить о том, что «программист создал новый entity-компонент» - подразумевая, что программист создал новый тип данных.
Во-вторых, под entity-компонентом можно понимать логическое понятие, необходимое (или удобное) для объектного представления конкретного фрагмента информации в некоторой базе данных.
Существует еще и понятие «экземпляра entity-компонента». Это просто переменная языка Java. Технология EJB полностью «отделяет» программиста от экземпляра компонента. Программист может влиять на число экземпляров компонента, находящихся в EJB-контейнере, только косвенно. Кроме того, программист никогда непосредственно не обращается к экземпляру компонента. Можно с некоторой натяжкой сказать, что особенности реализации и цикла жизни экземпляров компонентов программиста не интересуют – он имеет дело с компонентами (во втором смысле), а не с их экземплярами.
Рассмотрим некоторую реляционную базу данных, содержащую таблицу с 1000 записей. В первом смысле можно сказать, что программист создает один entity-компонент – для работы с любой из записей этой таблицы. Особенности этого компонента как типа данных однозначно определяются именем и структурой таблицы, URL базы данных и бизнес-логикой системы, и не зависят от количества записей в таблице.
В то же время можно говорить (во втором смысле нашего определения компонента), что созданы 1000 entity-компонентов, каждый из которых предназначен для работы с отдельной записью из таблицы. Эти компоненты начинают существовать в тот момент, когда принимается решение, что к данным в таблице желательно обеспечить доступ с помощью технологии EJB с использованием entity-компонентов и что каждый компонент служит для представления информации ровно из одной записи. При этом не важно, существует уже или нет хоть один экземпляр компонента, установлен ли EJB-компонент (как тип данных) в контейнер EJB и даже то, запущен ли в данный момент контейнер EJB вообще. Другими словами, наш entity-компонент в такой трактовке – чисто логическое понятие.
Ничего определенного о количестве экземпляров наших entity-компонентов в этой ситуации сказать нельзя – все зависит от особенностей реализации конкретного контейнера EJB и параметров его настроек.
Допустим, несколько клиентов одновременно обращаются к одному и тому же entity-компоненту – то есть к данным, находящимся в одной записи нашей таблицы. Один EJB-контейнер может создать один разделяемый экземпляр entity-компонента, с которым будут работать все клиенты, а другой – несколько экземпляров одного и того же компонента (по числу клиентов).
Entity-компонент имеет много общего с session-компонентом. Как минимум, для него нужно написать класс компонента, определить home- и remote-интерфейсы, а также дескриптор развертывания – в виде XML-документа.
С принятием спецификации EJB 2.0 терминология немного изменилась: было введено понятие локальных интерфейсов, т.е. чистых интерфейсов Java, которые не обязаны удовлетворять требованиям RMI/IIOP и, следовательно, годятся только для выполнения локальных (в пределах одной виртуальной машины Java) вызовов. В связи с этим, термин «remote-интерфейс» из EJB 1.1 был заменен на «component-интерфейс» в EJB 2.0. Клиентами всех видов вызываются именно методы component-интерфейса. Существует два вида component-интерфейсов (как и home-интерфейсов) – local (вызовы в пределах одной JVM) или remote (удаленные вызовы).
Принципиальным отличием entity-компонента от session-компонента является наличие в классе компонента поля (или полей), которые хранят значение, позволяющее отличить данный entity-компонент от других. Это поле выполняет роль ключевого поля в реляционных базах данных. В общем случае необходимо определить новый тип данных для этого поля (создать новый класс Java). Этот класс называется «PrimaryKey-классом». Естественно, новый класс нужно создавать не всегда – в качестве primaryKey-класса можно использовать стандартные классы Java, например, java.lang.String или java.lang.Integer.
Интересным является следующий случай: контейнер создал экземпляр entity-компонента, он находится в памяти, но при этом контейнер не сопоставил этот экземпляр ни с какими данными в базе данных (например, с конкретной записью). Обычно это выражается в том, что «ключевое» поле в классе компонента имеет значение null. Очевидно, что несколько таких экземпляров нельзя отличить друг от друга. В таком случае говорят, что данный экземпляр «не идентифицирован (не имеет identity)». Как правило, EJB-контейнер создает пул таких экземпляров. Первый попавшийся из них сопоставляется с данными в базе данных в момент, когда клиент выдает одну из create- или find-команд home-интерфейса.
Ниже приведены две диаграммы (они взяты из спецификации EJB 2.0). Первая показывает, как выглядит и «живет» entity-компонент с точки зрения клиента (рисунок 1).
Важно подчеркнуть следующие обстоятельства:
entity-компоненты могут быть созданы и уничтожены как с помощью вызовов EJB API (например, create() или remove()), так и любыми средствами работы с БД (например, с помощью SQL-операторов INSERT или DELETE).
entity-компонент существует и идентифицирован после вызова одного из create- или find-методов его home-интерфейса.
Спецификация EJB 2.0 позволяет в home-интерфейсе определить «статические» методы, т.е. методы, которые могут быть вызваны клиентом не для конкретного entity-компонента, а для его класса.
Вторая диаграмма показывает цикл жизни entity-компонента (т.е. различные этапы взаимодействия entity-компонента, клиента и контейнера EJB) (Рисунок 2)
В состоянии «pooled» находятся entity-компоненты (здесь правильнее говорить не о компонентах, а об их экземплярах), с которыми еще не сопоставлен identity. Размер этого пула является настраиваемым параметром для всех промышленных серверов приложений.
Второе возможное состояние компонента – состояние «ready». Переход в него выполняется либо в результате вызова клиентом одного из create-методов home-интерфейса компонента, либо по команде контейнера EJB (которая сопровождается вызовом callback-метода ejbActivate()).
В состоянии "ready" компонент сопоставлен с конкретным фрагментом данных в БД (например, с некоторой записью). При этом его ключевое поле получает определенное значение. Если наш entity-компонент сопоставлен с записью из таблицы реляционной СУБД, то ключевое поле этого компонента (любого его экземпляра), скорее всего, получит значение, соответствующее ключевому полю в этой записи.
Когда компонент находится в состоянии «ready», контейнер (или клиент) могут произвольное число раз вызывать методы ejbLoad() и ejbStore(), определенные в классе компонента. Метод ejbLoad() логически эквивалентен SQL-оператору SELECT, а метод ejbStore() – оператору UPDATE. Спецификация EJB не оговаривает строго все случаи, когда контейнер выполняет вызов этих методов. Здесь все зависит от конкретной реализации.
Entity-компоненты решают, главным образом, четыре задачи:
создание объектного представления данных из любых БД, к которым можно обратиться с помощью JDBC API;
кеширование данных из БД на уровне сервера приложений (а не на сервере баз данных и не на стороне клиента);
обеспечение настройки готовой программы (с точки зрения доступа к БД) применительно к требованиям каждого конкретного клиента без перекомпиляции кода программы;
обеспечение конкурентного доступа к данным и автоматическое управление транзакциями, включая распределенные транзакции с двухфазной фиксацией.
Надо также иметь в виду, что доступ к EJB-компонентам могут получить любые клиентские приложения, использующие CORBA 2.3 и старше. Это обстоятельство может быть очень важным при наличии большого количества ранее написанных на C++, Delphi (или других языках) клиентских приложений.
Рассмотрим немного подробнее каждый из приведенных пунктов.
EJB (совместно с JDBC) предлагает схему отображения как типов данных, характерных для реляционных СУБД, в их Java-эквиваленты, так и связей и ограничений на уровне СУБД в соотношения между Java-классами. Таблица соответствий типов данных приведена в спецификации JDBC, которую можно найти, например, по адресу http://java.sun.com/proucts/jdbc/download.html#corespec30.
Программист определяет EJB entity-компонент как класс (или несколько связанных классов), который (или которые) содержат поля, соответствующие полям из одной или нескольких записей из одной или нескольких таблиц JDBC-базы данных.
Важной особенностью реляционно-объектного отображения является то, то EJB предоставляет программисту возможность выбора. Либо программист полагается на стандартные возможности EJB, обеспечивающие автоматическое отображение реляционного представления данных в объектное и обратно (на основе формального описания схемы отображения на языке XML), либо он сам полностью определяет эту схему в каждом конкретном случае. Соответственно, возникают два вида entity-компонентов:
компонент в режиме CMP (Container-Managed Persistence), когда чтение данных из JDBC-БД и помещение их в экземпляры entity-компонентов и обратно выполняет контейнер EJB и
компонент в режиме BMP (Bean-Managed Persistence), когда код методов компонента содержит явные обращения к JDBC API (или к любому другому API) и контейнер просто посылает синхронизирующие сигналы, необходимые для приведения в соответствие состояния БД и связанных с ней экземпляров компонентов.
Гибкость объектно-реляционного отображения в режиме CMP существенно отличается для EJB версий 1.1 и 2.0. Хотя спецификация EJB 2.0 уже принята, программные продукты, реализующие эту технологию, появились только недавно. Например, EJB 2.0 позволяет (в отличие от EJB 1.1) учитывать наличие связей между таблицами с помощью удаленных ключей (foreign keys).
Следует также иметь в виду следующее очевидное обстоятельство: представление информации из БД в объектном виде – конкретно, в виде классов и интерфейсов Java – легко позволяет создать иерархическую структуру entity-компонентов c использованием наследования в классическом стиле ООП. Это позволяет не только эффективно развивать прикладную программу при изменении представления данных в БД, но и создать чисто логический уровень доступа к данным, причем часть бизнес логики системы может быть реализована с помощью EJB, а часть – каким-то другим образом. Впрочем, такой подход получил большое распространение применительно к EJB 1.1, в котором все обращения компонентов друг к другу были «удаленными» - с использованием RMI/IIOP – и, следовательно, весьма дорогостоящими с точки зрения затрат времени. В EJB 2.0 можно предусмотреть в дополнение к remote-интерфейсам (или вместо них) локальные высокоэффективные интерфейсы.
Еще одно интереснейшее новшество EJB 2.0 – с точки зрения объектного представления данных – это создание спецификации языка объектных запросов (QL, Query Language), используемого вместе с entity-компонентами в режиме CMP. QL позволяет формировать запросы, внешне очень похожие на SQL-операторы SELECT, но в которых фигурируют не реальные элементы из конкретных баз данных, а поля EJB-компонентов и дополнительная информация, необходимая для формализации связей между entity-компонентами (например, отношения между сущностями). Контейнер может «откомпилировать» такой оператор в эквивалентный ему SQL-оператор SELECT, а может поступить и каким-то другим образом.
Кэширование данных – необходимая и обычная ситуация при использовании серверов баз данных и любой более или менее универсальной технологии доступа к базам данных. В «обычной» двухзвенной системе (архитектуре «клиент-сервер») кэш создается и на стороне сервера, и (практически всегда) на стороне клиента. Переход к многозвенным системам логично ставит вопрос: не нужно ли перенести клиентский кэш на уровень сервера приложений или создать там дополнительный кэш – в добавление к клиентскому? Поскольку серверы приложений обычно запускаются в отдельных процессах (и в любом случае копируют данные в свои промежуточные структуры), появляются непроизводительные затраты, так как, по существу, время, затрачиваемое на копирование, удваивается. Чтобы снизить эти затраты, серверы приложений обычно кэшируют данные у себя.
Экземпляры Entity-компонентов и играют роль вспомогательного кэша. Нужно это или нет – решать программисту в каждом конкретном случае.
Важным для принятия решения о необходимости или желательности использования entity-компонентов является знание особенностей реализации выбранного сервера приложений. В общем случае лучше предполагать, что кэшированное объектное представление данных из БД доступно одному клиенту. Для разделяемого одновременного доступа к кэшированной информации со стороны нескольких клиентов entity-компоненты не предназначены (хотя такой режим работы возможно организовать для многих реализаций серверов приложений – например, в WebLogic). Любая качественная реализация сервера приложений содержит большое количество параметров настройки, которые влияют на производительность системы с использованием entity-компонентов. Знание этих параметров настройки и их грамотное использование – непременное условие создания эффективных систем.
Например, в Borland AppServer (новое название – Borland Enterprise Server) предусмотрены следующие параметры настройки (приведены, естественно, только наиболее важные):
ejb.transactionCommitMode – определяет режим взаимодействия entity-компонента с транзакциями. Возможные значения:
A (Exclusive). Означает, что данный entity-компонент имеет исключительный доступ к базе данных. Другими словами – состояние компонента в начале следующей транзакции совпадает с состоянием, полученным при завершении транзакции предыдущей. В этом режиме имеет смысл кешировать entity-компоненты – даже в случае выполнения многих транзакций.
B (Shared). Entity-компонент обеспечивает один из способов доступа к информации в БД. Состояние БД может быть изменено, например, при выполнении другим клиентом операций типа UPDATE. Предполагается, тем не менее, что значение ключевого поля (или ключевых полей) для используемой записи не меняется, и entity-компонент остается сопоставленным с этой записью в процессе работы (в том числе и при выполнении многих транзакций).
C (None). Режим похож на режим Shared, но entity-компонент сопоставляется с конкретной записью только на время выполнения одной транзакции.
ejb.maxBeansInCache – позволять задать максимальное число entity-компонентов, работающих в режиме ejb.transactionCommitMode=A, состояние которых хранится в кэше.
ejb.cmp.optimisticConcurrencyBehavior – определяет режим сохранения внесенных изменений для entity-компонентов, работающих в режиме CMP. Возможные значения:
UpdateAllFields. В этом режиме генерируется выражение UPDATE, в котором в операторе SET перечислены все поля, для которых в entity-компоненте предусмотрены их Java-эквиваленты – вне зависимости от того, были ли изменены их значения при работе с компонентом или нет.
UpdateModifiedFields. В генерируемое выражение UPDATE включаются только те поля, значения которых были модифицированы. Если в текущей транзакции состояние entity-компонента изменено не было, то выражение UPDATE выполняться просто не будет. Особенно полезен этот режим в случае, если entity-компонент обеспечивает доступ только для чтения данных.
VerifyModifiedFields и
VerifyAllFields. В Verify-режиме генерируются выражения UPDATE, для которых в оператор WHERE включаются проверки всех (или модифицированных) полей с целью предотвращения потерь изменений данных, внесенных параллельно работающими пользователями. Например (псевдокод ):
UPDATE set FIELD_VALUE = :NewValue WHERE FIELD_VALUE = :OldValue
где NewValue – новое значение (вносимое в БД), а OldValue – значение, считанное из этого поля в момент загрузки состояния из БД в экземпляр EJB-компонента.
Сигналы о необходимости выполнения синхронизации состояния кэша и БД вырабатываются контейнером EJB на основании информации о состоянии транзакций. Именно поэтому для entity-компонентов (в отличие от session-компонентов) запрещен режим ВMT – Bean Managed Transactions. Транзакциями для entity-компонентов всегда управляет контейнер.
Уже говорилось о схемах объектно-реляционного отображения, предлагаемого технологией EJB. Важнейшей особенностью такой схемы отображений является возможность задания ее в декларативном виде. Это означает, что программист формально задает логические соотношения между SQL-конструкциями и Java-классами. Реальное сопоставление выполняется не на стадии написания программы, а на стадии ее настройки для каждого конкретного заказчика – без перекомпиляции кода EJB-компонентов. Разумеется, речь идет главным образом для entity-компонентов в режиме CMP.
Гибкость настройки обеспечивается за счет двух основных решений:
описания схемы отображения на языке XML. Код entity-компонентов (в режиме CMP) не содержит никаких обращений к JDBC API.
обеспечение универсального доступа к источнику данных (в терминах JDBC) с помощью службы имен J2EE (JNDI). Это справедливо для любых entity-компонентов – как в режиме CMP, так и BMP. Более конкретно: технология EJB активно использует возможность регистрации в JNDI указателя на интерфейс javax.sql.DataSource (точнее, производных от него интерфейсов). Этот интерфейс позволяет получить (с помощью вызова метода getConnection()) логическое соединение с сервером баз данных.
Использование режима CMP приводит к тому, что программист может создавать entity-компоненты – как средство чтения и изменения информации в JDBC-БД – не зная ни JDBC API, ни того, какая конкретно база данных будет использована конкретным заказчиком. В коде компонента в явном виде присутствует только реализация бизнес-логики системы применительно к использованию информации из некоторой СУБД.
Универсальный доступ к данным (в описанном в предыдущем пункте стиле) тесно связан с управлением транзакциями. О различных режимах транзакций и взаимодействии мониторов транзакций с JDBC в рамках технологии J2EE рассказано в статье «Управление транзакциями в CORBA и EJB». Здесь достаточно сказать следующее: разработчику entity-компонента нет необходимости знать о том, как именно происходит управление транзакциями. Более того, разработчик компонента на стадии написания кода этого компонента даже не может знать, будут ли эти транзакции локальными или глобальными, будет ли процесс их завершения однофазным или двухфазным, используется ли его компонент в распределенных транзакциях или нет. За это отвечает т.н. deployer (в терминах технологии EJB) – т.е. специалист, который выполняет настройку готовой системы для конкретного заказчика. Deployer имеет дело не с Java-компонентами, а с их XML-дескрипторами.
Обеспечение конкурентного доступа к данным полностью берет на себя контейнер. Существует два подхода к решению этой задачи:
контейнер создает разделяемый (несколькими клиентами) экземпляр entity-компонента и обеспечивает разрешение возможных конфликтов (один из вариантов – выполнение сериализации, т.е. выстраивания в очередь, запросов, поступающих от разных клиентов);
контейнер создает несколько различных экземпляров каждого entity-компонента – по одному для каждого клиента, а разрешение конфликтов при попытке подтверждения транзакции возлагается на реально используемый сервер баз данных.
При использовании EJB-компонентов стандарта 1.1 вызов клиентом любого из методов как home-, так и remote-интерфейса, является дорогостоящим с точки зрения затрат времени. Это относится ко всем видам клиентов: не важно, кто вызывает бизнес-метод – клиентское приложение, апплет, сервлет или другой EJB-компонент. Тем не менее, даже в таком варианте существовала разница при выполнении действительно удаленного вызова (с передачей параметров по сети) и вызова «псевдоудаленного» – например, когда компонент-клиент и компонент-сервер расположены в одном контейнере и, следовательно, работают под управлением одной виртуальной машины Java.
Появление в EJB 2.0 локальных home- и component-интерфейсов еще более повысило важность разработки правильной архитектуры создаваемых систем, так как скорость выполнения удаленных вызовов и вызовов локальных различаются уже на порядки.
Для повышения производительности создаваемых систем применительно к использованию entity-компонентов желательно следовать нескольким рекомендациям:
бизнес-логика системы должна быть реализована session-, а не entity-компонентами. Entity-компоненты должны предоставлять методы для манипуляции данными. Подразумевается, что клиентом для entity-компонентов должны являться session-компоненты.
нужно стремиться к тому, чтобы session-компоненты, выступающие в качестве клиентов для entity-компонентов, располагались в том же EJB-контейнере, что и серверные entity-компоненты, а home- и component-интерфейсы серверных entity-компонентов должны быть локальными.
Нужно стремиться к тому, чтобы «сворачивать» (по возможности) выполнение более или менее сложных действий в единственный удаленный вызов метода session-компонента, в коде которого выполняются (уже локальные) вызовы нескольких методов нескольких entity-компонентов. Хорошим и наглядным, хотя и надоевшим, является пример с перечислением некоторой суммы со счета на счет. Для объектного представления каждого из счетов используется отдельный entity-компонент. Эффективной схемой выполнения перевода суммы со счета на счет является такая:
Создается (как тип данных) session-компонент с бизнес-методом transfer(), который в качестве аргументов получает номера счетов, участвующих в операции, и величину переводимой суммы;
Создается (как тип данных) entity-компонент, в котором предусмотрен метод add_value() или даже два метода – increment() (без проверки корректности операции) и decrement() – с проверкой допустимости списания указанной суммы со счета. Эти методы объявлены в локальном component-интерфейсе.
Клиент вызывает метод create(),. Вызов этого метода возвращает указатель на component-интерфейс созданного session-компонента. Это первый удаленный вызов.
Клиент вызывает (по полученному component-интерфейсу) бизнес-метод transfer(). Это второй удаленный вызов.
При использовании технологии EJB 2.0 можно обойтись даже одним удаленным вызовом, если это вызов «статического» метода из home-интерфейса нашего session-компонента.
В коде бизнес-метода transfer() выполняется поиск первого нужного счета – с помощью вызова локального метода find(), второго (аналогичным образом), а затем также локальные вызовы метода decrement() для первого счета и increment() – для второго. Возможен альтернативый вариант: вместо двух вызовов методов find(), каждый из которых эквивалентен отдельному SQL-оператору SELECT, можно сформировать один «SELECT»-оператор языка QL, сформировав должным образом его WHERE-часть.
Таким образом, при выполнении большого количества обращений к разным методам трех EJB-компонентов, только одно-два обращения являются удаленными.
следует (по возможности) избегать явного начала транзакции клиентом. В рассмотренном примере транзакцию для достижения более высокой производительности должен начинать session-компонент в момент вызова клиентом бизнес-метода transfer(), а не сам клиент. Связано это с тем, что реализации контейнера EJB (вместе с реализацией сервиса транзакций) гораздо проще начать новую транзакцию, чем анализировать полученный от клиента контекст транзакции и выполнять другие необходимые действия. Всегда следует иметь в виду, что в общем случае «общение» EJB-контейнера и менеджера транзакций реализовано с помощью выполнения дорогостоящих удаленных вызовов, и чем меньше будет таких вызовов, тем лучше. Из приведенных ниже данных вы увидите, что правильное управление транзакциями существенно повышает производительность системы в целом.
Естественно, возникает вопрос: когда разумно использовать BMP, а когда – CMP-режим работы entity-компонентов?
Надо понимать, что не всегда этот выбор возможен. Например, вы хотите использовать QL EJB 2.0. Это означает, что вы должны работать в режиме CMP. С другой стороны, если вы хотите работать с БД, для которой просто нет JDBC-интерфейса (но есть другой Java API), то безальтернативным вариантом становится выбор режима BMP.
Но в большинстве случаев возможность выбора есть. Как быть?
В общем случае рекомендуется по возможности использовать режим CMP из-за двух его преимуществ:
создание логической схемы отображения полей БД на Java-переменные повышает универсальность программы, простоту и гибкость ее настройки в интересах конкретного клиента;
серверы приложений могут оптимизировать некоторые действия в режиме CMP, что позволяет достичь несколько большей производительности по сравнению с режимом BMP. Простейшие тесты для Borland AppServer показали, что операции INSERT и DELETE в режиме BMP и CMP выполняются за одинаковое время, зато SELECT для CMP-режима выполнялся на 20%, а UPDATE – на 10% быстрее.
В качестве примера типичного кода, который может находиться в методах entity-компонента, работающего в режиме CMP, приведем код, который генерируется мастером Borland JBuilder:
public void ejbLoad() throws RemoteException { mkey = ((Integer) entityContext.getPrimaryKey()).intValue(); Connection connection = null; PreparedStatement statement = null; try { connection = dataSource.getConnection(); statement = connection.prepareStatement ("SELECT STRING_FIELD FROM MASTERT_EJB WHERE MKEY = ?"); statement.setInt(1, mkey); ResultSet resultSet = statement.executeQuery(); if (!resultSet.next()) { throw new NoSuchEntityException("Row does not exist"); } this.stringField = resultSet.getString(1); } catch(SQLException e) { throw new EJBException ("Error executing SQL SELECT STRING_FIELD FROM MASTERT_EJB WHERE MKEY = ?: " + e.toString()); } finally { closeConnection(connection, statement); } : } public void ejbLoad() throws RemoteException { mkey = ((Integer) entityContext.getPrimaryKey()).intValue(); Connection connection = null; PreparedStatement statement = null; try { connection = dataSource.getConnection(); statement = connection.prepareStatement ("SELECT STRING_FIELD FROM MASTERT_EJB WHERE MKEY = ?"); statement.setInt(1, mkey); ResultSet resultSet = statement.executeQuery(); if (!resultSet.next()) { throw new NoSuchEntityException("Row does not exist"); } this.stringField = resultSet.getString(1); } catch(SQLException e) { throw new EJBException ("Error executing SQL SELECT STRING_FIELD FROM MASTERT_EJB WHERE MKEY = ?: " + e.toString()); } finally { closeConnection(connection, statement); } ... }
Как видно, при вызове метода происходит получение значения ключевого поля с помощью стандартного метода getPrimaryKey(), затем компонент получает логическое соединение с БД из пула соединений, а затем выполняются классические обращения к JDBC API с проверкой корректности завершения и возбуждением (если необходимо) соответствующих исключений. Ясно, что ни одно из этих выполняемых действий не является излишним.
В этом разделе будет проведено некоторое сравнение использования entity-компонентов и классического JDBC API для решения одинаковых задач. Надо сразу предупредить читателя, что сравнение не совсем корректное – entity-компоненты здесь будут выступать в роли «мальчиков для битья». Дело в том, что entity-компоненты обеспечивают явно избыточные возможности – по сравнению с теми задачами, которые решаются в тестовых примерах. В тестах не используется ни одна из «сильных» сторон entity-компонентов: кеширование данных в оперативной памяти между транзакциями, моделирование соотношений «один-ко-многим», «многие-к-одному» и других, гибкость настройки на различные базы данных.
Правильнее даже говорить не о сравнении этих способов – entity-компоненты (явно – в режиме BMP – или неявно – в режиме CMP) выполняют те же JDBC-операторы, которые присутствуют в варианте «чистого» JDBC. Таким образом, в тестах производится оценка того, сколько времени занимают дополнительные действия, выполняемые контейнером или entity-компонентом.
Впрочем, одно преимущество entity-компонентов все-таки демонстрируется. Это размер кода, который надо написать программисту. Во многих случаях это обстоятельство может иметь важное значение.
При тестировании в качестве инструмента использовались JBuilder 5 и Borland AppServer 4.5.1. Клиентские приложения во всех случаях запускались из среды JBuilder. Тестировались как «локальный» (клиент и сервер баз данных/сервер приложений находятся на одном компьютере), так и «удаленный» вариант. Borland Appserver и Interbase всегда работали на одном и том же компьютере...
Copyright © 1994-2016 ООО "К-Пресс"