Продолжается подписка на наши издания! Вы не забыли подписаться?

Управление транзакциями в CORBA и EJB**

Александр Цимбал

Управление транзакциями – один из важнейших аспектов при создании надежных распределенных систем, хотя в различных технологиях создания таких систем степень универсальности управления транзакциями может существенно варьироваться. Примером вполне жизнеспособного, но все же упрощенного решения может служить схема управления транзакциями в COM+ – за счет предположения о том, что COM-объекты не имеют состояния и создаются заново для каждой новой транзакции.

Схема управления транзакциями в CORBA носит универсальный характер в силу универсального характера самой CORBA.

Что вообще понимается под транзакцией в распределенной системе?

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

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

Возникают следующие проблемы:

Попытки создания универсальной модели управления транзакциями предпринимались неоднократно. Наибольший успех выпал на долю модели Distributed Transaction Proccessing Model (DTP, иногда используется аббревиатура DTM) консорциума X/Open (в настоящий момент – Open Group).

Модель DTP

Здесь будет рассмотрен упрощенный вариант DTP (исключая так называемые Менеджеры Коммуникационных Ресурсов – Communication Resource Managers, CRM). Согласно этой модели, участниками транзакционной распределенной системы являются:

Эти три участника должны, естественно, «общаться» между собой в процессе функционирования системы. Для формализации такого взаимодействия DTP определяет два стандартных протокола:

Протокол взаимодействия прикладной программы и Менеджера Ресурсов не определен в модели DTP. Если вашей программе нужно напрямую обращаться к RM, это можно сделать произвольным образом (например, через SQL).

Схематически взаимодействие участников можно показать следующим образом:

Сервис транзакций CORBA (OTS)

Сервис транзакций CORBA (Object Transaction Service, OTS) создан в соответствие с моделью DTP. Конечно, были введены термины, более или менее специфические для OTS. Начнем с них.

Основные понятия OTS

Контекст транзакции (transaction context). Контекст транзакции в OTS представляет собой структуру данных, объявленную на языке IDL. Эта структура совместима с так называемым XID – идентификатором транзакции в модели DTP. Помимо уникального идентификатора конкретной транзакции, представляющего собой массив байтов произвольной длины, контекст транзакции предусматривает сохранение информации о транзакциях, дочерних для данной транзакции, так как OTS поддерживает вложенные транзакции. Важно понимать следующее:

Инициатор транзакции (transaction originator). Инициатором транзакции является программа, которая начинает транзакцию с помощью вызова метода tx_begin() – если говорить в терминах протокола TX. Этот метод (в OTS он имеет имя begin()) вызывается для Менеджера Транзакций, точнее, для одного из стандартных интерфейсов OTS. Как правило, инициатор транзакций должен ее и завершить, хотя OTS в принципе предоставляет возможность завершить транзакцию и другому ее участнику – не обязательно инициатору.

Транзакционный объект (transactional object). Транзакционным называется любой объект, участвующий в транзакции, т.е. объект, получающий тем или иным способом доступ к контексту текущей транзакции. Это не значит, что транзакционный объект обязательно имеет состояние или это состояние должно изменяться в соответствии с классической логикой выполнения транзакций. Довольно часто транзакционному объекту достаточно получить текущий статус транзакции (например, начата ли она вообще) или пометить транзакцию, как подлежащую обязательному откату.

Транзакционный сервер (transactional server). Транзакционным сервером называется любой сервер приложений (в любом виде), который содержит хотя бы один транзакционный объект.

Восстанавливаемый объект (recoverable object). Восстанавливаемый объект – это, конечно же, транзакционный объект, но при этом его состояние должно изменяться в соответствие с логикой выполнения транзакции (изменения состояния фиксируются или отменяются при завершении транзакции). Важнейшей особенностью поведения восстанавливаемого объекта явялется то, что он обязательно должен «оповестить» о своем существовании Менеджер Транзакций, и не вообще, а применительно к текущей транзакции. Для это предусмотрен метод ax_reg() протокола XA. Обычно при сохранении состояния такого объекта оно помещается в файл, базу данных или другой вид долговременного хранилища.

