EJB Advocate: Создание производительных EJB-компонентов управления данными

Использование некорректно спроектированных EJB™-компонентов может привести к серьезным проблемам производительности при тестировании системы или (что еще хуже) во время эксплуатации. EJB Advocate показывает, как разработать сигнатуры методов для минимизации "болтливости" между уровнями и получения наибольшей пользы от EJB-компонентов.

Джефф Хэмбрик, инженер, IBM

Джефф Хэмбрик (Geoff Hambrick) является ведущим консультантом IBM Software Services для группы обеспечения WebSphere (WebSphere Enablement Team). Он живет в Раунд Рок, Техас (недалеко от Остина). Общей задачей группы обеспечения является поддержка предпродажных процессов, которая осуществляется с помощью подробного технического инструктажа и кратковременных совещаний по проверке концепций. За свою работу по созданию и распространению передового опыта для разработки приложений J2EE на базе IBM WebSphere Application Server в марте 2004 года Джефф был удостоен звания инженера с отличием IBM (IBM Distinguished Engineer).



28.02.2008

Из IBM WebSphere Developer Technical Journal.

В каждой рубрике EJB Advocate приводится суть типичного диалога с реальными пользователями и разработчиками в процессе предоставления рекомендаций по решению какой-либо интересной проблемы. Идентификационные детали участников диалога не сообщаются, а новаторские или проприетарные архитектуры не применяются. Более подробная информация приведена в статье "Знакомство с EJB Advocate".

Проблема

Уважаемый EJB Advocate!

Моим первым J2EE-проектом было создание Web-приложения, визуализирующее товары из каталога нашей компании, для того чтобы клиенты могли добавить их в свою корзину покупок. Я был большим сторонником объектной технологии и идеи EJB-компонентов управления данными, пока не приступил к тестированию прототипа приложения на моем лэптопе с использованием интегрированной среды тестирования IBM® WebSphere® Studio Application Developer. Короче говоря, сервлет загружал локальные EJB-компоненты, формирующие страницу, более пяти минут. Когда я переделал его так, чтобы он использовал JDBC, эта же работа выполнялась менее одной секунды.

Очень не хочу так говорить, но теперь я
No Longer a Fan (Больше не фанат)

В общем-то, сразу было ясно, что в данном случае архитектура EJB-компонентов управления данными имеет изъяны, потому что сравнение хорошо спроектированных CMP EJB-компонентов управления данными с закодированными вручную JDBC-компонентами никогда не показывало такой разницы в производительности. Однако были веские причины сначала сосредоточиться в ответе на раннем тестировании. Идея заключалась в том, чтобы подсказать дополнительный тест, который могли бы выполнить разработчики, и, тем самым, помочь им найти решение проблемы самостоятельно.

Уважаемый No Longer a Fan!

Это хорошо, что Вы использовали локальные, а не удаленные EJB-компоненты управления данными, то есть производительность не пострадала от активизаций удаленных методов. Накладные расходы подобного рода легко могут удвоить время загрузки страницы. Одна из наилучших практических рекомендаций по использованию EJB-компонентов управления данными - всегда применять локальные компоненты. Мы кратко рассмотрели такой подход в статье, вышедшей в прошлом месяце.

Также было отрадно услышать, что Вы сразу же создали прототип для тестирования производительности локальных EJB-компонентов управления данными и сравнения с уровнем персистенции, непосредственно закодированным с использованием JDBC. Многие разработчики не проводят тестирования производительности архитектур, которые предполагают использовать, до создания всего приложения и в конечном итоге сталкиваются с большим объемом повторной работы и, возможно, критической ситуацией в процессе эксплуатации. Другие разработчики пытаются скрыть детали механизма персистенции за "компонентом доступа" для облегчения последующего перехода, добавляя еще один уровень в систему (JDBC и EJB-компоненты управления данными уже являются абстракциями механизма персистенции).

Думаю, что знаю, в чем проблема, но хотел бы, чтобы Вы включили трассировку JDBC-источника данных и посмотрели, сколько выполняется SQL-запросов (см. WebSphere Application Server Information Center). Вы можете также включить EJB-трассировку, которую, как указал Мэтт Оберлин (Matt Oberlin) в своей статье "Встреча с экспертами", довольно легко выполнить в WebSphere Studio Application Developer (см. рисунок 1).

