Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2006.02.26;
Скачать: CL | DM;

Вниз

Интерфейс взаимодейстия с базой   Найти похожие ветки 

 
Piter ©   (2006-01-02 23:33) [0]

Нужна организовать для DLL-плагина к программе доступ к SQL-базе. База - Interbase, но это не так важно.

То есть, внутри программы понятно: TDatabase / TQuery - все ясно.

А вот как организовать интерфейс взаимодействия с плагином? Чтобы плагин мог выполнять SQL запросы и получать результаты? Может кто занимался подобным, имеет опыт?

Может что-то типа:

CreateRequest("SQL-выражение") - функция возвращает Handle некоего созданного объекта (ну там ноль в случае неудачи)

Потом всякие:

GetParam(Handle, "ParamName", AsInteger)

А в конце работы нужно закрыть объект:

CloseRequest(Handle);

Соответственно, в программе Handle это может быть чисто физически просто адрес TQuery. При выполнении запроса CreateRequest создается объект TQuery, Handle возвращается плагину. При CloseRequest объект TQuery уничтожается.

TQuery вроде как потоко-безопасный.

Покритикуйте плиз данную схему и как по другому можно?

В данной схеме меня пока смущает частое создание/уничтожение объекта TQuery - при каждом запросе. С другой стороны, вроде ничего страшного, TDataBase один общий будет, так что по скорости проблем особо не должно быть.


 
Piter ©   (2006-01-02 23:37) [1]

Ну а:

CreateRequest, GetParam, CloseRequest - соответственно, просто импортируемые плагином функции


 
Piter ©   (2006-01-02 23:40) [2]

Еще вот что смущает - GetParam же может возвращать разные типы: integer, float, string и т.д.

То есть, в общем случае он возвращает PByte какой-нибудь, а уж плагин должен разобраться на что это ссылка в соответствии с поданым запросом: AsInteger, AsString и т.д.

Или сделать отдельные функции:

GetIntegerParam(Handle, "ParamName")
GetStringParam(Handle, "ParamName")

и т.д.

Вот думаю - как лучше?


 
Sergey Masloff   (2006-01-02 23:43) [3]

Непонятно зачем это. Да и клиентская библиотека InterBase все рвно в очередь построит все запросы, непараллельная она.
 Плагины к разным базам обращаться будут чтоли?


 
Sergey Masloff   (2006-01-02 23:57) [4]

Можно заюзать MIDAS.
Например что-то в таком роде:
1 модуль реализующий IAppServer. Он реализует соединение с БД
   Можно сделать его универсальным то есть он будет получать SQL запрос в текстовом виде, создавать динамически TQuery и провайдера для него и отдавать ссылку на провайдер клиенту. А тот уже штатными средствами ClientDataSet будет работать с данными. Параметры можно передавать через OwnerData
 Можно сделать несколько стандартных интерфейсов - выполнение произвольного SQL выполнение ХП и так далее. Интерфейс наследник IAppServer публикуется и используется всеми другими плагинами дла работы с базой.
 Может конечно и изврат но может и нет. Я особо идею не обдумывал но реализовать так точно можно. И будет пусть и не тривиальное но использование довольно стандартной технологии.


 
Piter ©   (2006-01-03 01:12) [5]

Sergey Masloff   (02.01.06 23:43) [3]

ты не понял. Программа - плагиновая.

Плагин может делать в принципе что ему захочется. Он, конечно, вправе использовать сам какие ему угодно базы данных. НО - зачем?

Хочется с помощью ядра программы предоставить доступ к нормальной базе данных. Более того, вполне возможно, что работа с базой будет осуществляться еще одним плагином. Хочешь - плагин для Interbase, хочешь - плагин для Access или MS SQl, да хоть Oracle.

Для других плагинов все это абсолютно прозрачно. Пусть они просто оперируют простыми SQL выражениями и получают наборы данных. А откуда взяты эти данные - плагин и не знает. Да хоть IB, хоть Oracle, хоть из текстовых файлов.

Проблема не в распараллелизации и подобном. Проблема в описании интерфейса взаимодействия плагина с базой, хранилищем данных.