Восстанавливаемый сервер (recoverable server). Восстанавливаемый сервер – это транзакционный сервер, содержащий хотя бы один восстанавливаемый объект.

Ресурсный объект (resource object). Ресурсный объект тесно связан с восстанавливаемым объектом. Строго говоря, восстанавливаемый объект регистрирует у Менеджера Транзакций (с помощью вызова метода ax_reg()) не себя, а ресурсный объект. Задачей ресурсного объекта является реализация callback-методов (который являются частью интерфейса XA), которые будут вызваны Менеджером Транзакций в процессе ее завершения. Таким образом, именно ресурсный объект отвечает за конкретные действия, связанные с изменением состояния восстанавливаемого объекта. Часто с каждым восстанавливаемым объектом сопоставлен свой ресурсный объект (или физически один и тот же объект является и восстанавливаемым, и ресурсным).

Эвристики (heuristic decisions). Эвристики предоставляют возможность возбуждения исключительной ситуации, которая не следует из логики выполнения транзакции. Пример такого алогичного поведения является возникновение исключительной ситуации при выполнении отката (rollback) транзакции: Менеджер Транзакции, получив указания от инициатора транзакции, выполняет для каждого из зарегистрированных в данной транзакции ресурсных объектов метод xa_rollback(), а в коде соответствующего callback-метода программист хочет сообщить, что откат транзакции по какой-либо причине невозможен.

Основные интерфейсы OTS

Как всегда при работе с CORBA, формальным описанием любого сервиса является набор объявлений на языке IDL. Здесь не будут рассмотрены все интерфейсы OTS или все его методы – в частности, интрефейсы, необходимые для выполнения вложенных транзакций.

Все необходимые объявления содержатся в стандартном файле CosTransactions.idl, который Вы можете найти либо на сайте www.omg.org, либо в комплекте поставки того или иного программного продукта, поддерживающего транзакции в стиле CORBA. Например, в случае использования Borland AppServer, этот файл находится в каталоге каталог_установки/idl.

Интерфейс TransactionalObject. Этот интерфейс не имеет методов:

module CosTransactions
{
  ...
  interface TransactionalObject
  {
  };
};

Его задачей является объявление всех CORBA-объектов, «реализующих» данных интерфейс, как транзакционных объектов. Соответствующие IDL-объявления могут выглядеть, например, так:

#include "CosTransactions.idl"

module MyModule 
{
  interface MyInterface : CosTransactions::TransactionalObject 
  {
    long myMethod (in long arg);
  };
};

Интерфейс Current. Это один из наиболее часто используемых интерфейсов при работе с OTS. Он объявляет методы, которые в большинстве случаев позволяют выполнять все необходимые действия при выполнении транзакций.

module CosTransactions
{
    ...
  interface Current
  {
    void begin() raises(SubtransactionsUnavailable);
    void commit(in boolean report_heuristics)
      raises(NoTransaction, HeuristicMixed, HeuristicHazard  );
    void rollback() raises(NoTransaction);
    void rollback_only() raises(NoTransaction);
    Status get_status();
    string get_transaction_name();
    void set_timeout(in unsigned long seconds);
    Control get_control();
    Control suspend();
    void resume(in Control which)  raises(InvalidControl);
  };
};

Смысл почти всех методов очевиден – за исключением, может быть, методов get_control(), suspend() и resume(). Метод get_control() позволяет обеспечить альтернативный способ управления транзакцией – посредством вспомогательного объекта Control. Интейфейс Control будет рассмотрен позже.