Рисунок 1. Включение EJB-трассировки в WebSphere Studio Application Developer
Рисунок 1. Включение EJB-трассировки в WebSphere Studio Application Developer

Далее, как указал Стэйси Джойнес (Stacy Joines) в своей отличной книге по производительности WebSphere, для поиска и исправления узких мест действительно важным является сбор точных показателей производительности. Причина, по которой я прошу Вас выполнить более точные измерения, заключается в том, что возможно Вы увидите намного больше SQL-запросов для компонента управления данными, чем для случая с JDBC, и это влияет на разницу в производительности. Фактически, я предполагаю, что Вы увидите один SQL-запрос, выполняемый для каждого атрибута, прочитанного из каждого логического объекта product, плюс один!

Сообщите мне, что Вы обнаружили.

Ваш EJB Advocate


Хорошо, плохо и ужасно

Уважаемый EJB Advocate!

Как вы догадались? Я отображаю до десяти товаров на странице с пятью атрибутами каждый (единица учета, описание, цена, ссылка на изображение и дата, когда товар станет доступен). Я обнаружил пятьдесят один SQL-запрос в случае с EJB-компонентом и всего один SQL-запрос в случае с прямым JDBC! Не удивительно, что EJB-компоненты управления данными и близко не подходят по производительности. Скорее всего, я сделал правильный выбор, решив использовать JDBC.

Все еще
No Longer a Fan (Больше не фанат)

Я надеялся, что данные SQL-трассировки сделают очевидной проблему, и No Longer a Fan больше не будет основываться на неточных измерениях секундомером, а попытается найти причину различий или проблемы, прежде чем принять решение. Помочь кому-либо преодолеть крах иллюзий сложнее, чем я думал! Вот каков был мой ответ:

Уважаемый No Longer!

Я полагаю, что Вы активизируете компоненты управления данными вне области действия глобальной транзакции, что вызывает выполнение каждого вызова в отдельной транзакции и приводит к генерированию своего собственного SQL-запроса (в зависимости от Ваших настроек развертывания). А именно, будет генерироваться один вызов home для обнаружителя (finder), возвращающего следующие десять товаров, и пять вызовов ассоциированных с отображаемыми атрибутами методов get() каждого логического объекта. Вызов логических объектов вне глобальной транзакции определенно является "наихудшей" методикой (чаще называемой антишаблоном). Фактически, избегать вызова объектов вне транзакции настолько важно, что некоторые EJB-разработчики предлагают (как и EJB Advocate) объявлять транзакции "mandatory" (обязательными) в дескрипторе развертывания EJB: использовать <trans-attribute>Mandatory</trans-attribute> в теге <container-transaction>. Это объявление вызовет генерирование исключительной ситуации, если до обращения к компоненту управления данными транзакция уже не была инициализирована.

Есть два способа заключить логику, которая возможно вызывает EJB-компоненты управления данными, в глобальную транзакцию и значительно повысить производительность. Один представляет собой "простой способ", а второй - "правильный способ".

Простым способом является явное добавление в сервлет кода для начала и окончания глобальной транзакции вокруг вызовов EJB-компонентов, например:

...
import javax.transaction.*;
...
public class YourServlet implements HttpServlet {

    private InitialContext initCtx = new InitialContext();
    public void doGet (
		HttpServletRequest req, HttpServletResponse
    ){
		UserTransaction userTran =
		 (UserTransaction)initCtx.lookup(
	           "java:comp/UserTransaction"
		);
        
		userTran.begin();

		//Использовать логический объект для загрузки данных
		...
		userTran.commit();
    }
    ...
}

Некоторые разработчики идут немного дальше и создают сервлет суперкласса для обработки такого поведения, используя методику, называемую наследованием шаблонов (template inheritance). Суперкласс должен объявляться как abstract. Его метод doGet() должен быть объявлен как final и вызывать вниз по цепочке абстрактный метод doGetYourParent(), реализованный сервлетом YourServlet (который наследует это поведение). Код родительского класса может выглядеть примерно так:

...
import javax.transaction.*;
...
public abstract class YourParentServlet implements HttpServlet {

    private InitialContext initCtx = new InitialContext();
    public final void doGet (
		HttpServletRequest req, HttpServletResponse
    ){
		UserTransaction userTran =
		 (UserTransaction)initCtx.lookup(
	           "java:comp/UserTransaction"
		);
        
		userTran.begin();
		doGetYourParent();
		userTran.commit();
    }

