DeepEdit!

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

  • Увеличить размер шрифта
  • Размер шрифта по умолчанию
  • Уменьшить размер шрифта

Пример организации хранилища данных на основе библиотеки компонентов kbmMW

       
Автор: Ким Мадсен (Kim Madsen)


Что такое хранилище данных (datawarehouse)? Это большая коллекция данных из разных источников, организованная таким образом, чтобы обеспечить эффективный поиск необходимых данных. Другими словами оно требует такую организацию данных, чтобы облегчить доступ и поиск. Правда, каждое хранилище данных предъявляет собственные минимальные требования к структуре организации данных, которую оно считает приемлемой для своей системы. Данные должны удовлетворять некоторым условиям, для облегчения операции поиска, а это означает, что в некоторых ситуациях они должны быть реорганизованы, чтобы удовлетворять всем требованиям, прежде чем смогут быть добавлены в хранилище данных.
Однако существуют ситуация, когда данные по различным причинам не могут быть реорганизованы, и должны находиться хранилище данных. Например, когда они используются в качестве доказательств по уголовным делам, или когда большие объемы данных делают нецелесообразным их реорганизацию.
В этой статье показано, как можно использовать kbmMW в подобных ситуациях, позволяя пользователям отыскивать многие терабайты информации (общий объем данных постоянно растет), хранящихся в тысячах отдельных базах данных (также постоянно растущих в объеме).
Давайте рассмотрим условную ситуацию при которой организация получает большие (размером в гигабайты) базы данных формата SQLite каждый день из разных мест. Данные, среди прочего, содержат сотни тысяч записей с подробной информацией о том как функционируют технические устройства, кроме этого содержат большое количество кадров видеозаписей. Каждое техническое устройство с течением времени может (а как правило, должно) формировать множество баз данных, каждая из которых содержит результаты одного производственного цикла. При этом есть требование не вносить изменение в структуру этих баз данных. И в компании не заинтересованы в дублировании информации среди различных систем баз данных по ряду причин, одной из которых является безопасность.
Ранее в этой компании в один момент времени обрабатывали один файл базы данных, анализировали его удаленно по сети с использованием инструментов вроде Matlab. Однако, производительность подобных операций была крайне низкой из-за огромного объема данных передаваемых по сети. Это привело к тому, что персонал стал дублировать данные среди локальных баз данных, поскольку это было наиболее простым решением. Но такой подход не является целесообразным по ряду причин:
-        Безопасность...
Конфиденциальные наборы данных нельзя хранить так, как им бы этого хотелось.
-        Безопасность...
Экспертам было позволено изменить некоторые участки базы данных. Однако, если существовали дубликаты данных, нет никакой гарантии, что изменениям подверглись первоначальные источники данных. Ручное изменение в данном случае является ненадежным.
-        Производительность...
Ручное (или полуавтоматическое) копирование данных из многочисленных файлов баз данных в локальное хранилище, требует много времени, дискового пространства и сопряжено с ошибками. Поэтому лучшим решением являются операции поиска. И это то, что составляет действительную ценность библиотеки kbmMW Возможно вам уже известно, что kbmMW используется в качестве промежуточного звена между приложением-сервером и одним или несколькими клиентами. Библиотека позволяет клиентам запрашивать у сервера различные операции и ожидать ответов на эти запросы.
Другой особенностью kbmMW Enterprise Edition является фрэймворк WIB 

(Wide Information Bus - ««Широкая информационная тина»), 