Методы suspend() и resume() позволяют гибко управлять транзакциями в многопоточных приложениях. Метод suspend() не блокирует текущий поток – он просто временно отменяет привязку текущего потока к текущему контексту транзакции. Если позднее данный контекст транзакции опять нужно сопоставить с данным (или другим) потоком, то для этого вызывается метод resume().

Возможные состояния транзакции определяются с помощью следующее перечисления:

  enum Status
  {
    StatusActive,
    StatusMarkedRollback,
    StatusPrepared,
    StatusCommitted,
    StatusRolledBack,
    StatusUnknown,
    StatusNoTransaction,
    StatusPreparing,
    StatusCommitting,
    StatusRollingBack
  };

Получение объектной ссылки на интерфейс Current может быть выполнено по-разному. Стандартным способом CORBA является вызов метода resolve_initial_references(), например:

CORBA::ORB_var orb = CORBA::ORB_init (__argc, __argv);
...
CORBA::Object_var obj = orb->resolve_initial_references ("TransactionCurrent");
CosTransactions::Current_var  current = CosTransactions::Current::_narrow (obj);

Интерфейс Control. Этот интерфейс не нужен сам по себе – он позволяет программисту получить объектные ссылки на действительно полезные и нужные интерфейсы – Coordinator и Terminator.

  interface Control
  {
    Terminator get_terminator() raises(Unavailable);
    Coordinator get_coordinator()  raises(Unavailable);
  };

Интерфейс Terminator просто дублирует функциональность интерфейса Current. Он существует для того, чтобы предоставить возможность завершения транзакции не только инициатору транзакции, но и любому транзакционному объекту. Заметим, что спецификация OTS не требует обязательного предоставления такой возможности.

  interface Terminator
  {
    void commit(in boolean report_heuristics)
      raises(HeuristicMixed,  HeuristicHazard);
    void rollback();
  };

Интерфейс Coordinator более интересен, правда, большинство его методов относятся к вложенным транзакциям. Помимо возможностей, предоставлямых интерфейсом Current, интерфейс Coordinator содержит совершенно необходимый метод регистрации ресурсного объекта:

  interface Coordinator
  {
    ...
    Status get_status();
    boolean is_same_transaction(in Coordinator tc);
    void rollback_only()raises(Inactive);
    string get_transaction_name();

    RecoveryCoordinator register_resource(in Resource r)
      raises(Inactive);

    PropagationContext get_txcontext () raises(Unavailable);
  };

Эти интерфейсы можно использовать, например, так:

CosTransactions::Current_var current;
CosTransactions::Control_var control;
CosTransactions::Coordinator_var coordinator;

...
CORBA::ORB_var orb = CORBA::ORB_init (__argc, __argv);
try
{
  CORBA::Object_var obj = 
  orb->resolve_initial_references ("TransactionCurrent");
  current = CosTransactions::Current::_narrow (obj);
  control = current->get_control();
  coordinator = control->get_coordinator();
  ...
  coordinator->rollback_only();
}
catch (CORBA::Exception& e) {...}

Интерфейс Resource. Этот интерфейс должен быть реализован программистом. В нем объявлены callback-методы, которые будут вызываться Менеджером Транзакций:

  enum Vote
  {
    VoteCommit,
    VoteRollback,
    VoteReadOnly
  };

  interface Resource
  {
    Vote prepare() raises(HeuristicMixed, HeuristicHazard  );
    void rollback()
      raises(
        HeuristicCommit,
        HeuristicMixed,
        HeuristicHazard
      );
    void commit()
      raises(
        NotPrepared,
        HeuristicRollback,
        HeuristicMixed,
        HeuristicHazard
      );
    void commit_one_phase() raises(HeuristicHazard);
    void forget();
  };

Регистрация объекта, реализующего интерфейс Resource, выполняется с помощью вызова метода register_resource() интерфейса Coordinator.