    protected abstract void doGetYourParent(
		HttpServletRequest req, HttpServletResponse
    );
	    ...
}

Изменения в сервлетах суперклассов, необходимые для использования наследования шаблонов, довольно просты:

  1. Измените выражение implements класса YourServlet с HttpServlet на YourParentServlet, и
  2. Измените его методы doGet(), doPost() и другие методы HttpServlet на do<Method>YourParent().

Одним из главных преимуществ подхода наследования шаблонов является облегчение согласованного и прозрачного добавления качественных характеристик сервиса, таких как начало и завершение транзакции, проверка кэша, обработка ошибок и другое поведение, которое Ваша команда, вероятно, собирается включить, чтобы гарантировать устойчивую работу.

Независимо от подхода к началу глобальной транзакции, Вы должны заметить значительное снижение количества SQL-запросов при трассировке (в зависимости от access intents (целей обращения к данным) и других настроек развертывания; информация по настройке таких атрибутов как Collection Increment (десять в Вашем случае) приведена в WebSphere Application Server Information Center).

Но даже если Вы сделали эти изменения и исключили все места, где CMP вызывались вне области действия глобальной транзакции, инструментальная программа анализа нагрузки, измеряющая производительность системы в условиях, близких к условиям эксплуатации (например, IBM Rational® Performance Tester), все равно будет показывать существенное различие между пропускной способностью и коэффициентом использования CPU кодом JDBC и кодом EJB-компонента управления данными, даже если программа профилирования (такая как JInsight) и программа анализа путей (такая как IBM Tivoli® Monitoring for Transaction Performance) не показывают отличий.

"Правильный способ" - исправить исходный код, зависящий от деталей вашего дизайна. Возможно, Вы уже довольно близко, поэтому позвольте задать вопрос: используете ли Вы JavaServer™ Page для визуализации страницы из "объекта передачи данных" (POJO с методами только get/set), загруженного сервлетом (наилучшая методика использования J2EE)? Или сервлет визуализирует HTML-ответ напрямую?

Ваш EJB Advocate


Скройте ужасное за специальным фасадом

Уважаемый EJB Advocate!

Вы правы, я использовал JSP для визуализации страницы, следуя подходу Model 2. Другими словами, сервлет загружает массив из десяти объектов "ProductView" (таких же, как Ваш объект передачи данных, за исключением того, что они являются Serializable), и затем вызывает JSP. Чтобы было понятно, вот соответствующий исходный код в сервлете, написанный в стиле, который Вы предложили в предыдущем ответе:

...
import javax.transaction.*;
...
public class YourServlet implements HttpServlet {

    private InitialContext initCtx = new InitialContext();
    public void doGet (
		HttpServletRequest req, HttpServletResponse
    ){
		UserTransaction userTran =
		 (UserTransaction)initCtx.lookup(
	           "java:comp/UserTransaction"
		);
        
		userTran.begin();
		ProductLocalHome home =
		 (ProductLocalHome)initCtx.lookup(
	           "java:comp/env/ProductLocal"
		);
        
		Collection products = home.findNextNFrom(last, count);
		int size = 0;
		ProductView[] results = new ProductView[products.size];
		Iterator i = products.iterator();
		ProductView product = null;
		while (i.hasNext()) {
			ProductLocal product = (ProductLocal)i.next();
			ProductView result = new ProductView();
			// Каждая из этих строк вызывала транзакцию и SQL
			result.setSku(product.getSku());
			result.setDesc(product.getDesc());
			result.setPrice(product.getPrice());
			result.setImage(product.getImage());
			result.setData(product.getDate());
			results[size++] = result;
		}
		// Установить в HttpServletRequest
		req.setAttribute("ProductView", results);

		// Активизировать JSP ProductView (здесь не интересно)
         ...
		userTran.commit();

    }
    ...
}

Кстати, JSP использует специальный тег для навигации по массиву объектов ProductView (в действительности этот тег выполняет навигацию по массиву любого типа объектов, симулирующих "тег bean"), из которых для замещения свойств могут использоваться "теги bean property". Я надеюсь, что говорю достаточно подробно.

