Главная » Delphi » Примеры из реальной жизни DataSnap

0

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

мимся с тем, как можно применить эту технологию для решения практических задач.

Объединения

Существенное влияние на построение приложений реляционных баз данных  ока зывают  способы организации связей  между таблицами. Зачастую  удобнее  работать с представлениями (view), созданными на базе высоко  нормализованных данных. В таком случае  представления существенно облегчают и упрощают отображение данных, по сравнению с их реальной структурой. Но при  обновлении данных  в подобных объеди нениях (join) необходимо соблюдать дополнительные меры предосторожности.

Обновление одной таблицы

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

При  использовании  объединений,  требующих обновления  лишь  одной   таблицы, большинство необходимых действий Delphi может  выполнить самостоятельно. При  за писи в базу данных информации из одной таблицы необходимо выполнить следующее.

1.  Добавьте постоянные (persistent) поля в объединенный компонент TQuery.

2.   Для каждого поля компонента TQuery, которое необходимо обновить, устано

вите TField.ProviderFlags=[].

3.  Чтобы сообщить приложению DataSnap, какую именно таблицу нужно обновить, поместите в обработчик события DataSetProvider.OnGetTableName код, при веденный ниже. Это новое  событие упрощает способ задания имени  таблицы, хо тя в предыдущих версиях Delphi  то же самое можно  было сделать  с помощью со бытия DataSetProvider.OnGetDataSetProperties:

procedure TJoin1Server.prvJoinGetTableName(Sender: TObject; DataSet: TDataSet; var TableName: String);

begin

TableName := ‘Emp';

end;

По завершении указанных  действий имя таблицы сохраняется в компоненте Cli- entDataSet. Теперь при вызове метода ClientDataSet1.ApplyUpdates() прило жению  DataSnap будет известно имя  таблицы, используемое по умолчанию, и ему не придется выполнять его поиск.

Можно  воспользоваться и другим  способом —  компонентом TUpdateSQL, выпол няющим обновление лишь требуемой таблицы. Благодаря этой  возможности в Delphi во время  процесса согласования можно  использовать метод  TQuery.UpdateObject. Такой  подход  наиболее точно соответствует процессу, применяемому в традицион ных приложениях клиент/сервер.

НА ЗАМЕТКУ

Не все объекты класса TDataset обладают свойством UpdateObject. Но для них все равно  можно  использовать  этот  подход,  поскольку  изменения  относятся  к TUpdateSQL. Достаточно просто определить операторы SQL для каждого действия (удаления, вставки, изменения) и использовать код, подобный следующему:

procedure TForm1.DataSetProvider1BeforeUpdateRecord(Sender: TObject; SourceDS: TDataSet; DeltaDS: TCustomClientDataSet; UpdateKind: TUpdateKind; var Applied: Boolean);

begin

UpdateSQL1.DataSet := DeltaDS;

UpdateSQL1.SetParams(UpdateKind);

ADOCommand1.CommandText := UpdateSQL1.SQL[UpdateKind].Text;

ADOCom-

mand1.Parameters.Assign(UpdateSQL1.Query[UpdateKind].Params);

ADOCommand1.Execute;

Applied := true;

end;

Пример использования такого подхода можно найти на прилагаемом CD в катало

ге \Join1 раздела, посвященного настоящей главе.

Обновление нескольких таблиц

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

•   Прежний подход заключался в использовании метода  DataSetProvider. Be- foreUpdateRecord() для  разбиения пакета  данных  и  применения обновле ний к исходным таблицам.

•   Нынешний подход  заключается во внесении обновлений с помощью свойства

UpdateObject.

Если для объединения нескольких таблиц  используется кэширование обновлений, то  для  каждой  обновляемой таблицы потребуется настроить один  компонент TUp- dateSQL. Поскольку свойство UpdateObject может  быть  присвоено только  одному компоненту TUpdateSQL, то все свойства TUpdateSQL.DataSet необходимо связать с объединенным  набором данных  программно, в TQuery.OnUpdateRecord. После этого  нужно  вызвать метод  TUpdateSQL.Apply, чтобы  связать  все параметры и вы полнить исходный оператор  SQL.  В рассматриваемом случае  набор  данных  содер жится  в свойстве Delta. Этот набор  данных  передается в качестве параметра в обра ботчик события TQuery.OnUpdateRecord.Все, что осталось сделать, — так это присвоить значения свойствам SessionName и DatabaseName, что позволит осуществить обновление в том же самом контексте, что и другие транзакции, а также  связать свойство DataSet со свойством Delta, которое передается событию. Полученный в результате код обработчика события TQuery.OnUpdateRecord приведен в листинге 21.6.

Листинг 21.6. Объединение с использованием компонента TUpdateSQL

procedure TJoin2Server.JoinQueryUpdateRecord(DataSet: TDataSet; UpdateKind: TUpdateKind; var UpdateAction: TUpdateAction);

begin

usqlEmp.SessionName := JoinQuery.SessionName;

usqlEmp.DatabaseName := JoinQuery.DatabaseName;