В Delphi это удобно сделано с помощью TQuery, например. Верно же? Это некая стандартизация доступа к данным. НО. У меня .NET пока не планируется, поэтому действовать на уровне объектов нельзя - плагин может быть написан и на Delphi, и на C++, на чем угодно, что может вызывать WinApi функции и компилировать библиотеки DLL.

То есть, в программе то наверняка будет TDataBase, TQuery, но как предоставить данные плагину? В этом и проблема...

Уф, надеюсь внятно объяснил... Если что - спрашивай.


 
Piter ©   (2006-01-03 01:17) [6]

Кстати, не исключая вариант, что это я тебя не понял, Сергей.

Тогда плиз поподробнее :)

Особенно про:

Sergey Masloff   (02.01.06 23:57) [4]
создавать динамически TQuery и провайдера для него и отдавать ссылку на провайдер клиенту


что значит отдавать ссылку на провайдера? Что это такое?
Что может извлечь плагин, имея ссылку на провайдера. Да и что такое провайдер? :)

P.S. Думаю, и так понятно, что в базах я ламачок :)


 
Piter ©   (2006-01-03 01:19) [7]

Piter ©   (03.01.06 1:12) [5]
В Delphi это удобно сделано с помощью TQuery


ну точнее, наверное, сказать TDataSet, но опять же не суть важно, думаю проблема понятна...


 
Sergey Masloff   (2006-01-03 08:29) [8]

Ну я же дал направление поиска - MIDAS. Все уже давно реализовано. Провайдер посредник между датасетом в серверном модуле (Query там или StoredProcedure) и датасетом в клиентском модуле.
 То есть схема работы такая:

 1) У тебя появляется интерфейс потомок IAppServer в котором тебе придется добавить методы принимающие строку например с текстом SQL и возвращающие провайдер к которому можно подключить ClientDataSet

 2) У тебя появляется плагин реализующий твой потомок IAppServer. ТО есть в нем появляется TREmoteDataModule и методы реализации твоего IAppServer. Базовое приложение (к которому подцепляются все плагины) будет использовать эту реализацию для предоставления ее остальным плагинам - клиентам.  Кстати меняя этот "серверный" плагин можно допустим коннектится действительно не к IB а к ораклу или еще куда

 3) Появляются плагины-клиенты. Им всем передается ссылка на IAppServer которую они дергают для заполнения данными своих TClientDataSet.

 Естественно все это работает только если клиентские плагины дельфийские или C++Builder овские.

 Так что вооружайся справкой и вперед. Ключевые слова multi-tiered database applications. Простейший пример MIDAS есть в каталоге DEMOS. Только там все рассчитано что клиент и сервер на разных машинах поэтому всякие доп. прослойки появляются тебе они не нужны. Просто нужно напрямую ссылки на IAppServer передавать а не парится со всякими SocketServer-ами наверное.

Если есть вопросы общего плана я ответить могу а вото подробности - врядли Delphi у меня нет проверить ничего не могу ;-)


 
Sergey Masloff   (2006-01-03 08:39) [9]

Сейчас в новых (D6 D7) MIDAS по-другому называется. DataSnap вроде.


 
Sergey Masloff   (2006-01-03 10:00) [10]

Еще можно, видимо, обмениваться ADO-шными рекордсетами но там больше придется писать в плане изменений данных. То есть не проблема заполнить рекордсет и отдать его клиенту а вот написать универсальный механизм записи результатов редактирования клиентом рекордсета в базу не так просто.
 Именно если это должен быть универсальный механизм сложно так-то ничего сложного


 
Desdechado ©   (2006-01-03 11:30) [11]

Обязательно при передаче хэндлов (если ты хочешь именно по-своему сделать) помни, что компилироваться твоя прога и плагины должны на ИДЕНТИЧНЫХ версиях, ибо описание класса хранится и в EXE, и в DLL. И может (при разных версиях) получиться так, что обращаясь (в EXE) к TQuery.SQL.Text из плагина ты, на самом деле, обратишься к какому-нить другому свойству, расположенному по этому адресу (из-за несоответствия описания таблицы классов).
Часто это выявляется далеконе сразу, ибо положение свойств классов меняется редко, но вполне возможно. И будешь ловить AV и долго гадать, в чем дело...


 
Piter ©   (2006-01-03 12:51) [12]