Я был рад обнаружить, что код "простого способа" действительно приблизил производительность EJB-компонентов управления данными к производительности JDBC (по данным измерения JInsight). Я также использовал "обязательный" CMT-атрибут, который заботился о том, чтобы все вызовы к CMP происходили вне транзакции. Однако при непосредственном сравнении использование JDBC все равно значительно лучше (в случае использования нашего инструментального средства тестирования нагрузки - сейчас мы используем LoadRunner, но обратим внимание и на Rational Performance Tester, упомянутый Вами).

Спасибо, но все еще
No Longer a Fan (Больше не фанат)

На этот раз я получил больше, чем ожидал. No Longer a Fan предоставил примеры исходного кода, которые намного более точны, чем описания или блок-схемы. И, по-видимому, он воспользовался рекомендацией применять более точные средства измерения системной производительности, а также применил инструментальные средства анализа путей и нагрузок.

Если бы No Longer a Fan смешал код визуализации HTML (представление) с кодом извлечения данных, я вынужден был бы покопаться в наилучших методиках использования сервлетов (и это не преувеличение, поскольку я также защищаю все, что связано с J2EE). Я должен был бы объяснить наилучшую методику использования объектов передачи данных для потока данных от сервлета к JSP. Исходный код, который я включил в ответ ниже, не выглядел бы таким знакомым для No Longer a Fan.

Уважаемый No Longer!

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

Это хорошо, что Вы следовали наилучшим методикам Модели 2 и сделали дальнейший шаг для предоставления специализированного тега навигации для массивов. Также здорово, что Вы уже используете объекты передачи данных, в основном такие же, как и Service Data Objects. Наличие такой архитектуры делает "правильный способ" способным получить глобальную транзакцию также и "простым способом".

Иными словами, Вы можете создать сессионный фасад EJB для инкапсулирования логики, связанной со сбором данных для страницы (массив объектов ProductView). Этот шаблон рассматривается (среди прочего) в статье прошлого месяца, а также в книге Кайла Брауна (см. раздел "Ресурсы"). Фасад сессии может выглядеть примерно так:

	 extends SessionBean {

    public ProductView[] getCatalog (
		ProductKey lastKey, int count
    ){
		ProductLocalHome home =
		 (ProductLocalHome)initCtx.lookup(
	           "java:comp/env/ProductLocal"
		);
        
		// Этот вызов генерировал транзакцию и SQL
		Collection products = home.findNextNFrom(last, count);
		int size = 0;
		ProductView[] results = new ProductView[products.size];
		Iterator i = products.iterator();
		ProductView product = null;
		while (i.hasNext()) {
			ProductLocal product = (ProductLocal)i.next();
			ProductView result = new ProductView();
			// Каждая из этих строк вызывала транзакцию и SQL
			result.setSku(product.getSku());
			result.setDesc(product.getDesc());
			result.setPrice(product.getPrice());
			result.setImage(product.getImage());
			result.setData(product.getDate());
			results[size++] = result;
		}
		// Вместо установки в HttpServletRequest
		return results;
    }
    ...
}

Как можно заметить, в приведенном выше примере преимущество использования архитектуры Модели 2 с объектами передачи данных заключается в том, что основная часть логики в методе doGet() сервлета просто перемещается в метод getCatalog() фасада сессии. Главное преимущество такого перемещения - логику получения следующей страницы товаров теперь можно использовать вне контекста сервлета (как из управляемого сообщениями компонента или другого EJB-компонента). Может также предоставляться удаленный интерфейс (автоматически генерируемый инструментальными средствами в WebSphere Studio Application Developer), делающий его доступным из J2EE-клиента. Использование объекта передачи данных минимизирует "болтливость" между уровнями - нужен только один вызов без сохранения состояния. В любом случае сервлет больше не должен иметь дело с транзакциями. Это выглядит примерно так:

...
public class YourServlet implements HttpServlet {

    private InitialContext initCtx = new InitialContext();
    public void doGet (
		HttpServletRequest req, HttpServletResponse
    ){
		MySessionLocalHome home =
		 (MySessionLocalHome)initCtx.lookup(
	           "java:comp/env/MySession"
		);
        
		// Получить последнее ключевое поле, как раньше
		ProductView[] home.create().getProductView(last, 10);

		//Загрузить массив в HttpServletRequest и активизировать JSP
    	...
	
    }
    ...
}