usqlEmp.Dataset := Dataset;

usqlEmp.Apply(UpdateKind);

usqlFTEmp.SessionName := JoinQuery.SessionName; usqlFTEmp.DatabaseName := JoinQuery.DatabaseName; usqlFTEmp.Dataset := Dataset; usqlFTEmp.Apply(UpdateKind);

UpdateAction := uaApplied;

end;Поскольку все действия выполнены внутри архитектуры DataSnap, то весь процесс обновления может  быть запущен  при обращении к методу DataSnap ClientData- Set1.ApplyUpdates(0);.

Пример использования такого подхода можно найти на прилагаемом CD в катало

ге \Join2 раздела, посвященного настоящей главе.

Приложения DataSnap в Web

Несмотря на введение Kylix, Delphi остается жестко  привязанным к платформе (Windows  или Linux). Следовательно, любой тип клиента должен  выполняться на том типе машины, для которого он был написан. Но это не всегда желательно. Например, может  понадобиться обеспечить простой доступ  к базе  данных  через Internet, а по скольку ранее было  создано  приложение сервера, которое помимо обеспечения биз нес правил   функционирует  в  качестве брокера, то  было  бы  полезно использовать именно его и не создавать заново уровни  бизнес  правил  и доступа к данным  для каж дой среды исполнения.

Простой HTML

Настоящий раздел  посвящен вопросам адаптации приложения сервера и создания нового уровня представления, на котором используется простой язык HTML. Прежде чем  приступить  к изучению этого  раздела, необходимо ознакомиться с материалом главы  31,  “Компоненты WebBroker открывают двери  в Internet”,  предыдущего изда ния  Delphi 5 Руководство разработчика, находящегося на прилагаемом CD. При  исполь зовании такого  подхода  в архитектуру приложения вводится еще  один  уровень.  Поотношению к приложению сервера модуль WebBroker функционирует как клиент, преобразующий данные  в формат HTML  для отображения их в окне  броузера. При этом  теряются некоторые преимущества работы в интегрированной среде  разработ ки Delphi (такие, например, как возможность использования многочисленных эле ментов управления).  Но  если  доступ  к данным  в формате HTML  необходим, то  это неизбежно.

Создав  приложение WebBroker и модуль  WebModule, достаточно поместить в эту форму компоненты TDispatchConnection и TClientDataSet. После  заполнения не обходимых свойств можно  воспользоваться соответствующими методами для преобра зования данных в формат HTML и передачи их клиенту.

Одним  из эффективных приемов является добавление компонента TDataSetTa- bleProducer, связанного с компонентом TClientDataSet. В этом случае пользователь сможет  щелкнуть на ссылке  и перейти на страницу редактирования, на которой можно будет модифицировать и обновлять данные. В листингах 21.7 и 21.8 представлен пример реализации этого приема.

Листинг 21.7. Код HTML для редактирования и обновления данных

<form action="<#SCRIPTNAME>/updaterecord" method="post">

<b>EmpNo: <#EMPNO></b>

<input type="hidden" name="EmpNo" value=<#EMPNO>>

<table cellspacing="2" cellpadding="2" border="0">

<tr>

<td>Last Name:</td>

<td><input type="text" name="LastName" value=<#LASTNAME>></td>

</tr>

<tr>

<td>First Name:</td>

<td>

<input type="text" name="FirstName" value=<#FIRSTNAME>>

</td>

</tr>

<tr>

<td>Hire Date:</td>

<td>

<input type="text" name="HireDate" size="8" value=<#HIREDATE>>

</td>

</tr>

<tr>

<td>Salary:</td>

<td>

<input type="text" name="Salary" size="8" value=<#SALARY>>

</td>

</tr>

<tr>

<td>Vacation:</td>

<td>

<input type="text" name="Vacation" size="4" value=<#VACATION>>

</td>

</tr>

</table><input type="submit" name="Submit"value="Apply Updates">

<input type="Reset">

</form>

Листинг 21.8. Код для редактирования и внесения обновлений

unit WebMain;

interface uses

Windows, Messages, SysUtils, Classes, HTTPApp, DBWeb, Db,

DBClient, MConnect, DSProd, HTTPProd;

type

TWebModule1 = class(TWebModule)

dcJoin: TDCOMConnection;

cdsJoin: TClientDataSet;

dstpJoin: TDataSetTableProducer;

dsppJoin: TDataSetPageProducer;

ppSuccess: TPageProducer;

ppError: TPageProducer;

procedure WebModuleBeforeDispatch(Sender: TObject;

Request: TWebRequest; Response: TWebResponse;

var Handled: Boolean);

procedure WebModule1waListAction(Sender: TObject;

Request: TWebRequest; Response: TWebResponse;

var Handled: Boolean);

procedure dstpJoinFormatCell(Sender: TObject; CellRow,

CellColumn: Integer; var BgColor: THTMLBgColor;

var Align: THTMLAlign; var VAlign: THTMLVAlign;

var CustomAttrs, CellData: String);