Методы prepare() и commit() вызываются Менеджером транзакции при старте первой и второй фазы двухфазного завершения транзакции, соответственно. Начало завершения транзакции, разумеется, происходит по команде инициатора транзакции. Если же в данной транзакции зарегистрирован только один ресурсный объект, то не имеет смысла начинать двухфазный процесс завершения, и Менеджер Транзакции вызовет для единственного ресурсного объекта метод commit_one_phase().

Интереснее дело обстоит с методом forget(). Он предусмотрен на случай возникновения непредвиденных ситуаций, о чем говорит возбуждение эвристик-исключений. При возбуждении такого исключения ресурсный объект обязан сохранить где-то информацию, которая может понадобиться для обработки такой исключительной ситуации (точнее, предполагается, что такая информация сохранена и доступна). Вызов метода forget() Менеджером Транзакции говорит восстанавливаемому серверу, что сохранять эту информацию больше не нужно.

Интерфейс TransactionFactory. Этот интерфейс позволяет начать новую транзакцию (create()) или распространить действие этой транзакции на другие потоки команд в программе (recreate()).

  interface TransactionFactory
  {
    Control create(in unsigned long time_out);
    Control recreate(in PropagationContext ctx);
  };

Вот как можно начать транзакцию, используя данный интерфейс:

  CosTransactions::TransactionFactory_var factory;
  CosTransactions::Control_var control = factory->create(10);

Особенно полезен метод recreate() в случае, если транзакциями в вашей распределенной системе управляют несколько Менеджеров Транзакций. В этом случае отдельный Менеджер Транзакций вместе с обслуживаемыми им транзакционными объектами составляет так называемый «домен сервиса транзакций» (transaction service domain). В некоторых случаях удобно передать управление транзакцией из одного домена в другой. Этот процесс называется «interpositioning» и позволяет иногда существенно повысить производительность распределенной системы в целом.

Интерфейс Synchronization. Этот интерфейс объявляет два callback-метода, которые могут быть реализованы программистом, если он хочет получать (и использовать) уведомления о начале и завершении транзакции:

interface Synchronization : TransactionalObject
{
  void before_completion();
  void after_completion(in Status status);
};

Если вы реализовали этот интерфейс, то необходимо зарегистрировать объект, обеспечивающий эту реализацию, у Менеджера Транзакций. Процесс регистрации интерфейса Synchronization очень похож на процесс регистрации интерфейса Resource – просто вместо метода register_resource() интерфейса Coordinator используется метод register_synchronization() того же интерфейса:

interface Coordinator
{
  ...
  void register_synchronization (in Synchronization sync)
    raises(Inactive, SynchronizationUnavailable);
};

Типичная последовательность действий при выполнении транзакций

Наиболее распространенным способом при работе с OTS является использование интерфейса Current вместе с неявным автоматическим распространением контекста транзакции. Ниже приведены стандартные этапы выполнения транзакции.

< . . . >

JTS и JTA

В связи c созданием J2EE (Java 2 Enterprise Edition) возникла необходимость формализации управления транзакциями, выраженной в терминах интерфейсов Java. Было принято совершенно естественное решение: поскольку протоколом удаленного взаимодействия в J2EE является стандартный протокол CORBA IIOP (как реализация «бумажного протокола» GIOP «поверх» TCP/IP), то спецификацией Службы Транзакций Java (Java Transaction Service, JTS) стали интерфейсы, полученные путем «пропускания» IDL-интерфейсов OTS через компилятор, выполняющий отображение IDL на Java. Поэтому для использования JTS нужно просто переписать ранее приведенные фрагменты C++-программ c учетом синтаксиса Java.

В J2EE предусмотрен еще один слой – слой более высокого уровня, чем JTS – на уровне которого предусмотрено все необходимое (в большинстве случаев) для управления транзакциями на Java. Этот слой получил название JTA – Java Transaction API.