Я знаю, что все выглядит так, будто данная практическая методика использования меняет код начала и окончания транзакции на код активизации EJB-метода, но имеется ряд дополнительных преимуществ, кроме повторного использования логики в других ситуациях. Во-первых, ссылка на локальную сессию может кэшироваться в методе init() сервлета для устранения поиска в doGet(). Во-вторых, и это более важно, обработка транзакций может быть очень сложной, особенно при учете исключительных ситуаций. Некорректная обработка может привести к "утечкам", которые вызовут свой тип проблем производительности. Короче говоря, еще одной наилучшей практической методикой является использование управляемых контейнером транзакций везде, где это возможно.

Этот код "правильного способа" по существу будет выполняться также быстро, как код "простого способа" (с учетом использования интерфейса локальной сессии). Но проблема все равно останется, общая производительность использования локального логического объекта будет значительно хуже, чем при использовании JDBC (теперь вне сессионного EJB-компонента, который эффективно инкапсулирует модель из представления). Причина этого состоит в том, что хотя методы get<Property>() логического объекта являются локальными, все равно имеются значительные накладные расходы на проверку для системы защиты и транзакций. По некоторым оценкам эти накладные расходы составляют около десяти тысяч инструкций, что для данного примера будет незначительным при измерении программой анализа пути: 50 x 10000 = 500000. Но что если "счетчик" был равен 100, и число свойств, к которым осуществляется доступ, было равно 100. Общее число инструкций составит 100 миллионов, что уже является ощутимой разницей. Этот феномен, связанный с "масштабированием", показывает, почему тестирование нагрузки является наилучшим методом поиска реальных отличий в производительности. Тестирование пути позволяет обнаружить вероятного виновника, которого можно определить при помощи статического анализа (просмотра исходного кода). В данном случае количество инструкций, нужное для доступа к свойству объекта передачи данных, оценивается десятками, а не десятками тысяч - порядок величины лучше, чем даже при доступе к локальному EJB-компоненту в глобальной транзакции.

Ключом оптимальной производительности EJB-компонентов является использование объектов передачи данных и специальных методов для создания, получения и установки всех необходимых свойств для данного варианта использования в одном вызове. Это минимизирует "болтливость" между фасадом сессии и EJB-компонентом управления данными. Обычно можно спроектировать EJB-компоненты управления данными с правильным набором методов так, чтобы пользователю нужно было выполнить только один вызов после поиска - метод create, retrieve, update или delete. В следующем коде показано, как мог бы выглядеть EJB-компонент управления данными со специализированным get:

public abstract ProductBean extends EntityBean {

    // Здесь свойства, необходимые для специализированного метода
    // ТЕПЕРЬ БОЛЬШЕ НЕ В ИНТЕРФЕЙСЕ ДЛЯ ПРЕДОТВРАЩЕНИЯ ИНДИВИДУАЛЬНОГО ДОСТУПА 
    public abstract ProductKey getSku() ;
    public abstract void setSku(ProductKey value);

    public abstract String getDesc() ;
    public abstract void setDesc(String value);

    public abstract BigDecimal getPrice() ;
    public abstract void setPrice(BigDecimal value);

    public abstract String getImage() ;
    public abstract void setImage(String value);

    public abstract Date getDate () ;
    public abstract void setDate(Date value);

    // Возможно, больше свойств, чем указано выше

    // Здесь специализированный метод get 
    public ProductView getProductView (
    ){
		ProductView result = new ProductView();
		// Теперь нельзя вызвать транзакцию и SQL
		result.setSku(getSku());
		result.setDesc(getDesc());
		result.setPrice(getPrice());
		result.setImage(getImage());
		result.setDate(getDate());
		return result;
    }
    ...
}

Для навязывания использования специализированных методов многие EJB-разработчики в интерфейсе отображают только их, например:

public interface Product extends EJBLocalObject {
    public ProductView getProductView ();
    public void setProductView(ProductView value);
}

Метод home будет иметь специализированные create и find:

public interface ProductHome extends EJBLocalHome {
    public Product create(ProductView value);
    public Collection findNextNFrom(ProductKey last, int count);
}

Возможно, Вы заметили, что я не использую "Local" как часть имени интерфейса EJB-компонента управления данными (для home или для компонента). Поскольку я никогда не отображаю удаленный интерфейс к EJB-компоненту управления данными, увеличение длины имени класса выглядит излишеством.

В любом случае при использовании этих специализированных методов фасад сессии изменился бы следующим образом:

public class YourSession extends SessionBean {