он обеспечивает возможность создания соединения в полностью асинхронном режиме.
При использовании WIB все клиенты и серверы рассматриваются в виде узлов.   Серверы — это узлы, которые публикуют определенную информацию и подписываются на прем запросов, в то время как узлы-клиенты публикуют запросы и подписываются на получение ответов от серверов.
kbmMW v. 3.50.01 Enterprise Edition в настоящее время существует в виде 2-ой бета-версии и скоро должен выйти релиз, который будет предоставлять возможность серверу выдавать инкрементальные ответы на запросы клиентов из базы данных. Это не просто сценарий выборки данных по запросу, мы говорим о том, что клиент запрашивает все больше и больше информации. Сервер выдает наборы записей (как виртуальные так и фактические), которые соответствуют поступающим запросам всякий раз когда данные доступны.
При этом нам необходим некий вид управления соединениями с тысячами база данных. При этом было бы желательно кэшировать соединение с каждой из них для последующего повторного использования. Ведь в конечном итоге хочется иметь клиента, который просто посылает запросы. Примером может служить опрос данных о температуре работы технических устройств. Эти данные могут быть проанализированы множеством способов, описание чего выходит за рамки этой статьи. (В случае нашей компании для этого используются различные сценарии и вектор-ориентированные интегрированные окружения разработки, облегчающие анализ массивов данных, использующих методики вроде тех, что доступны в Matlab, а также позволяющих проигрывать видеопотоки, хранящиеся в базах данных) .
Оставшаяся часть статью посвящена описанию того, как создать приложение такого типа в виде сервера и клиента, используя kbmMW
Создание сервера
Сначала мы рассмотрим процесс создания простого приложения-сервера на основе kbmMW со встроенной службой запросов для доступа к базе данных. Потом мы его немного модифицируем, чтобы получить описанную выше функциональность.
Создайте новое приложение VCL Forms, это будет означать что наш сервер будет запускаться как обычное приложение. В реальности вам скорее всего захочется создать сервер в виде службы, но, поскольку, рассмотрение вопросов создания служб под Windows не входит в задачи этой статьи, я буду краток и в качестве сервера сделаю простое Windows-приложение (в демонстрационных примерах на домашней странице Components4Developers в kbmMW/Downloads/Samples section есть демонстрация того, как создать сервер в виде службы).
Проделайте следующие действия:
Добавьте компонент TkbmMWServer и назовите его «Server»
Добавьте компонент TkbmMWTCPIPIndyMessagingServerTransport, назовите его «Transport». В качестве значения свойства Server выберите компонент Server, а свойству ClusterID присвойте значение «Demo»
Добавьте два компонента очереди сообщений для входящих и исходящих сообщений компонента Transport. Один из них назовите qIn, а другой — qOut. Установите свойство InboundMessageQueue компонента Transport в значение qIn, свойство OutboundMessageQueue - в значение qOut.
Добавьте две кнопки «Listen» и «Don't listen» для включения и отключения нашего сервера.
Форма вашего приложения должна выглядеть следующим образом:

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

Пример организации хранилища данных (продолжение 1)

Добавьте обработчики нажатия на кнопки «Listen» и «Don't Listen»:
procedure TForm1.Button1Click(Sender:  TObject); begin
Transport.Subscriptions.Clear; 

// Подписываемся на посылку запросов этому серверу 

Transport. Subscriptions. Subscribe( kbmMWGenerateRequestSubscriptionSubject Transport. ClusterID));

// Регистрация подписки (и ее отмена) этого сервера 

Transport. Subscriptions. Subscribe(
'SUB.'+Transport.ClusterID+'.>'); Transport. Subscriptions. Subscribe(
'USB.'+Transport.ClusterID+'.>');

// Активация сервера и обмена сообщениями 

Server.Active:=true; end;
procedure   TForm1 .Button2Click (Sender :    TObject); begin
Server Active :=false ; end ;
Для передачи сообщений важное значение играет правильная подписка, в противном случае узел (речь сейчас идет о сервере) не будет получать сообщения. В нашем примере сообщения будут посылать клиенты, поэтому мы должны подписать сервер на прием этих запросов. Мы настроим сервер на прием трех типов сообщений: Запросы (REQ), Подписки (SUB) и отмена подписки (USB). Последнии два, на самом деле, не требуется для нашего примера, но использовать пару SUB и USB является хорошей практикой. В любом случае наш сервер будет принимать только запросы для кластера с именем Demo (устанавливается в свойстве ClusterID компонента Transport).

ХРисунок 3

Теперь мы добавим службу запросов при помощи соответствующего мастера:
Найдите группу Components4Developers Wizards и выберете kbmMW Service Wizard:
Теперь давайте настроим компонент Transport на работу с любыми соединениями. Для этого откройте свойство Bindings в инспекторе объектов и добавьте новое связывание (Ip4)
Появится первая страница мастера. В качестве типа службы выберете Query service/kbmMW_1.0 и нажмите кнопку «Next»
Далее необходимо указать имя службы и ее версию . Версионность может быть полезна в том случае, если вы измените интерфейс службы и захотите поддерживать клиентов обеих версий. Назовите службу «DATAWAREHOUSE», поле номера версии оставьте пустым и нажмите кнопку «Next».

implementation uses Unit2

{$R  *.dfm}