JTA содержит четыре основных интерфейса – UserTransaction, TransactionManager, Transaction и XAResource. Наиболее важным для прикладных разработчиков является интерфейс UserTransaction. Остальные интерфейсы используются скорее системными, чем прикладными компонентами. Например, интерфейсы TransactionManager и Transaction используются EJB-контейнерами для управления транзакциями в режиме CMT, а интерфейс XAResource предназначен для взаимодействия менеджеров транзакций и менеджеров ресурсов.

< . . . >

Транзакции и EJB

EJB (Enterprise JavaBeans) представляет собой компонентную модель, ориентированную на создание серверных приложений. Управление транзакциями – одно из важнейших задач, которая должна решать такая модель.

Управление транзакциями реализовано в технологии EJB достаточно гибким образом, особенно для session-компонентов. Об особенностях управления транзакциями для каждого из видов EJB-компонентов будет рассказано ниже.

Глобальные и локальные транзакции

Модель DTP вводит понятие Менеджера Ресурсов. Это просто некое приложение, которое содержит ресурсные объекты. Напоминаем, что ресурсные объекты – это объекты, задачей которых является выполнение конкретных действий для «транзакционной логики» выполнения сохранения состояния восстанавливаемых объектов. Примером таких приложений являются SQL-серверы.

В технологии EJB вводятся понятия «локальных» и «глобальных» транзакций. Локальными называются транзакции, управление которыми полностью берет на себя конкретный Менеджер Ресурсов (Oracle, например). В случае локальных транзакций нет необходимости в использовании Менеджера Транзакций – все решает прямое взаимодействие прикладной программы и сервера СУБД.

Глобальные транзакции – это транзакции, при выполнении которых Менеджеры Ресурсов не могут справиться с задачей самостоятельно и нельзя обойтись без Менеджера Транзакций. Примером такой ситуации является использование распределенных транзакций, т.е. транзакций, которые затрагивают несколько Менеджеров Ресурсов сразу.

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

Атрибуты транзакций

Основным способом управления транзакциями в EJB является возложение обязанностей за это на контейнер EJB (хотя во многих случаях программист может взять управление транзакциями на себя). На практике это означает, что код бизнес-методов EJB-компонентов, как правило, не содержит обращений к интерфейсам JTS и/или JTA. Вместо этого автор компонента помещает некоторую информацию в так называемый «дескриптор развертывания» (deployment descriptor) для данного компонента. Дескриптор развертывания представляет собой XML-документ, и при установке компонента в контейнер последний считывает эту информацию и руководствуется ей в процессе работы системы. Такой режим управления транзакциями называется CMT-режим (Container-Managed Transactions).

Разработчик EJB-компонента при использовании режима CMT (который рекомендуется использовать в большинстве случаев) должен для тех методов EJB-компонента, которые объявлены в его Home- и Remote-интерфейсах, задать атрибуты транзакции.

Контейнер в режиме CMT начинает локальную транзакцию в следующих случаях:

< . . . >

Транзакции и session-компоненты

При использовании session-компонентов автор компонента сам решает, кто будет управлять транзакциями – контейнер EJB или же сам компонент (режим BMT – Bean-Managed Transactions). Рекомендуется использовать CMT-режим и только в случае, когда это невозможно, управлять транзакциями из кода компонента.

Применительно к разговору о транзакциях имеет смысл обратить внимание на поведение session stateful-компонентов, т.е. компонентов с состоянием (компоненты без состояния могут создаваться заново для каждого нового вызова, и рассматривать их просто неинтересно).

Важно понимать, что контейнер EJB не должен (и не может) обеспечивать подтверждение или отмену изменения состояния session-компонента в процессе завершения транзакции – у него просто нет для этого никакой информации. Только разработчик компонента знает, что нужно сделать при завершении транзакции тем или иным способом. Тем не менее, контейнер EJB все-таки частично приходит на помощь разработчику: вы можете указать, что ваш session stateful-компонент реализует интерфейс javax.ejb.SessionSynchronization следующего вида:

