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


Создание клиентского приложения, использующего распределенные транзакции


Для тестирования созданного ранее сервера и инициации распределенных транзакций создадим клиентское приложение, имитирующее процесс оформления заказов. На главной форме приложения поместим кнопку с надписью "Connect", три компонента TDCOMConnection, связанные с соответствующими серверами, три компонента TClientDataSet, связанные с соответствующими компонентами TDCOMConnection, три компонента TDataSource, связанные с компонентами TClientDataSet и блокнот из двух страниц. На одной из страниц блокнота разместим компонент TDBGrid, отображающий данные из таблицы со списком товаров на складе, компонент TEdit для ввода пользователем адреса доставки, и кнопку для инициирования транзакции - принятия заказа. На второй странице поместим два компонента TDBGrid для отображения данных из двух других таблиц и компонент TSplitter между ними (рис. 25):

Рис. 25. Клиентское приложение для тестирования распределенных транзакций

Создадим обработчики событий, связанных с нажатием на кнопки:

unit allcl1; //Client application for using distributed transactions

//By N.Elmanova

//05.12.1998

interface

uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, Grids, DBGrids, Db, DBClient, StdCtrls, MConnect, ExtCtrls, ComCtrls;

type TForm1 = class(TForm) PageControl1: TPageControl; TabSheet1: TTabSheet; TabSheet2: TTabSheet; DCOMConnection1: TDCOMConnection; ClientDataSet1: TClientDataSet; DataSource1: TDataSource; DBGrid1: TDBGrid; Edit1: TEdit; Label1: TLabel; Button1: TButton; DBGrid2: TDBGrid; DBGrid3: TDBGrid; Splitter1: TSplitter; DCOMConnection2: TDCOMConnection; ClientDataSet2: TClientDataSet; DataSource2: TDataSource; DCOMConnection3: TDCOMConnection; ClientDataSet3: TClientDataSet; DataSource3: TDataSource; Button2: TButton; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); private { Private declarations } public { Public declarations } end;

var Form1: TForm1;

implementation

{$R *.DFM}



procedure TForm1.Button1Click(Sender: TObject); var n:integer;val:double;gnam,addr:widestring;


begin try n:= ClientDataSet1.FieldByName('GOODSNUMBER').Value; val:= ClientDataSet1.FieldByName('PRICE').Value; gnam:= ClientDataSet1.FieldByName('GOODSNAME').Value; addr:=Edit1.Text; DcomConnection2.Connected:=true; DCOMConnection2.AppServer.DoTrans(n,val,addr,gnam); ShowMessage('Заказ принят'); except ShowMessage('Заказ не принят '); end; DcomConnection2.Connected:=false;

end;

procedure TForm1.Button2Click(Sender: TObject); begin DCOMConnection1.Connected:=true; DCOMConnection2.Connected:=true; DCOMConnection3.Connected:=true; CLientdataset1.data:=Dcomconnection1.Appserver.GetGoods; CLientdataset2.data:=Dcomconnection2.Appserver.GetPays; CLientdataset3.data:=Dcomconnection3.Appserver.GetDelivery; DCOMCOnnection1.Connected:=false; DCOMCOnnection2.Connected:=false; DCOMCOnnection3.Connected:=false;

end; end.

Для тестирования распределенных транзакций запустим приложение. Введем адрес в компонент TEdit, выберем строку в списке товаров и нажмем на кнопку "Заказать" (рис. 26).



Рис. 26. Тестирование распределенной транзакции

В результате получим сообщение о том, что заказ принят

Нажав на кнопку Connect, обновим данные в компонентах TDBGrid. При этом запись, выбранная ранее, исчезнет, а в двух других компонентах TDBGrid появятся две новых (рис. 27):



Рис. 27. Результат выполнения распределенной транзакции

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



Сведения о завершенных и отмененных транзакциях можно получить, выбрав в MTS Explorer опцию Transaction Statistics (рис. 28):



Рис. 28. Просмотр статистики выполнения и отката распределенных транзакций

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

Отметим, однако, что при создании подобного рода серверных объектов и их клиентов следует всегда пытаться исключить возможность ошибочных действий пользователя, поэтому более корректным было бы иметь следующий обработчик события, связанного с нажатием на кнопку "Заказать":

procedure TForm1.Button1Click(Sender: TObject); var n:integer;val:double;gnam,addr:widestring;

begin try n:= ClientDataSet1.FieldByName('GOODSNUMBER').Value; val:= ClientDataSet1.FieldByName('PRICE').Value; gnam:= ClientDataSet1.FieldByName('GOODSNAME').Value; addr:=Edit1.Text; DcomConnection2.Connected:=true; DCOMConnection2.AppServer.DoTrans(n,val,addr,gnam); ShowMessage('Заказ принят'); Button2Click(self); except ShowMessage('Заказ не принят '); end; DcomConnection2.Connected:=false;

end;

Отметим также, что при нажатии на кнопку "Заказать" в случае отсутствия данных в компонентах TDBGrid в клиентском приложении возникнет исключение, связанное с отсутствием нужного поля в компоненте TClientDataSet. Следовательно, данная кнопка в такой ситуации должна быть невыбираемой. Поэтому установим значение ее свойства Enabled равным False и перепишем обработчик события,связанного с нажатием на кнопку Connect::

procedure TForm1.Button2Click(Sender: TObject); begin try DCOMCOnnection1.Connected:=true; DCOMCOnnection2.Connected:=true; DCOMCOnnection3.Connected:=true; CLientdataset1.data:=Dcomconnection1.Appserver.GetGoods; CLientdataset2.data:=Dcomconnection2.Appserver.GetPays; CLientdataset3.data:=Dcomconnection3.Appserver.GetDelivery; Button1.Enabled:=true; except Button1.Enabled:=false; ShowMessage('Один из серверных объектов недоступен'); end; DCOMCOnnection1.Connected:=false; DCOMCOnnection2.Connected:=false; DCOMCOnnection3.Connected:=false;

end;

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

Итак, на примере Delphi 4 и Microsoft Transaction Server мы рассмотрели возможность и способы использования технологий COM и COM+ для организации распределенных вычислений и управления распределенными транзакциями.

Следующая статья данного цикла будет посвящена другой технологии, используемой при организации распределенных вычислений - DCE (Distributed Computing Environment), и ее реализации в многоплатформенном сервере приложений Inprise Entera.

<< |



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