procedure TForm1.Button1Click(Sender:  TObject); begin
Server.Active:=true end;
procedure TForm1.Button2Click(Sender:  TObject); begin
Server.Active:=false; end;
procedure TForm1.FormCreate(Sender:  TObject); begin
Server.RegisterService(TkbmMWQueryService2,false); end;
end.
Для простоты, в нашем примере мы настроим сервер на работу с файлами баз данных со следующими именами: DBl.db, DB2.db, DB3.db.. DBn.db. Клиент будет указывать номер базы данных в которой производится поиск в данный момент времени. Этот пример предполагает наличие таблицы «DATA» с одинаковой структурой в каждой из баз данных. Для каждого файла базы данных нужен свой собственный компонент TkbmMWSQLiteConnectionPool для обеспечения соединений, кэширования результирующих наборов данных, метаданных и др. Таким образом, когда клиент хочет получить доступ к конкретной базе данных, мы можем на лету создать экземпляр класса
TkbmMWSQLiteConnectionPool, соединить его с соответствующей базой данных и обработать запрос клиента. Или мы можем выбрать более подходящие действия, а именно хранить список всех ранее открытых баз данных, что позволит оставлять каждую из них открытой и тем самым быть готовой к использованию. Сейчас вы узнаете как сделать такой, своего рода, пул пулов соединений.
Мы создадим потокобезопасную хэш-таблицу, в которой будут хранится все ранее открытые пулы соединений. Для этого мы будем использовать модуль kbmMWGlobal, в котором содержится много полезных контейнеров и других объектов.

uses
WindowsMessages,  SysUtils    Variants,  Classes,  GraphicsControls,  Forms,  Dialogs,  kbmMWCustomMessagingTransportkbmMWCustomTransport,   kbmMWServerkbmMWCustomServerMessagingTransportStdCtrls,   kbmMWGlobal;
Теперь в класс TForml добавим поле, в котором будет хранится пулы соединений к базам данных, это позволит нам пользоваться пулами в дальнейшем без необходимости повторного открытия файлов базы данных.
procedure Button1Click(Sender:  TObject); procedure Button2Click(Sender:  TObject); procedure FormCreate( Sender:  TObject); private

{ Private declarations } 

public

{ Public declarations }

DBs:TkbmMWThreadHashStringList; end;
Добавим код, необходимый для создания и уничтожения объекта в события создания и уничтожения формы. Определим, что объекты, которые хранятся в списке будут автоматически удалены, когда будут удалены соответствующие записи из списка или когда будет уничтожен сам список.
procedure TForm1.FormCreate(Sender:  TObject); begin
DBs:=TkbmMWThreadHashStringList.Create(100); DBs.FreeObjectsOnDestroy:=true;
Server.RegisterService(TkbmMWQueryService2,false); end;
procedure TForm1 FormDestroy( Sender:  TObject); begin
FreeAndNil(DBs); end;
Теперь добавим два метода для выборки пула соединения с базой данных из списка (по сути - пула с пулами соединений) и автоматического создания нового пула соединения при получении запроса к новой базе данных. Сначала добавьте модуль kbmMWSQLite, который будет использоваться в модуле Unitl, а потом следующий метод.
function TForm1.GetConnectionPool
(ADatabaseName:string):TkbmMWSQLiteConnectionPool; begin
DBs BeginWrite;
try
Result:=TkbmMWSQLiteConnectionPool
DBs. GetObject( ADatabaseName)); if Result = nil then begin
Result:=TkbmMWSQLiteConnectionPool.Create(nil); try
Result.Database:='дирректория базы данных\'
+ADatabaseName+'.db';
Result.Active:=true;
DBs AddManagedObject(ADatabaseName,Result); except
FreeAndNil( Result); raise; end; end; finally
DBs EndWrite; end; end;
Этот метод вначале гарантирует, что доступ к контенту DBs защищается, потому что только один поток в каждый момент времени имеет к нему доступ. Далее ищется пул соединения, основанный на имени базы данных. Если такой пул не удалось найти, мы создаем новый и добавляем его в список DBs. Если запрашиваемую базу данных найти не удалось, данный метод генерирует исключение. Оповещение клиентов для других ситуаций необходимо кодировать аналогичным образом.
Итак, мы определили как будет происходить вызов виртуального набора данных в службе запроса. Для выполнения запросов к любой базе данных будем использовать компонент TkbmMWSQLiteQuery. Есть несколько вариантов взаимодействия с клиентами: можно посылать клиенту весь результирующий набор (комбинацию соответствующих критериям запроса записей из всех указанных клиентом баз данных) за один раз или посылать инкрементальные наборы данных. В этом примере мы будем отправлять все соответствующие записи из базы данных к клиенту в одном инкрементальном наборе.
Поместите компоненты TkbmMWMDQuery и TkbmMWMDConnectionPool в модуль службы запросов (unit2.pas)
Переименуйте компонент kbmMWMDQueryl в «DATA» и установите его свойство публикации в значение True, а в качестве пула соединения укажите компонент kbmMWMDConnectionPooll. Таким образом клиенты смогут запрашивать данные у этого компонента. Набор данных виртуальной памяти (этим объясняется присутствие аббревиатуры MD (от «memory dataset» - прим. пер.) в имени компонента) предоставляет нам несколько интересных событий: OnPerformFieldDefs и OnPerformQuery. OnPerformFieldDefs облегчает определение полей в режиме выполнения программы на лету. Мы можем также определить их и на этапе проектирования в этом примере, поскольку нам известна структура таблицы DATA, которая неизменна для всех баз данных, с которыми работает сервер. Однако, мы определим эти настройки при помощи кода, т.к. клиенты помимо критериев поиска указывают и номер базы данных, для которой предназначается запрос.
procedure TkbmMWQueryService2  DATAPerformFieldDefs Sender:  TObject; AParamsOnly:  Boolean);
var
ds:TkbmMWMDQuery; begin
ds:=TkbmMWMDQuery(Sender);
if not AParamsOnly then begin