public abstract interface SessionSynchronization 
{
  void afterBegin()   throws EJBException, RemoteException;
  void afterCompletion(boolean status) throws EJBException, RemoteException;
  void beforeCompletion()   throws EJBException, RemoteException;
}

< . . . >

Транзакции и entity-компоненты

С транзакциями для entity-компонентов дело обстоит в каком-то смысле проще, чем для session-компонентов – ими управляет контейнер и только контейнер. Сигналы о начале и завершении транзакции используются контейнером для синхронизации состояния компонента в долговременном хранилище и экземпляра компонента (т.е. объектного представления его состояния) в оперативной памяти.

Поскольку entity-компонент по определению является объектным представлением информации в той или иной базе данных, возникает проблема автоматического сопоставления транзакции в терминах OTS (JTS) и транзакции в терминах, например, SQL-сервера типа ORACLE. Об этом подробнее будет рассказано немного позже – в разделе, посвященном взаимодействию EJB-компонентов с СУБД.

Получение объектной ссылки на интерфейс UserTransaction

Основным способом «ручного» управления транзакциями для EJB-компонентов – т.е. при работе с session-компонентами в режиме BMT – является использование интерфейса javax.transaction.UserTransaction. Спецификация EJB 2.0 оговаривает два способа, позволяющих программисту получить объектную ссылку на этот интерфейс. Один из этих способов применим всегда, другой – только в коде методов компонента, да и то при определенных условиях.

Универсальный способ получение объектной ссылки на UserTransaction требует использования JNDI – службы имен J2EE. Спецификация EJB жестко фиксирует имя, под которым опубликована эта объектная ссылка. Это имя - "java:comp/UserTransaction". Код программы может иметь следующий вид:

javax.naming.Context context = new javax.naming.InitialContext();
javax.transaction.UserTransaction user_tr =
    (javax.transaction.UserTransaction) context.lookup("java:comp/UserTransaction");

Второй способ позволяет получить нужную объектную ссылку, обращаясь к методу getUserTransaction() некоторого специального объекта, создаваемого EJB-контейнером при создании нового экземпляра компонента. Для session-компонентов этот объект имеет тип javax.ejb.SessionContext, для entity-компонентов – javax.ejb.EntityContext. Программист может сохранить соответствующие объекты-контексты в коде стандартного метода setSessionContext (для session-компонентов) или setEntityContext (для entity-компонентов) примерно так:

public class Server implements SessionBean, SessionSynchronization 
{
  private SessionContext sessionContext;
  ...
  public void setSessionContext(SessionContext sessionContext) 
    throws RemoteException 
  {
    this.sessionContext = sessionContext;
  }
  public myMethod () 
  {
    ...
    javax.transaction.UserTransaction user_tr = 
          sessionContext.getUserTransaction();
    ...
  }
}

Взаимодействие с СУБД

Для разных видов компонентов EJB взаимодействие их с долговременными хранилищами – базами данных различных видов – осуществляется по-разному. Session-компоненты предназначены для реализации бизнес-логики и, следовательно, для них взаимодействие с СУБД не является первоочередной задачей просто по определению. Конечно, разработчик session-компонента может поместить в код его бизнес-методов все, что угодно, в том числе и обращения к API некоторой СУБД, но контейнер EJB ничего «не знает» о такого рода действиях. Если нужно для session-компонента обеспечить связь между транзакциями в смысле OTS/JTS и транзакциями СУБД, то делать это необходимо «вручную». Сохранение же состояния stateful session-компонентов – процесс вообще прозрачный для разработчика этих компонентов (в том смысле, что ему для этого, как правило, ничего делать не надо).

Другое дело – entity-компоненты. Entity-компоненты существуют тогда, когда есть некоторая информация в базах данных. Они теснейшим образом связаны с СУБД, поэтому совершенно необходим механизм автоматического сопоставления транзакций OTS/JTS и транзакций СУБД.