Sergey Masloff   (03.01.06 8:29) [8]
Естественно все это работает только если клиентские плагины дельфийские или C++Builder овские


так вот ничего естественного!

Сергей, я же писал:

Piter ©   (03.01.06 1:12) [5]
плагин может быть написан и на Delphi, и на C++, на чем угодно, что может вызывать WinApi функции и компилировать библиотеки DLL.


то есть, фактически НИКАКИХ объектов, так как .NET не планируется.

Desdechado ©   (03.01.06 11:30) [11]
Обязательно при передаче хэндлов (если ты хочешь именно по-своему сделать) помни, что компилироваться твоя прога и плагины должны на ИДЕНТИЧНЫХ версиях


да НЕ БУДЕТ плагин знать о том, что Handle - это ссылка на TQuery. Для плагина это как объект ядра программы. Handle можно и не ссылкой на TQuery делать, а рандомно генерить, просто так удобно и обеспечивается уникальность Handle...


 
Sergey Masloff   (2006-01-03 13:00) [13]

Piter ©   (03.01.06 12:51) [12]
>плагин может быть написан и на Delphi, и на C++,
Да я это просмотрел. Тогда какие к фигам Query? Их просто не существует в це бейсики и вообще нигде кроме борланда.
Тогда можно использовать или рекордсеты ADO (но соответственно ADO нужно) или все данные отдавать в raw виде - ну там массив OLE Variants или самому конструировать строку вида

<rowset name = "custom name">
 <row>
   <col kind="myint" name = "field1">field1value</col>
   <col kind="mystr" name = "field2">field2value</col>
   ....
 </row>
 ....
</rowset>

выделять под нее память и отдавать указатель на нее и размер плагину. А там пусть разбирает как хочет


 
Piter ©   (2006-01-03 13:32) [14]

Sergey Masloff   (03.01.06 13:00) [13]
Тогда какие к фигам Query?


Эх... Сергей!!! Перечитай плиз первые мои два поста!!!

О TQuery я говорил в том смысле - КАК сама работа с базой будет в ядре осуществляться. А плагин то понятное дело ни о каких TQuery и не знает.

Твою идею понял - возвращать весь набор данных в виде аля XML... Но а вдруг запрос вернет тысячи записей??? А если вдруг миллион? На формирование такого списка уйдет безумная куча времени и ресурсов... Мне пока мой способ нравится больще.

Итак, еще раз мой способ.

Это как бы перенос идеи объекта ядра из Windows. То есть, плагин создает объект ядра:

CreateRequest("SQL запрос");

Ему возвращается Handle. Далее используя этот Handle он может оперировать с открытым набором данных, как то:

GetIntegerParam(Handle, "ParamName"): integer

SetPosition(Handle, spNext): boolean

вместо spNext там: spBack, spFirst, spLast.

В САМОМ же движке это может быть наглядно реализовано, например:

function CreateRequest(SQLRequest: PChar): THandle; stdcall;
var
 query: TQuery
 s: string;
begin
 s := SQLRequest;
 query := SQLManager.CreateQuery;
 if query = nil then
   Result := 0
 else
 begin
   query.SQL.add(s);
   query.Open;
   Result := THandle(Pointer(Query));
 end;

Или типа подобного, я сейчас от руки набросал - но думаю понятно.


 
Piter ©   (2006-01-03 13:38) [15]

Возникает пока 1 вопрос по МОЕМУ способу:

хотелось бы универсальной функции: CreateRequest, то есть, которая принимала бы и SELECT запросы, и INSERT, UPDATE и так далее, преобразовывая в вызовы нужных методов TQuery: Open или ExecSQL.


 
Piter ©   (2006-01-03 13:41) [16]

В общем, я так понял мое направление правильное :)
Сергей мыслит бизнес-процессами, поэтому предполагает использование серъезных технологий, а у меня тут не так :)

Думаю, это самое оптимальное - использование TQuery и предоставление доступа к нему как к объекту ядра, то есть плагин просто каждый раз будет передавать Handle и вызывать нужные ему функции по работе с данными.