II Определяем поля, которые необходимо вернуть клиенту 

ds. FieldDefs. Clear;
ds.FieldDefs.Add('ID',ftString,20,false); ds.FieldDefs.Add('X',ftFloat,0,false); ds.FieldDefs.Add('Y',ftFloat,0,false);
end;
end;
Теперь добавьте параметры в свойство Params компонента DATA
(TkbmMWMDQuery)
^Pf Object Inspector
Создайте три параметра (Имя, тип данных, типа параметра, размерность):
Database, ftString, ptInput, Size 100
ConditionLow, ftFloat, ptInput
ConditionHigh, ftFloat, ptInput
Назначение этих параметров - дать возможность клиентам передавать базе данных число и некоторое условие, чтобы обеспечить возможность выбора данных из базы данных. Для нашего примера мы будем придерживаться условия, что в качестве индикатора базы данных могут использоваться числа, разделенные запятыми, для определения того какой именно запрос необходимо обработать.
Теперь определим SQL выражение, которое будет посылать запрос к одной базе данных. Это можно сделать как в режиме выполнения приложения, так и в режиме его проектирования. Поскольку наш запрос будет одинаков для любой базы данных, давай его создадим в режиме
Теперь необходимо добавить код для фактического поиска и возврата результирующих данных. Этот код необходимо поместить в обработчик события OnPerformQuery.
В первой части события PerformQuery происходит получение значения параметров, переданных с клиента
procedure TkbmMWQueryService2.DATAPerformQuery Sender:  TObject;  var ACanCache,
ACallerMustFree: Boolean; var ADataset: TDataSet); var
i: integer;
sDBID :string;
ds:TkbmMWMDQuery;
sDatabase:string;
slDatabase: TStringList;
conditionLow,conditionHigh:double;
cp: TkbmMWSQLiteConnectionPool;
bFirst: boolean;
mt: TkbmMemTable; begin
ds:=TkbmMWMDQuery( Sender);

II Получаем значения параметров

sDatabase:=ds. ParamByName['Database']. AsString; conditionLow:=ds.ParamByName['conditionLow'].AsFloatconditionHigh:=ds.ParamByName['conditionHigh'].AsFloat;


Далее, когда клиент посылает запрос серверу, для каждой базы данных запускается цикл, где запрашивается пул соединения и выполняется запрос в компоненте TkbmMWSQLite. Результирующий набор данных (если таковой имеется) посылается клиенту в асинхронном режиме при помощи метода SendPartialResultDataset. Методу нужно указать является ли текущая порция данных последней или промежуточной. В набор данных, отправляемых клиенту необходимо включать последнюю порцию данных которая может быть помечена как цельная (если метод SendPartialResultDataset ни разу не вызывался), либо как последняя часть набора данных (если метод SendPartialResultDataset вызывался хотя бы один раз). KbmMW отслеживает это самостоятельно.
Этими действиями мы достигаем двух целей:
1) Серверу нет необходимости посылать полное определение полей в промежуточный или последний набор данных Клиент будет знать необходимо ли еще получить порции данных или все из них уже получены.