procedure WebModule1waEditAction(Sender: TObject;

Request: TWebRequest; Response: TWebResponse;

var Handled: Boolean);

procedure dsppJoinHTMLTag(Sender: TObject; Tag: TTag;

const TagString: String; TagParams: TStrings;

var ReplaceText: String);

procedure WebModule1waUpdateAction(Sender: TObject;

Request: TWebRequest; Response: TWebResponse;

var Handled: Boolean);

private

{ Закрытые объявления }

DataFields: TStrings;

public

{ Открытые объявления }

end;

var

WebModule1: TWebModule1;implementation

{$R *.DFM}

procedure TWebModule1.WebModuleBeforeDispatch(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);

begin

with Request do

case MethodType of

mtPost: DataFields:=ContentFields;

mtGet: DataFields:=QueryFields;

end;

end;

function LocalServerPath(sFile: string = ”): string;

var

FN: array[0..MAX_PATH- 1] of char;

sPath: shortstring;

begin

SetString(sPath, FN, GetModuleFileName(hInstance, FN,

SizeOf(FN)));

Result := ExtractFilePath( sPath ) + ExtractFileName( sFile );

end;

procedure TWebModule1.WebModule1waListAction(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);

begin cdsJoin.Open;

Response.Content := dstpJoin.Content;

end;

procedure TWebModule1.dstpJoinFormatCell(Sender: TObject; CellRow, CellColumn: Integer; var BgColor: THTMLBgColor; var Align: THTMLAlign; var VAlign: THTMLVAlign; var CustomAttrs, CellData: String);

begin

if (CellRow > 0) and (CellColumn = 0) then

CellData := Format(‘<a href="%s/getrecord?empno=%s">%s</a>’,

[Request.ScriptName, CellData, CellData]);

end;

procedure TWebModule1.WebModule1waEditAction(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);

begin

dsppJoin.HTMLFile := LocalServerPath(‘join.htm’);

cdsJoin.Filter := ‘EmpNo = ‘ + DataFields.Values[‘empno’];

cdsJoin.Filtered := true;

Response.Content := dsppJoin.Content;

end;procedure TWebModule1.dsppJoinHTMLTag(Sender: TObject; Tag: TTag; const TagString: String; TagParams: TStrings; var ReplaceText: String);

begin

if CompareText(TagString, ‘SCRIPTNAME’) = 0 then

ReplaceText:= Request.ScriptName;

end;

procedure TWebModule1.WebModule1waUpdateAction(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);

var

EmpNo, LastName, FirstName, HireDate, Salary, Vacation: string;

begin

EmpNo:=DataFields.Values[‘EmpNo’];

LastName:=DataFields.Values[‘LastName’];

FirstName:=DataFields.Values[‘FirstName’];

HireDate:=DataFields.Values[‘HireDate’];

Salary:=DataFields.Values[‘Salary’];

Vacation:=DataFields.Values[‘Vacation’];

cdsJoin.Open;

if cdsJoin.Locate(‘EMPNO’, EmpNo, []) then begin

cdsJoin.Edit;

cdsJoin.FieldByName(‘LastName’).AsString:=LastName;

cdsJoin.FieldByName(‘FirstName’).AsString:=FirstName;

cdsJoin.FieldByName(‘HireDate’).AsString:=HireDate;

cdsJoin.FieldByName(‘Salary’).AsString:=Salary;

cdsJoin.FieldByName(‘Vacation’).AsString:=Vacation;

if cdsJoin.ApplyUpdates(0)=0 then

Response.Content:=ppSuccess.Content

else

Response.Content:=pPError.Content;

end;

end;

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

CОВЕТ

При создании модуля WebModule и приложения сервера использовалась концепция уче- та состояния. Поскольку протокол HTTP не зависит от состояния, нельзя гарантировать, что по окончании соединения значения свойств останутся прежними.CОВЕТWebBroker — это один из способов предоставить данные Web-броузерам. Используя WebSnap можно существенно расширить возможности приложения, поскольку, кроме всего прочего, он позволяет осуществлять поддержку сеансов и работу со сценариями.

Чтобы запустить этот  пример, убедитесь, что  предварительно скомпилировали и зарегистрировали приложение из примера Join2. Затем  скомпилируйте Web приложение (версии CGI или ISAPI) и поместите исполняемый файл  в каталог  Web сервера, обладающий правом выполнять сценарии (script  capable). Коду  в каталоге сценариев необходим также  файл  join.htm, поэтому  скопируйте туда и его. Теперь остается набрать в командной строке броузера адрес  http://localhost/scripts/ WebJoin.exe и просмотреть результаты работы этого примера.

Пример использования такого подхода можно найти на прилагаемом CD в катало

ге \WebBrok раздела, посвященного настоящей главе.

Источник: Тейксейра, Стив, Пачеко, Ксавье.   Borland Delphi 6. Руководство разработчика. : Пер.  с англ. — М. : Издательский дом “Вильямс”, 2002. —  1120 с. : ил. — Парал. тит. англ.

По теме:

  • Комментарии