А в конце вызывается: CloseRequest(Handle) и тогда в ядре программы соответствующий объект TQuery уничатожается...

Если кто что понял - покритикуйте :)


 
Sergey Masloff   (2006-01-03 13:46) [17]

Нет непонятно.
1) при чем тут вообще THandle - дельфийский псевдоним для Integer
2) если запрос вернет несколько тысяч записей ресурсов при использовании T(IB)Query уйдет не меньше а как бы и не больше чем в моем варианте
3) Ты собираешься держать тучу открытых запросов еще и с навигацией по ним. Причем если у тебя плагин отвалится то ты об этом никогда не узнаешь и открытый запрос останется навеки

Вобщем, идея мертвая. ИМХО. Дерзай может что и получится


 
Desdechado ©   (2006-01-03 13:50) [18]

ты для каждого плага собираешься свое множество квериков хранить (т.е. разделять будешь) или общий пул на всех (а как тогда с "чужими" некорректные операции)?


 
Sergey Masloff   (2006-01-03 13:57) [19]

Piter ©   (03.01.06 13:38) [15]
>хотелось бы универсальной функции: CreateRequest, то есть, которая >принимала бы и SELECT запросы, и INSERT, UPDATE и так далее, >преобразовывая в вызовы нужных методов TQuery: Open или ExecSQL
Тут проблем нет есть свойство StatementType или что-то в этом роде возвращает перечислимое значение с типом выражения нк там INSERT или скажем DDL или DELETE


 
Piter ©   (2006-01-03 13:58) [20]

Sergey Masloff   (03.01.06 13:46) [17]
1) при чем тут вообще THandle - дельфийский псевдоним для Integer


ну просто ссылки на объекты ядра обычно имеют в Delphi возвращаемый тип YHandle, вот и все. Например: CreateMutex, CreateSemaphore :)

Можно с таким же успехом просто LongWord использовать

Sergey Masloff   (03.01.06 13:46) [17]
2) если запрос вернет несколько тысяч записей ресурсов при использовании T(IB)Query уйдет не меньше а как бы и не больше чем в моем варианте


хм... почему?

В твоем варианте я считаю кучу времени уйдет на формирование ИЗ набора даных текстовых XML строк. А у меня?

Sergey Masloff   (03.01.06 13:46) [17]
Ты собираешься держать тучу открытых запросов еще и с навигацией по ним


а какие еще варианты? Ведь может быть несколько плагинов, каждый вправе использовать базу данных...

Sergey Masloff   (03.01.06 13:46) [17]
Причем если у тебя плагин отвалится то ты об этом никогда не узнаешь и открытый запрос останется навеки


а что значит плагин отвалится? Выгрузится DLL-библиотека из адресного пространства? Сам себя что ли плагин выгрузит?


 
Piter ©   (2006-01-03 14:04) [21]

Desdechado ©   (03.01.06 13:50) [18]
ты для каждого плага собираешься свое множество квериков хранить


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

Собственно, с другой стороны - один плагин может вполне обойтись одним своим TQuery.

Sergey Masloff   (03.01.06 13:57) [19]
Тут проблем нет есть свойство StatementType или что-то в этом роде возвращает перечислимое значение


это свойство есть у TQuery?

Тогда не в тему, но странно - а зачем в самом TQuery разделили процедуры Open и ExecSQL?


 
Sergey Masloff   (2006-01-03 14:11) [22]

2) Потому что (IB)Query расходует кучу ресурсов на поддержку двунаправленых курсоров. Так что прочитать все через компоненты без этого оверхеда и завернуть все в строковый буфер врядли сильно медленннее. Кроме того тот же IBQuery упадет с EOutOfMemory гораздо раньше чем пойдет речь о десятках тысяч записей.

>Ведь может быть несколько плагинов, каждый вправе использовать базу >данных...
Как обычно спросил->данные получил->отвалился. И так все хоть 100 штук одновременно

>а что значит плагин отвалится?
Да что угодно. Потеряет плагин твой хендл и все. Вариантов масса - от простых ошибок до умелого применения try except<пусто><end>


 
Piter ©   (2006-01-03 14:27) [23]