conditionHigh:=ds.ParamByName['conditionHigh'].AsFloat

// Получаем список идентификаторов баз данных для осуществления поиска 

slDatabase:=TStringList.Create; try
slDatabase.CommaText:=sDatabase

// Цикл для каждого идентификатора базы данных 

bFirst  := true;
for i:= 0 to slDatabase.Count-1 do begin
sDBID:=slDatabase.Strings [i];

// Давайте проигнорируем некоторые исключение относящиеся к доступу к

II базе данных

try

// Поиск или создание пула соединения к базе данных

cp:=Form1.GetConnectionPool(sDBID); 

// Настроим компонент запроса к базе данных SQLite 

kbmMWSQLiteQuery1.ConnectionPool:=cp; try
kbmMWSQLiteQuery1 ParamByName
['conditionLow'].AsFloat:=conditionLow; kbmMWSQLiteQuery1 ParamByName ['conditionHigh'].AsFloat:=conditionHigh; kbmMWSQLiteQuery1 Open;

// Теперь мы должны получить даные которые необходимо вернуть II клиенту.

// Очень важно, чтобы последний пакет результата был послан kbmMW // автоматически, без использования нами метода SendPartialResultDataset. // Поскольку мы, на самом деле, не знаем, были ли данные в последнем II запросе, то отправим последний пакет пустым.

if kbmMWSQLiteQuery1 RecordCount>0 then begin
SendPartialResultDataset
Form1 Transport,  kbmMWSQLiteQuery1 KBMMW_MESSAGEPRIORITY_NORMAL
bFirst); bFirst:=false;
Последняя часть кода закрывает запрос, отключает от базы пул соединения и генерирует последний пустой набор данных для возврата. Этот пустой набор данных посылается для указания того, что он является последним (поскольку есть вероятность получить пустой или несуществующий набор данных где-то внутри цикла), потому что мы не можем знать обработали ли мы последний набор данных в каждой точке цикла. Также мы указываем системе, что она должна освободить наше временное хранилище TkbmMemTable, когда закончит с ним работать.
bFirst:=false; end; finally
kbmMWSQLiteQuery1 Close;
kbmMWSQLiteQuery1.ConnectionPool   := nil; end; except

// Ничего не делаем

end;
end; finally
slDatabase. Free; end;

// Если не получили данные, вызываем исключение. 

if bFirst then
raise Exception. Create
('Не удалось найти соответствующеи слайды.');

// Теперь подготовим последний (пустой) пакет данных. 

mt:=TkbmMemTable.Create(nil); mt. CreateTableAs ds,[ mtcpoStructure]); mt. Open; ADataset:=mtACallerMustFree:=true; end;

В качестве последних действий необходимо указать службе запросов, что клиенты могут получать доступ к опубликованным компонентам запроса. Это делается путем установки свойства AllowClientNamedQuery в модуле данных службы запросов.
Клиент
Теперь давайте создадим клиента, который будет работать с созданным нами сервером. Для этого создадим новое приложение VCL Forms и добавим следующие компоненты:
TkbmMWTCPIPIndyClientMessagingTransport
2 x TkbmMWMemoryMessageQueue
TkbmMWClientConnectionPool
-        TkbmMWClientQuery
TkbmMWBinaryStreamFormat
Компоненты TDataSource, TDBGrid и две кнопки.
В качестве значения свойства InboundMessageQueue компонента Transport установим ссылку на компонент qIn, а свойства OutboundMessageQueue компонента Transport - ссылку на компонент qOut. Свойству Transport компонента TkbmMWClientConnectionPool укажем ссылку на компонент Transport, а свойству ConnectionPool компонента kbmMWClientQueryl укажем ссылку на компонент kbmMWClientConnectionPool. Далее свойство TransportStreamFormat компонента kbmMWClientQueryl свяжем с kbmMWBinaryStreamFormatl (что соответствует аналогичным настройкам службы запросов в приложении-серевере). Компонент DBGrid свяжем с компонентом Datasource, который связан с компонентом kbmMWClientQueryl, и свойству ClusterID компонента Transport присвоим значение «Demo».
Свойству QueryService компонента kbmMWClientQueryl укажем имя службы на сервере, которая будет предоставлять данные нашему клиенту («DATAWAREHOUSE»). Поскольку мы не стали указывать номер версии службы DATAWAREHOUSE, то свойство QueryServiceVersion нужно оставить пустым.

