Распределенные вычисления и технологии Inprise


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


Теперь создадим второй объект для управления созданной ранее в базе данных DBDEMOS таблицей delivery.db. Закроем все открытые проекты и создадим новый серверный объект, такой же, как и предыдущий (рис. 17):

Рис. 17. Серверный объект DelDM для управления таблицей delivery.db

В отличие от предыдущего случая компонент TDatabase свяжем с базой данных DBDEMOS (рис. 18):

Рис. 18. Свойства компонента TDatabase серверного объекта delDM

В качестве свойства SQL компонента TQuery используем следующее SQL-предложение:

delete from delivery where OrdNum=:d

Затем отредактируем библиотеку типов, добавив три метода GetDelivery, AddDeliery и DelDelivery для получения данных из таблицы, добавления и удаления записи (рис. 19):

Рис. 19. Библиотека типов серверного объекта, управляющего таблицей delivery.db

Реализация этих методов имеет следующий вид:

unit del1; //Another simple MTS server



//By N.Elmanova

//01.12.1998

interface

uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, ComServ, ComObj, VCLCom, StdVcl, BdeProv, BdeMts, DataBkr, DBClient, MtsRdm, Mtx, dels_TLB, DBTables, Provider, Db;

type TdelDM = class(TMtsDataModule, IdelDM) deltable: TTable; DelProvider: TProvider; Database2: TDatabase; Query4: TQuery; Session2: TSession; private { Private declarations } public { Public declarations } protected function GetDelivery: OleVariant; safecall; procedure AddDelivery(OrdNum: Integer; const OrdName: WideString; const OrdAddr: WideString); safecall; procedure DelDelivery(OrdNum: Integer); safecall; end;

var delDM: TdelDM;

implementation

{$R *.DFM}

function TdelDM.GetDelivery: OleVariant; begin Result:=DelProvider.Data;

SetComplete;

end;

procedure TdelDM.AddDelivery(OrdNum: Integer; const OrdName: WideString; const OrdAddr: WideString); begin try deltable.open; deltable.append; deltable.fieldbyname('OrdNum').Value:=OrdNum; deltable.fieldbyname('GoodsName').Value:=OrdName; deltable.fieldbyname('Address').Value:=OrdAddr; deltable.post; deltable.close; SetComplete; except SetAbort; raise; end;


end;

procedure TdelDM.DelDelivery(OrdNum: Integer); begin try database2.open; deltable.open; deltable.SetRangeStart; deltable.FieldByName('OrdNum').AsInteger:=OrdNum; deltable.SetRangeEnd; deltable.FieldByName('OrdNum').AsInteger:=OrdNum; deltable.ApplyRange; deltable.Delete; deltable.close; database2.close; except SetAbort; raise; end;

end; initialization TComponentFactory.Create(ComServer, TdelDM, Class_delDM, ciMultiInstance, tmApartment); end.

Скомпилируем и установим данный объект в тот же "пакет", что и предыдущий.

Рекомендуется протестировать данный объект, создав клиентское приложение, более или менее аналогичное предыдущему. При тестировании следует помнить, что в этой таблице есть уникальный первичный ключ, поэтому при вводе записей с одинаковым значением поля OrdNum транзакции завершаться не будут.

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

delete from ord where ordnum=:d



Рис. 20. Модуль данных серверного объекта pays для управления таблицей ord.dbf

Теперь компонент TDatabase свяжем с созданной нами базой данных dbpay, содержащей таблицу ord.dbf (с ней мы свяжем компонент TTable, рис. 21):



Рис. 21. Свойства компонента TDatabase серверного объекта, управляющего распределенными транзакциями

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

delete from ord where ordnum=:d

Теперь добавим в проект библиотеки типов двух созданных ранее серверов. Для этого следует выбрать из меню Delphi опцию Project/Import type library, нажать кнопку Add и выбрать соответствующий файл с расширением *.tlb (рис. 22):



Рис. 22. Импорт библиотек типов серверных объектов - участников распределенной транзакции

Далее отредактируем библиотеку типов данного серверного объекта, создав методы GetPays. AddPay, DelPay для доставки данных клиентскому приложению, добавления и удаления записей, а также метод DoTrans, реализующий распределенную транзакцию (удаление записи о выбранном товаре из таблицы STOCKTABLE в базе данных IBLOCAL и добавление по одной записи в таблицу заказов на доставку delivery.db в базе данных DBDEMOS и в таблицу счетов за заказы ord.dbf в базе данных paydb, рис. 23).





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