    public ProductView[] getCatalog (
		ProductKey lastKey, int count
    ){
		ProductHome home =
		 (ProductHome)initCtx.lookup(
	           "java:comp/env/ProductLocal"
		);
        
		// Этот вызов вызывал транзакцию и SQL
		Collection products = home.findNextNFrom(last, count);
		int size = 0;
		ProductView[] results = new ProductView[products.size];
		Iterator i = products.iterator();
		ProductView product = null;
		while (i.hasNext()) {
			// Выполнить next с итератором по логическим объектам для поиска 
			Product product = (Product)i.next();

			// Теперь только один вызов после поиска, что является идеальным
			results[size++] = product.getProductView();
		}
		// Вместо установки в HttpServletRequest
		return results;
    }
    ...
}

Обратите внимание на то, что мы просто переместили код, который находился в цикле метода getCatalog() сессионного EJB-компонента, в метод getProductView() EJB-компонента управления данными; затем мы заменили перемещенный код вызовом одного метода. Если Вы протестируете нагрузку для кода, реализованного таким способом, то, вероятно, заметите значительно более разумные накладные расходы использования EJB-компонентов управления данными по сравнению с JDBC. Учитывая, что эти EJB-компоненты управления данными значительно легче обслуживать, чем JDBC, компромисс будет того стоить.

И просто на будущее - вот UML-диаграмма общей архитектуры. На ней показаны взаимосвязи между JSP-страницами и сервлетами, сессионными компонентами и EJB-компонентами управления данными, объектами передачи данных data key и data view:

Рисунок 2. UML-диаграмма общей архитектуры
Рисунок 2. UML-диаграмма общей архитектуры

Я надеюсь, что данная дискуссия возродит Ваш энтузиазм, касающийся EJB-компонентов управления данными. По крайней мере, я надеюсь, что Вы познакомились с некоторыми новыми инструментальными программами и методиками точного измерения отличий в производительности и оценки компромиссов.

Ваш EJB Advocate


Заключение

В данном обмене мнениями мы увидели, почему использование фасада сессии перед локальным EJB-компонентом управления данными так важно для гарантирования выполнения только одной транзакции на UI-событие, а также для минимизации количества SQL-запросов, передаваемых в базу данных. Также существенно, что мы увидели важность использования объекта передачи данных (теперь называемого SDO) и специализированных методов EJB-компонентов управления данными для минимизации "болтливости" между уровнями даже при использовании локального интерфейса. И, между прочим, мы увидели, что использование SDO позволяет перемещать данные по всему пути от логического объекта к JavaServer Page, визуализирущей HTML, передавая через фасад сессии и сервлет (контроллеры model и view соответственно).

Мы рассмотрели, как можно использовать наследование шаблонов, чтобы добавлять поведение (например, для начала и подтверждения глобальной транзакции) прозрачно для кода сервлета. Хотя использование фасада сессии минимизирует потребность в данном подходе, наследование шаблонов в сервлетах все равно может быть полезно в ситуациях, когда вызывается более одного сессионного компонента в контексте doGet() или doPost().

Мы также рассмотрели объявление транзакций обязательными в EJB-компонентах управления данными, а также не отображение индивидуальных CMP-атрибутов в интерфейсе логического объекта. Обе этих методики помогут вам навязать свои политики использования.

Одна рассмотренная наилучшая методика работы (не проектирования) была связана с измерением производительности, используя инструментальные средства анализа пути и нагрузки, затем анализ кода и конфигурации для поиска и исправления узких мест. Идея заключается в применении таких программ как Rational Performance Tester, Tivoli Monitoring for Transaction Performance и JInsight, которые перехватывают количество вызовов, объем передаваемых данных и время, затраченное на каждый вызов. Также мы порекомендовали, что статический анализ должен основываться на исходном коде, а не на высокоуровневых диаграммах классов или диаграммах последовательностей (хотя они полезны для получения общего представления).

Что дальше...

Если вы интересуетесь проблемой, связанной с использованием EJB-компонентов любого типа, не стесняйтесь связаться с EJB Advocate. В противном случае в следующей статье мы исследуем Service Data Objects и рассмотрим, какую роль играют EJB-компоненты (сессионные и компоненты управления данными) в сервис-ориентированной архитектуре.

Ресурсы

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=WebSphere, Технология Java
ArticleID=292184
ArticleTitle=EJB Advocate: Создание производительных EJB-компонентов управления данными
publish-date=02282008