Рисунок 16: Обработчики нажатия на кнопки «Connect» и «Disconnect»

procedure TForm1 btnConnectClick( Sender:  TObject); begin
kbmMWClientConnectionPool1.Active   := true; end;
procedure TForm1 btnDisconnectClick( Sender:  TObject); begin
kbmMWClientConnectionPool1.Active   := falsekbmMWClientConnectionPool1 KillConnections ; end;
На самом деле подключение не происходит немедленно, вместо этого мы указываем пулу соединений сохранять подключение клиентов с сервером, это позволяет создавать соединения по мере необходимости. Аналогично мы указываем пулу соединений, что он должен разорвать соединение, когда пользователь щелкает по кнопке «Disconnect».

Теперь напишем обработчик нажатия на кнопку, которая будет посылать запросы на сервер:
procedure TForm1.QueryClick(Sender:   TObject); begin

II Мы хотим получить доступ к опубликованному запросу с именем DATA на сервере 

kbmMWClientQuery1.Query.Text: = '@DATA';

II Давайте получим параметры и потом заполним их соответствующей информацией II запроса

kbmMWClientQuery1. FetchDefinitions;

II Установка значений параметров запроса

II Поиск всех баз данных определяющихся значениями 3, 4 и 5

IIПоиск зтчений в интервали от 10 до 20

kbmMWClientQuery1.ParamByName['Database'].AsString:='3,4,5'; kbmMWClientQuery1.ParamByName['ConditionLow'].AsInteger:=10; kbmMWClientQuery1.ParamByName['ConditionHigh'].AsInteger:=20 , kbmMWClientQuery1. AsyncOpen;

II Теперь сервер пройдется по всем указанным базам данных, и произведет поиск II соласноуказанным критериям, и оповестит нас сообщениями о каждом II пустом результате в событии OnAsyncResponse компонента Transport 

end;
И, наконец, давайте добавим код обработки асинхронных ответов, которые будут поступать один или несколько раз после каждого нажатия по кнопке «Query».
procedure TForm1 TransportAsyncResponse( Sender:  TObject; const TransportStream: IkbmMWCustomResponseTransportStream; const RequestID:  Integer;  const Result: Variant;
UserStream:  TkbmMWMemoryStream); var
ps: TkbmMWDatasetPartialState; begin

II Если вы х^отители удостоверится в том, что ответ на запрос осуществляется II компонентом запроса

II сравните свойство LastRequestID этого компонента с параметром RequestID II текущего сообщения

if kbmMWClientQuery1.ActiveClient.LastRequestID
RequestID then
begin
ps   := kbmMWClientQuery1.SetQueryResult
Result, UserStream); case ps of
mwdpsAll        

II Мы получили все данные в одном сообщении. .

II Больше не будет сообщений на этот запрос 

mwdpsInitial:   

II Мы получили сообщение об инициализации и

II теперь набор данных связан со структурой

II полей и некоторыми данными 

mwdpsData :   ;       

II Мы получили промежуточное сообщение-ответ.

II Полученная информация будет добавлена к

II имеющимся данным. 

mwdpsFinal:   ;     

II Мы получили последнее сообщение-ответ на этот

II запрос. Теперь у нас есть весь набор данных.

end;
end;
Теперь, при условии, что у нас есть реальная SQLite база данных и мы правильно настроили kbmMWSQLiteConnectionPool на сервере, у нас есть возможность обрабатывать запросы в асинхронном режиме и возвращать клиентам контролируемые разработчиком растущие наборы данных.
Давайте подведем некоторые итоги. В этой статье мы рассмотрели:
Как создать простейшие приложения сервера и клиента используя kbmMW Enterprise Edition
Как работать с наборами данных в виртуальной памяти
Как работать с базами данных SQLite
Как использовать механизм сообщений Wide Information Bus
(WIB)
*        Как работать с контролируемыми разработчиками растущими результирующими наборами данных.

 


Частные бесплатные объявления в разделе "купить пеноблоки в Санкт-Петербурге" . Трудоустройство за рубежом на Slando . Аренда дома в Подмосковье на Сландо







jAntivirus