Главная » 2009»Март»21 » Использование баз данных Paradox в локальной сети
Использование баз данных Paradox в локальной сети
20:26
Хотелось бы поделиться опытом использования баз данных Paradox в локальной сети. По большому счету, принципы распределенного и локального доступа к таблицам Paradox ничем не отличаются. Исключения составляют некоторые правила и рекомендации, которые изложены ниже...
В последнее время в сторону BDE раздается много замечаний и нареканий по поводу ограниченности и ненадежности при организации сетевого доступа к файлам БД. Существует множество решений и хитростей, призваных облегчить жизнь разработчику, работающему с BDE. Как показывает опыт, такие эксперименты чаще всего приводят к частичной и полной потере данных в файловых таблицах... Отсюда правило первое : если вы хотите использовать Paradox в сети, установите BDE в полном объеме на каждой клиентской машине, включая ту, где находятся файлы базы данных. Обязательны следующие установки в конфигурации на каждой машине : Это предотвратит Sharing Violation : BDE->Configuration->System->Init : LOCAL SHARE = TRUE BDE->{Самое важное} Это предотвратит неразбериху в местоположении данных.
BDE не работает с базами данных напрямую, как например \\head\C\Database. Диск с данными необходимо подключить как сетевой. Еще лучше, если буквы сетевого диска будут одинаковыми на всех машинах... BDE->Configuration-> Drivers-> Native-> Paradox : NETDIR = F:\Databse, BDE->где F = \\head\c\ Возникает резонный вопрос : а что если баз данных несколько? Тогда в качестве NETDIR необходимо указать корневой каталог, в нашем случае F. Если вы работаете с файловыми базами данных, то вам необходимо воспринимать BDE как некоторый сервис, как скажем, interbase. Сравнение конечно не из лучших, однако нормально работающий BDE - залог сохранности и надежности ваших данных.
К вопросу о Refresh. Если вы работаете локально, то никаких проблем с обновлением набора данных у вас не возникает. При изменении данных, вы сразу видите результат на экране. В сети ситуация несколько иная : пользователи на других компьютерах не видят тех изменений, которые происходят без их участия. Конечно, ничто не мешает нам через определенные интервалы времени обновлять элементы управления с помощью Refresh, однако не очень приятно наблюдать за тем, как ваша программа превращается в новогоднюю елку, постоянно мерцая элементами управления. Проблема решается с помощью BDE Callback Functions : // Объявления... TForm1 = class(TForm) // ... public function TableChangeCallBack(CBInfo: Pointer): CBRType; procedure UpdateTableData(var Msg: TMessage); message WM_UPDATETABLE; procedure TablesAfterOpen(DataSet: TDataSet); end;
// Реализация... function TForm1.TableChangeCallBack(CBInfo: Pointer): CBRType; begin PostMessage(Handle, WM_UPDATETABLE, 0, 0); end;
procedure TForm1.UpdateTableData(var Msg: TMessage); Var Index : Integer; begin For Index := 0 To DBDataModule.ComponentCount - 1 Do // DBDataModule - это Data module, в котором находятся все // TTable нашего проекта... If DBDataModule.Components[Index] Is TTable Then If TTable(DBDataModule.Components[Index]).State = dsBrowse then TTable(DBDataModule.Components[Index]).Refresh; end;
procedure TForm1.TablesAfterOpen(DataSet: TDataSet); begin TBDECallback.Create(Self, TTable(DataSet).Handle, cbTableChanged, nil, 0, TableChangeCallBack, True); end; // На событие OnCreate в Data Module // подключаем наши функции к TTable... procedure TDBDataModule.DBDataModuleCreate(Sender: TObject); Var Index : Integer; begin For Index := 0 To ComponentCount - 1 Do If Components[Index] Is TTable Then TTable(Components[Index]).AfterOpen := Form1.TablesAfterOpen; end; Но и это еще не все... Теперь нам нужен TTimer... procedure TForm1.FrashmanTimer(Sender: TObject); Var Index : Integer; begin Try For Index := 0 To DBDataModule.ComponentCount - 1 Do If DBDataModule.Components[Index] Is TTable Then If (TTable(DBDataModule.Components[Index]).Active) And (TTable(DBDataModule.Components[Index]).State = dsBrowse) Then DBIForceReRead(TTable(DBDataModule.Components[Index]).Handle); Except End; end;
Сохранность данных в сети. Для того чтобы быть уверенным в сохранности данных, необходимо на событие AfterPost Компонета TTable назначить следующее : dbiSaveChanges(TTable(DataSet).Handle);
Блокировки. Существует два подхода к разделенному изменению данных : оптимистический и писсимистический. В первом случае речь идет о клиент-сервере и транзакциях. Иначе говоря, сколько угодно пользователей могут одновременно изменять одни и те же данные. В нашем случае такой возможности просто не существует. Необходимо самим предусмотреть ситуацию, когда пользователи, например, попытаются одновременно редактировать одну и ту же запись. Ничего страшного, кроме сообщения "record locked by another user", не произойдет. Однако желательно самим обработать данную ситуацию. Вот пример функции, которая проверяет, заблокирована запись или нет : Function IsRecordLocked(Table: TTable) : Boolean; Var Locked: BOOL; hCur: hDBICur; rslt: DBIResult; begin Table.UpdateCursorPos; Check (DbiIsRecordLocked (Table.Handle, Locked)); Result := Locked; if (Result = False) then begin Check(DbiCloneCursor (Table.Handle, False, False, hCur)); try rslt := DbiGetRecord (hCur, dbiWRITELOCK, nil, nil); if rslt <> DBIERR_NONE then begin if HiByte (rslt) = ERRCAT_LOCKCONFLICT then Result := True else Check (rslt); end else Check (DbiRelRecordLock (hCur, False)); finally Check (DbiCloseCursor (hCur)); end; end; end;