Sergey Masloff   (03.01.06 14:11) [22]
Да что угодно. Потеряет плагин твой хендл и все


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

Тоже самое, что процесс в Windows может наоткрывать кучу файлов, насоздовать кучу окон - все будет тормозить, но система ничего с этим поделать не в состоянии - а может прада процессу столько нужно?

Так что тут вопрос такой... В конце концов плагин работает в ВАП моего процесса, поэтому при "умелом" программировании он полюбому сможет убить ядро программы, тут как ни крутись...

Sergey Masloff   (03.01.06 14:11) [22]
Как обычно спросил->данные получил->отвалился


Да, тут согласен, в моем варианте ПЛАГИН должен закрывать TQuery, это как недостаток при неумелом программировании, так и достоинство при умелом.

Sergey Masloff   (03.01.06 14:11) [22]
Кроме того тот же IBQuery упадет с EOutOfMemory гораздо раньше чем пойдет речь о десятках тысяч записей


хм... сча потестю :)


 
Piter ©   (2006-01-03 17:22) [24]

Sergey Masloff   (03.01.06 14:11) [22]
Кроме того тот же IBQuery упадет с EOutOfMemory гораздо раньше чем пойдет речь о десятках тысяч записей


сейчас руки дошли.

Итак, в таблице post всего записей: 17610

При этом запрос:

IBQuery1.SQL.Add("Select * from post");

Выполняется быстро и никаких нехваток памяти.


 
Sergey Masloff   (2006-01-03 18:06) [25]

Piter ©   (03.01.06 17:22) [24]
Last() сделал? ;-)
Если нет то и говорить не о чем. По моим (давним) наблюдениям предел размера буфера порядка 5 Мб.


 
Piter ©   (2006-01-03 18:11) [26]

Sergey Masloff   (03.01.06 18:06) [25]

сделал


 
Piter ©   (2006-01-03 18:17) [27]

Sergey Masloff   (03.01.06 18:06) [25]

Last сделал, тестировал на стандартном D7.
Записей в таблице: 17610

Вроде никаких таких тормозов и ошибок памяти.

Единственное что - использовал для тестов Embedded версиб FB, локальную. Но это должно быть пофигу я думаю для TQuery - ему то откуда знать.


 
Piter ©   (2006-01-03 18:19) [28]

Sergey Masloff   (03.01.06 18:06) [25]
По моим (давним) наблюдениям предел размера буфера порядка 5 Мб.


в этом наверное дело, вся база занимает: 11,2 Mb

Там 2 больших таблицы и несколько мелких (очень мелких). Тестируемая таблица самая большая, но видимо не дотягивает...

Тогда надо таблицу на сотню тысяч записей взять... У меня такой под рукой нету :(


 
Piter ©   (2006-01-03 18:20) [29]

генерировать влом, но потом может потестю :)


 
Sergey Masloff   (2006-01-03 18:25) [30]

Ты сделай select * from big_table, small_table
получишь m*n записей ;-)))))
только 2 раза большую не множь а то устанешь ждать
Кстати может в новых IBX подняли размеры? Надо будет посмотреть. Я в 2003 году явление наблюдал - у меня начали клиенты стали сообщать об ошибке при одной из операций, в результате очень долгих тестовых мук было установлено что дело в IBQuery (или IBDataSet) и ограничении его внутр. буфера.



Страницы: 1 вся ветка

Текущий архив: 2006.02.26;
Скачать: CL | DM;

Наверх




Память: 0.57 MB
Время: 0.044 c
15-1139238180
Lexer
2006-02-06 18:03
2006.02.26
On-line сертификация


2-1139653214
13
2006-02-11 13:20
2006.02.26
сохранение в ini


11-1120230729
Dodfr
2005-07-01 19:12
2006.02.26
Known problems with KOL DLL using forms and applet ?


15-1138770992
RustaMAN
2006-02-01 08:16
2006.02.26
расшарить резак???


2-1139215867
G@rik
2006-02-06 11:51
2006.02.26
Что нужно, чтобы работал exe-шник?