Реализация этих методов имеет следующий вид:

unit pay1; //MTS server for managing distributed transactions

//By N.Elmanova

//04.12.1998

interface

uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, ComServ, ComObj, VCLCom, StdVcl, BdeProv, BdeMts, DataBkr, DBClient, MtsRdm, Mtx, paysrv_TLB, dels_TLB, st_TLB, DBTables, Provider, Db;

type Tpays = class(TMtsDataModule, Ipays) paytable: TTable; PayProvider: TProvider; Database3: TDatabase; Query6: TQuery; Session3: TSession; Query5: TQuery; private FStockDM1: IStockDM1; FDelDM: IDelDM; { Private declarations } public { Public declarations } protected function GetPays: OleVariant; safecall; procedure AddPay(Pnum: Integer; Pval: Double; const Address: WideString); safecall; procedure DelPay(Pnum: Integer); safecall; procedure DoTrans(Num: Integer; Val: Double; const Addr, Gname: WideString); safecall; end;

var pays: Tpays;

implementation

{$R *.DFM}

function Tpays.GetPays: OleVariant; begin Result:=PayProvider.Data;

SetComplete;

end;

procedure Tpays.AddPay(Pnum: Integer; Pval: Double; const Address: WideString); begin try paytable.open; paytable.append; paytable.fieldbyname('OrdNum').Value:=PNum; paytable.fieldbyname('Payment').Value:=Pval; paytable.fieldbyname('Address').Value:=Address; paytable.post; paytable.close; SetComplete; except SetAbort; end; end;

procedure Tpays.DelPay(Pnum: Integer); begin try Database3.Open; paytable.Open; paytable.SetRangeStart; paytable.FieldByName('ordnum').AsInteger:=Pnum; paytable.SetRangeEnd; paytable.FieldByName('ordnum').AsInteger:=Pnum; paytable.ApplyRange; paytable.Delete; paytable.Close; Database3.Close; SetComplete; except SetAbort; raise; end; end;

procedure Tpays.DoTrans(Num: Integer; Val: Double; const Addr, Gname: WideString); begin try OleCheck(ObjectContext.CreateInstance(CLASS_StockDM1, IStockDM1, FStockDM1)); OleCheck(ObjectContext.CreateInstance(CLASS_DelDM, IDelDM, FDelDM)); FStockDM1.DeleteGoods(Num); FDelDM.AddDelivery(Num,Gname,Addr); AddPay(Num,Val,Addr); except DisableCommit; raise; end; EnableCommit;



end; initialization TComponentFactory.Create(ComServer, Tpays, Class_pays, ciMultiInstance, tmApartment); end.

Прокомментируем приведенный выше код для метода DoTrans. Этот код реализует распределенную транзакцию, вызывая методы двух порожденных ей серверных объектов и выполняя собственные манипуляции с таблицей счетов. При вызове метода DoTrans клиентским приложением все три серверных объекта функционируют согласованно.

В начале выполнения создается так называемый контекст транзакции - интерфейс ITransactionContextEx. Этот интерфейс контролирует выполнение транзакции и обладает методами CreateInstance (создание экземпляра порожденного объекта), Commit (завершение транзакции) и Abort (откат транзакции). Отметим, что если для порождения серверного объекта MTS клиентским приложением используется компонент TDCOMConnection, то для порождения серверного объекта другим серверным объектом используется вызов метода CreateInstance интерфейса ITransactionContextEx. Параметрами этого метода являются CLSID объекта, интерфейс объекта и указатель на объект (возвращаемый параметр).

Далее следуют вызовы методов порожденных серверных объектов и собственные манипуляции с данными. Если все операции были успешны, транзакция завершена, и может быть выполнен метод Commit интерфейса ITransactionContextEx. Если же операции были неуспешны, и в одном или обоих порожденных серверах либо во время собственных манипуляций с данными возникнут исключения (например, другой пользователь уже удалил запись из списка товаров, сделав заказ, или какая-то из таблиц заблокирована), будет вызван метод Abort.

Для тестирования распределенных транзакций установим все три серверных объекта в один и тот же "пакет" (рис. 24):



Рис. 24. Серверные объекты, участвующие в распределенной транзакции


Содержание раздела