На этом уровне взаимодействия с СУБД технология EJB позволяет использовать только такие СУБД, к которым определен JDBC-интерфейс.

Спецификация EJB не оговаривает стандартным образом все аспекты, необходимые для реального взаимодействия entity-компонентов с JDBC-БД, оставляя некоторые вопросы на усмотрение разработчиков конкретных серверов приложений. В связи с этим, дальнейшее обсуждение иногда будет относиться не к технологии EJB вообще, а к одной из ее реализаций – к Borland AppServer 4.5 (последней версии на момент написания данной статьи).

Обеспечение взаимодействия между JTS и JDBC

С точки зрения процесса выполнения транзакций, важно понимать, что EJB различает (и по-разному использует) JDBC-совестимые СУБД двух видов:

Отличия в версиях JDBC (1.0 или 2.0) в данном случае непринципиальны. Связано это с тем, что технология EJB всегда использует для получения логического соединения с БД метод getConnection() интерфейса javax.sql.DataSource. Реализация этого интерфейса может быть выполнена по-разному: если для выбранной СУБД существует JDBC-драйвер версии 2.0 или старше, то он сам реализует этот интерфейс. Если же в наличии имеется только JDBC-драйвер версии 1.0, то реализацию интерфейса DataSource (и организацию пула соединений с БД) выполняет специально предназначенный для этого компонент сервера приложений. В Borland AppServer этот компонент называется JDBC-адаптером.

Необходимо подчеркнуть что соединения, полученные до открытия глобальной транзакции, не будут сопоставлены с этой (новой) транзакцией. Поэтому BMP-компоненты должны получить новое соединение с БД посредством вызова метода getConnection() после открытия глобальной транзакции. В случае CMP-компонентов это делает сам контейнер.

Важно следующее: в любом случае присутствует реализация интерфейса javax.sql.DataSource (точнее, производных от него интерфейсов), и объектная ссылка на эту реализацию доступна через стандартную службу имен JNDI. Контейнер EJB использует эту объектную ссылку для получения соединения с СУБД, не зная, физическое это соединение или логическое.

Следующий важный для понимания момент. Для entity-компонента (который, напоминаю, всегда работает в режиме BMT, т.е. транзакциями управляет не сам компонент, а его контейнер) должен быть указан один из 6 возможных атрибутов транзакций. Три из них определяют транзакции глобальные – управляемые Менеджером Транзакций, три других – локальные, т.е. их выполнение берет на себя исключительно Менеджер Ресурсов (JDBC-совместимая СУБД в нашем случае).

Локальные транзакции рассматривать неинтересно. Суть происходящего в этом случае такова: при отсутствии Менеджера Транзакций (точнее, отказе от его услуг) в момент, когда это становится необходимым – а именно, при выполнении фрагмента кода того или иного метода компонента, содержащего обращение к JDBC API – начинается локальная транзакция в классическом стиле JDBC. Естественно, в контексте этой транзакции фиксируются (или отменяются) только те изменения, которые сопоставлены с конкретным физическим соединением с СУБД.

Если же транзакция является глобальной (т.е. автор компонента указал атрибут Required, RequiresNew или Mandatory), то за завершение транзакции отвечает Менеджер Транзакций. Он просто вызывает callback-методы IDL-интерфейса Resource OTS для зарегистрированных в этой транзакции ресурсных объектов. Поскольку сейчас речь идет о Java, то в соответствие IDL-интерфейсу CosTransactions::Resource поставлен интерфейс JTS, который называется org.omg.CosTransactions.Resource.

< . . . >

Пример использования транзакций

В данном примере иллюстрируются некоторые аспекты управления транзакциями при совместной работе Java- и C++-приложений.

< . . . >

При создании демонстрационных примеров использовались следующие продукты:

**Полный текст статьи можно найти в печатной версии журнала

Copyright © 1994-2016 ООО "К-Пресс"