Данная лекция позволит изучить способы администрирования баз данных программно, из проекта Delphi. Ведь может случиться, что вы будете писать программу и проектировать базу данных "на заказ", для какой-нибудь небольшой фирмы. Такая фирма, как правило, не может позволить себе иметь в штате профессионального программиста и (или) администратора. В подобных фирмах роль администратора берет на себя один из сотрудников, который является более-менее опытным пользователем. Обучить такого пользователя премудростям и тонкостям работы с утилитой непросто, еще сложнее научить его пользоваться " Delphi. С помощью этих компонентов можно создать простое приложение, которое будет выполнять административные функции с базами данных, и с которым сможет работать любой пользователь.
Итак, наша задача: создать приложение, которое позволит регулярно делать резервную копию базы first.gdb как в ручном, так и в автоматическом режиме. Также приложение, в случае необходимости, должно уметь восстанавливать базу данных из резервной копии, и сохранять лог-файлы о копировании и восстановлении. В случае каких-то проблем, администратор вышлет вам эти логи, по которым вы сможете определить проблему. Также программа должна уметь добавлять новых пользователей, удалять или редактировать старых. Разумеется, пользователя SYSDBA мы удалять не позволим.
Убедимся, что сервер у нас работает, и загружаем Delphi. Выделим форму, в свойство Name которой впишем fMain, а в свойство Caption - "Администрирование базы данных First.gdb ". Сохраним проект в отдельную папку, модулю дадим имя Main, а проекту в целом - AdminIB. Неплохо было бы сразу установить свойство BorderStyle в bsDialog, чтобы программа не могла менять размеры, а свойство Position - в poDesktopCenter.
Переходим на вкладку Win32 PageControl1 в PC1 для краткости кода, а свойству Align присвоим значение alClient.
Щелкаем по компоненту PC1 правой кнопкой и выбираем команду New Page - создалась новая вкладка. Выделим ее, и в свойстве Name вместо TabSheet1 напишем TSh1, а в свойстве Caption - "Резервное
Примерный вид вкладки смотрите на рисунке:
(рис 27.1) Вкладка "Резервное копирование базы данных"Вначале установим на лист панель Panel1, свойству Align которого присвоим значение alTop. Очистим свойство Caption. На панель поместим две радиокнопки RadioButton. В свойству Name первой кнопки укажем RB1, у второй - RB2. А в свойстве Caption, соответственно, "Ручное копирование" и "Автоматическое копирование". Свойство Checked компонента RB2 переведем в True.
Ниже поместим простую кнопку Button, которую переименуем в bStartBackup, а в свойстве Caption напишем "Начать копирование". Свойство Enabled переведем в False. Поскольку по умолчанию, программа будет выполнять автоматическое копирование, эта кнопка будет недоступна. Как только мы выделим радиокнопку "Ручное копирование", сразу вернем доступность этой кнопке.
В правой части панели поместите компонент Label. Чтобы компонент мог содержать многострочный текст, свойство AutoSize переведите в False, а свойство WordWrap - в True. В свойстве Caption напишите текст "Время запуска автоматического резервного копирования:", и подгоните размеры, как на рисунке.
Ниже находятся еще два Label с текстом "Часы:" и "Минуты:". Рядом с ними располагаются два компонента SpinEdit с вкладки Samples SE1, второй - в SE2. У первого в параметре MinValue оставьте 0, в параметре MaxValue укажите 23, а в параметре Value поставьте 1 (по умолчанию, автокопирование будет начинаться в час ночи, когда сотрудников на работе нет). У второго SpinEdit параметры такие: MinValue = 0, MaxValue = 59, Value = 0. Вместо этих компонентов можно было бы использовать один MaskEdit с маской "99:99", но, к сожалению, сам компонент не проверяет правильность ввода значений часов и минут, придется делать это самим. Поэтому остановимся на компонентах SpinEdit, которые позволят пользователю вводить только целые числа в заданном диапазоне.
Под панелью установите компонент Memo. Свойство Align установите в alClient, свойство ScrollBars в ssVertical, и не забудьте очистить от текста свойство Lines.
Далее главное: нужно добавить два не визуальных компонента - Timer с вкладки System и
Компонент . Этот компонент позволяет делать различные настройки резервного копирования, в зависимости от того, какие параметры в свойстве Options включены.
У IBBS. На этом приготовления вкладки закончены, приступим к кодированию. Все резервные копии должны попадать в одну папку, например, C:\DataBases\Backup. Эта папка физически должна существовать на диске. При этом мы не сможем делать копии с одним и тем же именем, ведь операционная система не позволит создавать файл с таким же именем. Конечно, можно перед очередным копированием удалять старую копию, но это тоже не выход - что, если новая резервная копия будет создана с ошибками, то есть, в базе данных появились нарушения? Можно было бы восстановить ее из старой копии, но для этого ее не нужно удалять! Выход: добавлять к имени файла дату и время его создания, тогда можно делать сколько угодно копий, и у всех будут разные имена.
Для этого в разделе Private модуля главной формы опишем функцию GetName:
private
{ Private declarations }
function GetName():String;
Установите курсор на название функции и нажмите <Ctrl+Shift+C>, чтобы сгенерировать саму функцию. Вот ее код:
{Функция возвращает префикс имени файла в виде yyyymmdd_hhmmss_ }
function TfMain.GetName: String;
var
ye, mo, da : Word; //для даты
ho, mi, se, ms : Word; //для времени
st : String[2]; //для добавления нуля, например 05
begin
DecodeDate(Date, ye, mo, da); //декодируем на составные дату
DecodeTime(Time, ho, mi, se, ms); //декодируем время
//теперь собираем строку:
Result:= IntToStr(ye); //добавили год
//получаем и добавляем месяц:
st:= IntToStr(mo);
if Length(st) = 1 then st:= '0' + st; //если 1 символ, добавим спереди 0
Result := Result + st;
//получаем и добавляем день:
st:= IntToStr(da);
if Length(st) = 1 then st:= '0' + st;
Result := Result + st + '_';
//теперь получаем и добавляем час:
st:= IntToStr(ho);
if Length(st) = 1 then st:= '0' + st;
Result := Result + st;
//получаем и добавляем минуты:
st:= IntToStr(mi);
if Length(st) = 1 then st:= '0' + st;
Result := Result + st;
//получаем и добавляем секунды:
st:= IntToStr(se);
if Length(st) = 1 then st:= '0' + st;
Result := Result + st + '_';
//теперь функция вернет префикс имени файла, например:
//20100205_010012_
end;
Здесь мы декодировали на составные части дату и время. Год уже имеет 4 цифры, поэтому его дополнительно обрабатывать не нужно. А вот месяц, день, час, минута или секунда могут состоять из одной цифры. Чтобы не запутаться, делаем проверку:
st:= IntToStr(mo); if Length(st) = 1 then st:= '0' + st; //если 1 символ, добавим спереди 0 Result := Result + st;
В переменную st мы получаем номер месяца. Если этот номер состоит из одной цифры, перед ней добавим '0'. И в конце прибавим результат в переменную Result. Таким же образом мы проверяем и остальные данные. Эта функция гарантирует нам не только уникальность имени файла, но и правильную сортировку файлов по дате. К тому же по имени сразу видно - когда был создан файл.
Теперь сделаем доступной кнопку bStartBackup, если пользователь отметил радиокнопку RB1 (Ручное копирование). Для этого сгенерируем для RB1 событие onClick:
{Щелкнули по "Ручное копирование"}
procedure TfMain.RB1Click(Sender: TObject);
begin
//если отмечено Ручное копирование, делаем
//кнопку доступной:
if RB1.Checked then
bStartBackup.Enabled:= True;
end;
Точно также сделаем кнопку недоступной, если щелкнули по RB2 (Автоматическое копирование). Для этого сгенерируем событие onClick для RB2:
{Щелкнули по "Автоматическое копирование"}
procedure TfMain.RB2Click(Sender: TObject);
begin
//если отмечено Автоматическое копирование, делаем
//кнопку недоступной:
if RB2.Checked then
bStartBackup.Enabled:= False;
end;
Соображаем дальше. Копирование у нас будет начинаться либо по нажатию кнопки, либо по IBBS, вначале объявим ее в разделе private, после функции GetName:
private
{ Private declarations }
function GetName():String;
procedure BackupCopy;
Установите курсор на процедуру и нажмите <Ctrl+Shift+C>, чтобы сгенерировать тело процедуры. Ее код:
{Процедура реализации резервного копирования}
procedure TfMain.BackupCopy;
var s : String; //для получения префикса файла
begin
//получим префикс:
s:= GetName();
//очистим Memo1, если там что то есть:
Memo1.Clear;
//задаем параметры компонента IBBackupService:
IBBS.ServerName := 'MyServ';
IBBS.LoginPrompt:= False;
IBBS.Params.Add('user_name=sysdba');
IBBS.Params.Add('password=masterkey');
IBBS.Active := True;
//начинаем копирование
try
IBBS.Verbose:= True;
IBBS.DatabaseName:= 'c:\DataBases\first.gdb';
IBBS.BackupFile.Clear;
IBBS.BackupFile.Add('c:\DataBases\Backup\' +
s + 'first.gbk');
IBBS.ServiceStart;
//пока не дошли до конца, записываем параллельно лог в Memo1:
while not IBBS.Eof do
Memo1.Lines.Add(IBBS.GetNextLine);
finally
IBBS.Active:= False;
end;
//сохраним лог в файл:
Memo1.Lines.SaveToFile('c:\DataBases\Backup\' +
s + 'first.log');
end;
Здесь мы вначале получили в переменную префикс имени файла. Зачем это нужно? Нам требуется:
Эти действия будут производиться в разное время, поэтому чтобы префикс копии совпадал с префиксом лог-файла, мы и сохраняем его в переменную s:
//получим префикс: s:= GetName();
Далее мы очищаем Memo1, если вдруг там уже был текст. После чего приступаем к настройкам IBBS:
IBBS.ServerName := 'MyServ';
IBBS.LoginPrompt:= False;
IBBS.Params.Add('user_name=sysdba');
IBBS.Params.Add('password=masterkey');
IBBS.Active := True;
Тут все достаточно прозрачно. Поскольку копирование будет производиться на серверном ПК, то будет использован IP -адрес. При этом следует обратить внимание на свойство Protocol. Это свойство позволяет выбрать протокол соединения. Поскольку программа будет загружаться там же, где установлен и где хранится база данных, оставляет значение по умолчанию - Local. Если программа будет работать с удаленным сервером, то здесь нужно будет выбрать один из сетевых протоколов, обычно выбирают TCP.
Далее с помощью SYSDBA и его пароль. Если вы изменили пароль (а это обязательно нужно сделать в реальной системе), вместо " masterkey " укажите свой пароль. В конце мы делаем IBBS активным.
Само копирование мы помещаем в блок try…finally…end, поскольку копирование может быть и неудачным, если БД разрушена:
try
IBBS.Verbose:= True;
IBBS.DatabaseName:= 'c:\DataBases\first.gdb';
IBBS.BackupFile.Clear;
IBBS.BackupFile.Add('c:\DataBases\Backup\' + s + 'first.gbk');
IBBS.ServiceStart;
//пока не дошли до конца, записываем параллельно лог в Memo1:
while not IBBS.Eof do
Memo1.Lines.Add(IBBS.GetNextLine);
finally
IBBS.Active:= False;
end;
Здесь свойство
В свойство
Свойство first.gbk". А затем стартуем копирование методом
Параллельно копированию в компонент Memo1 помещаем отчет о текущем действии, от начала копирования до конца. Метод GetNextLine компонента IBBS неактивным.
И в самом конце процедуры записываем полученный в Memo1 лог в файл с таким же именем, как резервная копия, но расширением *.log:
//сохраним лог в файл:
Memo1.Lines.SaveToFile('c:\DataBases\Backup\' +
s + 'first.log');
Теперь для ручного копирования нам осталось сгенерировать процедуру нажатия на кнопку "Начать копирование", откуда вызовем процедуру BackupCopy:
{Нажали на кнопку "Начать копирование"}
procedure TfMain.bStartBackupClick(Sender: TObject);
begin
BackupCopy;
end;
Попробуйте выполнить ручное копирование. Напомню, что сервер должен быть запущен, а папка " C:\DataBases\Backup " должна физически существовать на диске. Как только вы нажмете на кнопку, копирование начнется. Закончится оно примерно такой строкой:
gbak: closing file, committing and finishing. 38912 bytes written.
Зайдите в папку Backup - там должна появиться пара файлов с одинаковым именем и расширениями *.gbk и *.log. Это и есть наши резервная копия и лог-файл.
Займемся автоматическим копированием. Выделите таймер и сгенерируйте для него событие onTimer, которое будет срабатывать каждую минуту ( Interval =60000):
{Сработал таймер}
procedure TfMain.Timer1Timer(Sender: TObject);
var
ho, mi, se, ms : Word; //для времени
begin
//если включено ручное копирование, просто выходим:
if RB1.Checked then Exit;
//иначе декодируем время
DecodeTime(Time, ho, mi, se, ms); //декодируем время
//если нужный час и нужная минута, начинаем копирование:
if (ho = SE1.Value) and (mi = SE2.Value) then BackupCopy;
end;
Вначале мы проверяем, не включено ли ручное копирование. Если включено, то сразу выходим, ничего не делая. Затем мы снова декодируем время, чтобы посмотреть - совпадают ли данные в компонентах SpinEdit с текущими часом и минутой. И если совпадают, вызываем процедуру BackupCopy.
Заметим, что таймер может сработать не сразу, как системные часы покажут нужное время. Ведь в таймере учитываются и секунды. Значит, если копирование у нас должно начаться в 01:00, а когда программа была запущена, ваше системное время показывало, скажем, 25 секунд, то и таймер сработает в 01:00:25. Что, в принципе, не столь важно.
Сохраните проект, скомпилируйте и запустите. Попробуйте установить время авто-копирования на минуту-другую больше, чем показывают ваши системные часы (должна быть выделена радиокнопка "Автоматическое копирование"). Дождитесь, когда системное время сравняется с указанным. Возможно, придется подождать еще несколько секунд, после чего резервное копирование начнется автоматически. Опять появится пара файлов в папке Backup. Таким образом, достаточно выделить радиокнопку RB2 (у нас она выделена по умолчанию), и не выключать на сервере программу, чтобы обеспечить регулярное резервное , вашей базой данных и администраторской программой должен быть включен в назначенное время (серверные компьютеры обычно работают в режиме 24/7, то есть 24 часа в сутки, 7 дней в неделю).
Напоследок заметим, что компонент
| Переключатель | Описание |
|---|---|
| Игнорировать ошибки контрольных сумм. Применяется обычно при попытке восстановить поврежденную БД. | |
Игнорировать ошибки двухфазного подтверждения limbo -транзакций. Также применяется при восстановлении поврежденной БД. |
|
| При включенном переключателе в резервную копию попадут только метаданные. Можно использовать, если вы хотите получить пустую БД с таблицами, индексами, генераторами, триггерами и проч., но без записей. | |
| Не делать сборку мусора во время резервного копирования. | |
| Делать резервную копию в старом стиле. Как правило, не применяется. | |
Делать резервную копию, непереносимую на другие версии (а также и ). |
|
| Преобразует внешние файлы во внутренние таблицы. Также может применяться при восстановлении поврежденной БД. |
Как видно из таблицы, резервное копирование можно делать с некоторыми параметрами. Мы не использовали этих параметров, то есть, делали копию с параметрами "по умолчанию". В результате получили обычную резервную копию, для чего и нужна наша программа. Если же доведется ремонтировать базу данных, то делать это нужно утилитами и gback, которые предоставляют гораздо больше возможностей, чем указанные переключатели.
Определимся с задачей. Нам нужно:
Windows или файловым менеджером.Для восстановления базы данных сделаем новую вкладку. Выделите компонент PC1 ( PageControl ). Чтобы выделить именно компонент, а не страницу на нем, нужно щелкать правее ярлычка вкладки. Щелкнем по нему правой кнопкой и выберем команду New Page. В свойстве Name новой вкладки вместо TabSheet1 напишем TSh2, а в свойстве Caption - "Восстановление базы". Внешний вид вкладки представлен на рисунке ниже:
(рис 27.2) Вкладка "Восстановление базы"Здесь у нас будет:
InterBase Admin. Компонент предназначен для восстановления базы данных InterBase из резервной копии. Имеет достаточно широкие возможности настроек.Сделаем дизайн вкладки, как на рисунке 27.2. Вначале ставим панель Panel, свойство Align = alTop, свойство Caption очищаем, высоту делаем немного меньше половины страницы.
На панель сверху устанавливаем Label, Edit и Button. В свойстве Caption компонента Label выводим текст "Выберите резервную копию, из которой будем восстанавливать БД:", свойство Text компонента Edit1 очищаем. Кнопку делаем квадратной, размерами под Edit1: Height = Width = 21. В свойстве Caption помещаем "…". Свойство Name переименуем в bOpenBak.
Ниже на панель помещаем панельку GroupBox, в свойстве Caption которой напишем "Параметры восстановления". Внутрь GroupBox1 поместим шесть компонентов CheckBox, которые назовем соответственно CB1 … CB6 (сверху - вниз по первой колонке, и сверху - вниз по второй). В свойстве Caption этих флажков поместим соответственно текст:
"Отключить индексы"
"Отключить
"Отключить проверку внеш.ключей"
"Записывать данные для таблиц отдельно"
"Создать новую БД"
"Заполнить страницы на 100%"
Подгоните размеры компонентов, как на рисунке. У флажка CB5 свойство Checked переводим в True, так как нам по умолчанию нужно создать новую базу данных. Кроме того, включим еще и флажок CB2, чтобы не восстанавливать shadow -файл.
Ниже помещаем Label с текстом "Размер буфера страницы:", а рядом помещаем SpinEdit, свойство Name которого переименуем в SE3. В справочнике Delphi размер буфера страницы у компонента Delphi и по умолчанию установим именно этот размер. Для этого сделаем следующие настройки SE3:
MinValue = 1000 (минимальное значение) MaxValue = 5000 (максимальное значение) Increment = 100 (шаг приращения/убывания) Value = 3000 (текущее значение)
Ниже разместим еще один Label с текстом "Размер страницы:", а правее поместим ComboBox. Переименуем его свойство Name в CBox1, откроем редактор свойства Items и впишем построчно следующие возможные размеры страниц в :
1024 2048 4096 8192
В свойство Text поместим рекомендованное значение 8192.
Правее этих компонентов поместим обычную кнопку, в свойстве Caption которой напишем "Начать восстановление БД", а свойство Enable переведем в False (мы сделаем кнопку доступной, когда пользователь выберет какую-нибудь резервную копию, из которой требуется сделать восстановление). Свойство Name кнопки переименуйте в bStartRestore.
Ниже панели Panel поместим Memo (оставим имя по умолчанию Memo2 ). Свойство Align переведем в alClient, в свойстве ScrollBars выберем ssVertical, чтобы появилась вертикальная прокрутка, а также очистим текст в свойстве Lines.
Займемся не визуальными компонентами. OpenDialog переименуем в OD1. Откроем редактор свойства Filter и в столбец Filter Name впишем "Резервные копии БД", а в столбец Filter - "*.gbk". В свойстве InitialDir укажем папку с нашими резервными копиями "C:\DataBases\Backup". Также в свойстве DefaultExt укажем расширение файлов по умолчанию "gbk" (без точки).
Перейдем к компоненту IBRS. Как видите, этот компонент также имеет сложное раскрывающееся свойство Options, в котором можно настроить следующие переключатели:
| Переключатель | Описание |
|---|---|
| Сделать индексы в базе данных неактивными. | |
| При восстановлении БД не восстанавливать ее |
|
Отключить проверку Foreign Key ). Делать это нужно осторожно, предварительно сделав копию метаданных. |
|
| Восстановить метаданные и данные для каждой таблицы по одной записи за раз. Бывает полезно, если обычное восстановление не получается из-за разрушенных данных или нарушенных |
|
| При значении True можно будет восстанавливать копию поверх существующей базы данных. Крайне не рекомендуется. | |
| При True создается новый файл базы данных. | |
| При восстановлении заполнять страницы базы на 100%, вместо положенных по умолчанию 80%. Чтобы не ухудшить производительность БД, делать это стоит лишь с базами "только для чтения". |
Для шести из семи этих переключателей у нас имеются соответствующие компоненты ComboBox, мы опустили только переключатель
Займемся кодированием. Сгенерируйте процедуру нажатия на кнопку "…". Ее код:
{Щелкнули по кнопке "..."}
procedure TfMain.bOpenBakClick(Sender: TObject);
begin
//если диалог состоялся:
if OD1.Execute then begin
//укажем выбранный файл
Edit1.Text:= OD1.FileName;
//делаем доступной кнопку "Начать восстановление БД"
bStartRestore.Enabled:= True;
end; //if
end;
Остается реализовать само восстановление. Сгенерируйте процедуру нажатия на кнопку "Начать восстановление БД". Ее код:
{Щелкнули по кнопке "Начать восстановление БД"}
procedure TfMain.bStartRestoreClick(Sender: TObject);
var st: String;
begin
//получим префикс для восстановленной БД и лога:
st := GetName();
//Очистим Memo2:
Memo2.Clear;
//устанавливаем начальные параметры:
IBRS.ServerName:= 'MyServ';
IBRS.LoginPrompt:= False;
IBRS.Params.Add('user_name=sysdba');
IBRS.Params.Add('password=masterkey');
IBRS.Active:= True;
//начинаем восстановление
try
IBRS.Verbose:= True; //вести лог
IBRS.Options:= []; //очистим свойство Options
//далее мы включаем в Options переключатель,
//если его ComboBox включен:
if CB1.Checked then IBRS.Options :=
IBRS.Options + [DeactivateIndexes];
if CB2.Checked then IBRS.Options :=
IBRS.Options + [NoShadow];
if CB3.Checked then IBRS.Options :=
IBRS.Options + [NoValidityCheck];
if CB4.Checked then IBRS.Options :=
IBRS.Options + [OneRelationAtATime];
if CB5.Checked then IBRS.Options :=
IBRS.Options + [CreateNewDB];
if CB6.Checked then IBRS.Options :=
IBRS.Options + [UseAllSpace];
//устанавливаем размер буфера страницы:
IBRS.PageBuffers:= SE3.Value;
IBRS.PageSize:= StrToInt(CBox1.Text); //размер самой страницы
//очищаем свойство с именем БД, которая должна получиться:
IBRS.DatabaseName.Clear;
//устанавливаем новое имя с префиксом:
IBRS.DatabaseName.Add('c:\DataBases\Restore\' +
st + 'first.gdb');
//очищаем свойство с именем резервной копии:
IBRS.BackupFile.Clear;
//устанавливаем новое имя из OpenDialog:
IBRS.BackupFile.Add(OD1.FileName);
//стартуем восстановление:
IBRS.ServiceStart;
//параллельно ведем лог, пока не наступит конец восстановления:
while not IBRS.Eof do
Memo2.Lines.Add(IBRS.GetNextLine);
finally
IBRS.Active:= False; //деактивируем компонент
end; //try
//запишем полученный лог в файл:
Memo2.Lines.SaveToFile('c:\DataBases\Restore\' +
st + 'first.log');
//теперь очистим Edit1 и снова сделаем кнопку недоступной:
Edit1.Text:= '';
bStartRestore.Enabled:= False;
end;
Как видно из листинга, работа компонента Delphi в примере работы компонента
Свойство PageSize устанавливает размер страниц восстанавливаемой базы данных. Как уже говорилось в прошлых лекциях, при восстановлении БД из резервной копии можно изменить размер страниц этой БД. Например, если старая версия БД имела размер 4096, то при восстановлении можно установить размер страниц 8192.
Код процедуры подробно комментирован, и в дополнительных пояснениях не нуждается. Сохраните проект, скомпилируйте его и попробуйте сделать восстановление из какой-нибудь резервной копии БД, которую делали ранее. Восстановление закончится, когда в Memo2 появится строка " ", а в папке C:\DataBases\Restore появятся два файла: восстановленная база данных и лог-файл.
Определимся с задачей:
SYSDBA, редактирование запрещаем. Ведь при резервном копировании и восстановлении мы жестко задавали имя SYSDBA и пароль masterkey (если вы изменили этот пароль, значит у вас свой вариант). И если администратор изменит этот пароль, программа не сможет работать, вам придется изменить этот пароль в программе и перекомпилировать ее. Впрочем, в дальнейшем вы можете усложнить программу, добавив в нее, например, работу с ini -файлом (см. лекцию №18 курса "Введение в программирование на Delphi "). В этом случае, вы сможете прописать в ini -файл новый пароль пользователя SYSDBA, если администратор изменит его, и в дальнейшем использовать новый пароль. Если же администратор не будет менять пароля, то можно использовать пароль по умолчанию, " masterkey " (или ваш вариант).SYSDBA.Для реализации этой задачи создадим новую вкладку, которую назовем TSh3, а в свойстве Caption напишем "Пользователи". Внешний вид вкладки показан на следующем рисунке:
(рис 27.3 ) Вкладка "Пользователи"Здесь мы установили компонент ListBox, свойство Name которого переименовали в LB1, а свойство Align перевели в alLeft, чтобы компонент занял всю левую часть окна. Измените размеры компонента, как на рисунке.
В правой части установили три простых кнопки Button. Свойства Name кнопок переименовали в bAddUser, bModifyUser и bDeleteUser, а в свойстве Caption, соответственно, прописали "Добавить", "Редактировать" и "Удалить".
Кроме того, мы добавили не визуальный компонент . Компонент для краткости обращения переименовали в IBSS. Этот компонент предназначен для работы с пользователями, зарегистрированными в , и позволит нам редактировать, добавлять и удалять пользователей.
Несмотря на кажущуюся простоту окна, реализация работы с пользователями немного сложнее предыдущих примеров. В отличие от компонентов first.gdb, а с системной БД ,
что накладывает некоторые ограничения. Например, имя, отчество и фамилию пользователя не получится вносить русскими буквами, так как isc4.gdb создавалась не в кодировке WIN1251. Кроме того, в целях безопасности, компонент
Для доступа к данным пользователя компонент
Первым делом нам нужно при создании главной формы заполнить компонент private добавим новую процедуру ReloadUsers:
private
{ Private declarations }
function GetName():String;
procedure BackupCopy;
procedure ReloadUsers;
Реализация этой процедуры:
{Перечитываем данные о пользователях}
procedure TfMain.ReloadUsers;
var i: Integer; //для счетчика
begin
//вначале очистим ListBox:
LB1.Clear;
//заполняем список пользователями:
IBSS.ServerName:= 'MyServ';
IBSS.LoginPrompt:= False;
IBSS.Params.Add('user_name=sysdba');
IBSS.Params.Add('password=masterkey');
IBSS.Active:= True;
try
IBSS.DisplayUsers; //получаем информацию о пользователях
for i:=0 to IBSS.UserInfoCount -1 do
LB1.Items.Add(IBSS.UserInfo[i].UserName);
finally
IBSS.Active:= False;
end; //try
end;
Здесь все достаточно прозрачно. Вначале очищаем ListBox от возможных старых записей. Затем настраиваем сервис
Метод
LB1.Items.Add(IBSS.UserInfo[i].UserName);
мы добавляем в ListBox имя очередного пользователя. Теперь нужно вызвать эту процедуру при создании формы. Для этого сгенерируйте для главной формы событие OnCreate, в котором сделаем вызов этой процедуры:
ReloadUsers;
Проверить результат можно, скомпилировав и загрузив программу. При загрузке, в окне ListBox должны отобразиться зарегистрированные пользователи.
Далее нам придется сделать новую форму - редактор данных пользователей. Выберите команду File -> New -> Form. Форму назовите fEditor, в свойстве Caption напишите "Редактирование данных пользователя", сохраните новый модуль под именем Editor.
Свойство BorderStyle формы переведем в bsDialog, чтобы пользователь не мог менять размеры окна, а в свойстве Position выберем poMainFormCenter.
Внешний вид новой формы показан на рисунке ниже:
(рис 27.4 ) Форма fEditorИтак, у нас имеется:
В свойстве Caption первого GroupBox напишем "Обязательные данные", а второго - "Дополнительные данные".
На верхний GroupBox помещаем три Label и заполняем текст, как на рисунке. Затем помещаем три Edit. У первого свойство Name переименуем в eUser, у второго - ePass, и у третьего - ePass2.
Выделите одновременно (с нажатой Shift ) компоненты ePass и ePass2. В свойстве PasswordChar укажите символ "*", чтобы скрыть реальный пароль. Не забудьте удалить текст из свойства Text всех компонентов Edit.
На нижний GroupBox также помещаем три Label и заполняем текст, как на рисунке. Затем помещаем три Edit. У первого свойство Name переименуем в eName, у второго - eMiddle, и у третьего - eLast. Очищаем свойство Text у всех Edit.
Ниже расположим две кнопки, которые переименуем соответственно, в bAccept и bCancel, а свойства Caption - как на рисунке.
Теперь немного подумаем. Эта форма будет показываться в двух случаях: при добавлении нового пользователя, и при редактировании существующего. В первом случае пользователь еще не имеет пароля, во втором - имеет. Причем при редактировании существующего пользователя, администратор может как сменить старый пароль, так и не трогать его. Как узнать, менялся ли пароль существующего пользователя, если мы не имеем возможности вывести его с помощью ePass и ePass 2 какой-нибудь длинный пароль "по умолчанию", например, двадцать единичек. А при сохранении результата редактирования будем смотреть - если в ePass пароль "по умолчанию", значит сохранять пароль не нужно. В случае же добавления нового пользователя ePass и ePass2 заполняться не будут.
Пойдем дальше. Как уже говорилось, база данных isc4.gdb не поддерживает кодировку win1251, следовательно, русские буквы в нее помещать нельзя. Значит, при вводе текста во все шесть компонентов Edit придется делать проверку - что вводит пользователь. Если английские буквы или цифры, или BackSpace, то ничего не делаем, иначе запрещаем символ. Чтобы шесть раз не писать один и тот же код, в разделе private создадим функцию KeyCan:
private
{ Private declarations }
function KeyCan(c:Char):Boolean;
Код реализации функции представлен ниже:
{Проверяем допустимость символа}
function TfEditor.KeyCan(c: Char): Boolean;
begin
case c of
'A'..'z': Result:= True; //англ. буквы разрешаем
'0'..'9': Result:= True; //цифры разрешаем
#8 : Result:= True; //BackSpace разрешаем
else Result:= False; //остальное запрещаем
end; //case
end;
Теперь нам нужно реализовать проверку во всех шести Edit. Выделите первый, и сгенерируйте для него событие OnKeyPress. В процедуру поместите только одну строку:
if not KeyCan(Key) then Key:= #0;
То же самое проделайте с оставшимися пятью Edit. Таким образом, мы реализовали проверку на ввод допустимых символов. Если пользователь попытается ввести что-нибудь, кроме английских букв, цифр или BackSpace, то его действия будут игнорироваться.
Пойдем дальше. Если мы редактируем нового пользователя, то в ePass и ePass2 у нас будет текст в двадцать единиц. Если администратор захочет изменить пароль, то он изменит текст в ePass. Значит, для ePass нужно сгенерировать событие OnChange, в котором очищаем текст у ePass2:
ePass2.Text:= '';
Теперь подумаем вот о чем. Окно редактора пользователей мы будем вызывать из главной формы, там же мы будем сохранять изменения, если администратор нажмет кнопку "Подтвердить" в окне редактора. А как из главной формы узнать - хочет ли администратор сохранить изменения, или нет? Введем глобальную переменную izmen в модуль редактора пользователей:
var fEditor: TfEditor; izmen: Boolean;
Теперь для формы редактора пользователей сгенерируем событие OnShow, в котором сразу пропишем:
izmen:= False;
В событии нажатия на кнопку "Подтвердить" вписываем код:
izmen:= True; Close;
А при нажатии на кнопку "Отменить" просто закрываем форму:
Close;
Теперь, если администратор закроет эту форму иначе, чем нажатием на "Подтвердить", мы ничего предпринимать не будем. Осталось сделать проверки на то, ввел ли администратор логин нового пользователя, совпадают ли пароли в ePass и ePass2? Для этого сгенерируем событие OnClose для формы редактора:
{при закрытии формы}
procedure TfEditor.FormClose(Sender: TObject; var Action: TCloseAction);
begin
//если изменения не нужно делать, просто выходим:
if not izmen then Exit;
//Иначе делаем проверку. Если нет имени пользователя:
if eUser.Text = '' then begin
ShowMessage('Введите имя пользователя!');
Action := caNone; //запрещаем покидать форму
eUser.SetFocus; //переводим фокус на имя пользователя
end;
//Если пароль не '11111111111111111111', делаем проверку
//совпадает ли ePass и ePass2:
if ePass.Text <> '11111111111111111111' then
if ePass.Text <> ePass2.Text then begin
ShowMessage('Введите правильный пароль!');
Action := caNone; //запрещаем покидать форму
//очистим ePass и ePass2:
ePass.Text:= '';
ePass2.Text:= '';
ePass.SetFocus; //переводим фокус на имя пользователя
end;
end;
Комментарии достаточно подробны, чтобы вы смогли разобраться с кодом.
С этой формой закончили, вернемся на главную форму. Не забудем сразу же командой File -> Use Unit подключить к ней модуль Editor.
Реализуем добавление нового пользователя. Сгенерируйте обработчик нажатия на кнопку "Добавить". Код обработчика следующий:
{Нажали "Добавить"}
procedure TfMain.bAddUserClick(Sender: TObject);
begin
//вначале очистим данные, которые могут быть
//в редакторе пользователей:
fEditor.eUser.Text:= '';
fEditor.ePass.Text:= '';
fEditor.ePass2.Text:= '';
fEditor.eName.Text:= '';
fEditor.eMiddle.Text:= '';
fEditor.eLast.Text:= '';
//теперь показываем форму:
fEditor.ShowModal;
//если изменений делать не нужно, просто выходим:
if not Editor.izmen then Exit;
//иначе готовим компонент IBSS к созданию нового пользователя.
IBSS.Active:= True;
try
//теперь вводим данные нового пользователя:
IBSS.UserName:= fEditor.eUser.Text;
IBSS.Password:= fEditor.ePass.Text;
IBSS.FirstName:= fEditor.eName.Text;
IBSS.MiddleName:= fEditor.eMiddle.Text;
IBSS.LastName:= fEditor.eLast.Text;
//вызываем метод AddUser, который добавляет пользователя:
IBSS.AddUser;
finally
IBSS.Active:= False; //закрываем компонент
end; //try
//перечитываем информацию о пользователях:
ReloadUsers;
end;
Поскольку пользователь у нас новый, данных на него пока никаких нет. Значит, вначале мы очистим все Edit на форме редактора, и только потом покажем ее.
Когда администратор закончит работу с формой, и закроет ее, начинает работу следующая строка процедуры:
//если изменений делать не нужно, просто выходим: if not Editor.izmen then Exit;
То есть, если администратор не нажал кнопку "Подтвердить", мы просто выйдем, ничего не делая. Если же процедура работает дальше, то нужно сохранить нового пользователя:
IBSS.Active:= True;
try
//теперь вводим данные нового пользователя:
IBSS.UserName:= fEditor.eUser.Text;
IBSS.Password:= fEditor.ePass.Text;
IBSS.FirstName:= fEditor.eName.Text;
IBSS.MiddleName:= fEditor.eMiddle.Text;
IBSS.LastName:= fEditor.eLast.Text;
//вызываем метод AddUser, который добавляет пользователя:
IBSS.AddUser;
…
Как видите, при сохранении данных мы используем свойства компонента ReloadUsers для обновления данных.
Сохраните проект, скомпилируйте его и попробуйте добавить нового пользователя. Пользователь должен появиться в окне ListBox, кроме того, он должен быть виден и в утилите , в разделе Users дерева серверов.
Редактирование выбранного пользователя вызывается кнопкой "Редактировать". Сгенерируйте этот обработчик. Вот его код:
{Нажали "Редактировать"}
procedure TfMain.bModifyUserClick(Sender: TObject);
var i: Integer; //счетчик
begin
//если не один пользователь не выбран, просто выходим:
if LB1.ItemIndex = -1 then begin
ShowMessage('Выберите пользователя!');
Exit;
end
//если выбран SYSDBA, тоже выходим:
else if LB1.Items[LB1.ItemIndex] = 'SYSDBA' then begin
ShowMessage('Редактировать пользователя SYSDBA нельзя.');
Exit;
end; //else if
//иначе редактируем
//установим у IBSecurityService выбранного пользователя:
IBSS.Active:= True;
IBSS.UserName:= LB1.Items[LB1.ItemIndex];
//найдем в IBSS индекс нужного пользователя:
for i:= 0 to IBSS.UserInfoCount - 1 do
if IBSS.UserInfo[i].UserName =
LB1.Items[LB1.ItemIndex] then break;
//теперь i содержит индекс пользователя
//заполняем редактор пользователей данными:
fEditor.eUser.Text:= IBSS.UserInfo[i].UserName;;
//раз пользователь уже есть, выводим единички в качестве пароля:
fEditor.ePass.Text:= '11111111111111111111';
fEditor.ePass2.Text:= '11111111111111111111';
fEditor.eName.Text:= IBSS.UserInfo[i].FirstName;
fEditor.eMiddle.Text:= IBSS.UserInfo[i].MiddleName;
fEditor.eLast.Text:= IBSS.UserInfo[i].LastName;
//теперь покажем редактор:
fEditor.ShowModal;
//если изменений делать не нужно, просто выходим:
if not Editor.izmen then Exit;
//иначе сохраняем их:
try
//Имя пользователя менять не будем.
//Сохраним пароль, если там не единички:
if fEditor.ePass.Text <> '11111111111111111111' then
IBSS.Password:= fEditor.ePass.Text;
//Далее сохраняем остальные параметры:
IBSS.FirstName:= fEditor.eName.Text;
IBSS.MiddleName:= fEditor.eMiddle.Text;
IBSS.LastName:= fEditor.eLast.Text;
//сохраняем изменения физически методом ModifyUser:
IBSS.ModifyUser;
finally
IBSS.Active:= False;
end; //try
//перечитываем информацию о пользователях:
ReloadUsers;
end;
Подробно разбирать весь код не будем, так как комментариев достаточно, но остановимся на некоторых моментах. Строка
IBSS.UserName:= LB1.Items[LB1.ItemIndex];
делает в компоненте IBSS текущим пользователем того, который в данный момент выбран в списке ListBox. Затем мы находим индекс этого пользователя в списке. Зачем это нужно? Дело в том, что данные о пользователе можно вытащить только через свойство должны отобразиться эти данные.
Это самая простая операция с
{Удаление пользователя}
procedure TfMain.bDeleteUserClick(Sender: TObject);
var s: String; //для формирования строки
begin
//если не один пользователь не выбран, просто выходим:
if LB1.ItemIndex = -1 then begin
ShowMessage('Выберите пользователя!');
Exit;
end;
//иначе продолжаем. формируем запрос:
s:= 'Вы действительно желаете удалить пользователя ' +
LB1.Items[LB1.ItemIndex] + '?';
//попросим подтверждения. выходим, если не подтвердили:
if Application.MessageBox(PChar(s), 'Удаление пользователя',
MB_YESNOCANCEL+MB_ICONQUESTION) <> IDYES then Exit;
//если не вышли, значит удаляем пользователя
IBSS.Active:= True;
try
//делаем пользователя текущим:
IBSS.UserName:= LB1.Items[LB1.ItemIndex];
//удаляем его методом DeleteUser:
IBSS.DeleteUser;
finally
IBSS.Active:= False;
end; //try
//перечитываем информацию о пользователях:
ReloadUsers;
end;
Тут все почти так же, как в предыдущих примерах, за исключением нового метода
Вкладка Delphi по компонентам.
Данная лекция позволит изучить способы администрирования баз данных программно, из проекта Delphi. Ведь может случиться, что вы будете писать программу и проектировать базу данных "на заказ", для какой-нибудь небольшой фирмы. Такая фирма, как правило, не может позволить себе иметь в штате профессионального программиста и (или) администратора. В подобных фирмах роль администратора берет на себя один из сотрудников, который является более-менее опытным пользователем. Обучить такого пользователя премудростям и тонкостям работы с утилитой непросто, еще сложнее научить его пользоваться " Delphi. С помощью этих компонентов можно создать простое приложение, которое будет выполнять административные функции с базами данных, и с которым сможет работать любой пользователь.
Итак, наша задача: создать приложение, которое позволит регулярно делать резервную копию базы first.gdb как в ручном, так и в автоматическом режиме. Также приложение, в случае необходимости, должно уметь восстанавливать базу данных из резервной копии, и сохранять лог-файлы о копировании и восстановлении. В случае каких-то проблем, администратор вышлет вам эти логи, по которым вы сможете определить проблему. Также программа должна уметь добавлять новых пользователей, удалять или редактировать старых. Разумеется, пользователя SYSDBA мы удалять не позволим.
Убедимся, что сервер у нас работает, и загружаем Delphi. Выделим форму, в свойство Name которой впишем fMain, а в свойство Caption - "Администрирование базы данных First.gdb ". Сохраним проект в отдельную папку, модулю дадим имя Main, а проекту в целом - AdminIB. Неплохо было бы сразу установить свойство BorderStyle в bsDialog, чтобы программа не могла менять размеры, а свойство Position - в poDesktopCenter.
Переходим на вкладку Win32 PageControl1 в PC1 для краткости кода, а свойству Align присвоим значение alClient.
Щелкаем по компоненту PC1 правой кнопкой и выбираем команду New Page - создалась новая вкладка. Выделим ее, и в свойстве Name вместо TabSheet1 напишем TSh1, а в свойстве Caption - "Резервное
Примерный вид вкладки смотрите на рисунке:
(рис 27.1) Вкладка "Резервное копирование базы данных"Вначале установим на лист панель Panel1, свойству Align которого присвоим значение alTop. Очистим свойство Caption. На панель поместим две радиокнопки RadioButton. В свойству Name первой кнопки укажем RB1, у второй - RB2. А в свойстве Caption, соответственно, "Ручное копирование" и "Автоматическое копирование". Свойство Checked компонента RB2 переведем в True.
Ниже поместим простую кнопку Button, которую переименуем в bStartBackup, а в свойстве Caption напишем "Начать копирование". Свойство Enabled переведем в False. Поскольку по умолчанию, программа будет выполнять автоматическое копирование, эта кнопка будет недоступна. Как только мы выделим радиокнопку "Ручное копирование", сразу вернем доступность этой кнопке.
В правой части панели поместите компонент Label. Чтобы компонент мог содержать многострочный текст, свойство AutoSize переведите в False, а свойство WordWrap - в True. В свойстве Caption напишите текст "Время запуска автоматического резервного копирования:", и подгоните размеры, как на рисунке.
Ниже находятся еще два Label с текстом "Часы:" и "Минуты:". Рядом с ними располагаются два компонента SpinEdit с вкладки Samples SE1, второй - в SE2. У первого в параметре MinValue оставьте 0, в параметре MaxValue укажите 23, а в параметре Value поставьте 1 (по умолчанию, автокопирование будет начинаться в час ночи, когда сотрудников на работе нет). У второго SpinEdit параметры такие: MinValue = 0, MaxValue = 59, Value = 0. Вместо этих компонентов можно было бы использовать один MaskEdit с маской "99:99", но, к сожалению, сам компонент не проверяет правильность ввода значений часов и минут, придется делать это самим. Поэтому остановимся на компонентах SpinEdit, которые позволят пользователю вводить только целые числа в заданном диапазоне.
Под панелью установите компонент Memo. Свойство Align установите в alClient, свойство ScrollBars в ssVertical, и не забудьте очистить от текста свойство Lines.
Далее главное: нужно добавить два не визуальных компонента - Timer с вкладки System и
Компонент . Этот компонент позволяет делать различные настройки резервного копирования, в зависимости от того, какие параметры в свойстве Options включены.
У IBBS. На этом приготовления вкладки закончены, приступим к кодированию. Все резервные копии должны попадать в одну папку, например, C:\DataBases\Backup. Эта папка физически должна существовать на диске. При этом мы не сможем делать копии с одним и тем же именем, ведь операционная система не позволит создавать файл с таким же именем. Конечно, можно перед очередным копированием удалять старую копию, но это тоже не выход - что, если новая резервная копия будет создана с ошибками, то есть, в базе данных появились нарушения? Можно было бы восстановить ее из старой копии, но для этого ее не нужно удалять! Выход: добавлять к имени файла дату и время его создания, тогда можно делать сколько угодно копий, и у всех будут разные имена.
Для этого в разделе Private модуля главной формы опишем функцию GetName:
private
{ Private declarations }
function GetName():String;
Установите курсор на название функции и нажмите <Ctrl+Shift+C>, чтобы сгенерировать саму функцию. Вот ее код:
{Функция возвращает префикс имени файла в виде yyyymmdd_hhmmss_ }
function TfMain.GetName: String;
var
ye, mo, da : Word; //для даты
ho, mi, se, ms : Word; //для времени
st : String[2]; //для добавления нуля, например 05
begin
DecodeDate(Date, ye, mo, da); //декодируем на составные дату
DecodeTime(Time, ho, mi, se, ms); //декодируем время
//теперь собираем строку:
Result:= IntToStr(ye); //добавили год
//получаем и добавляем месяц:
st:= IntToStr(mo);
if Length(st) = 1 then st:= '0' + st; //если 1 символ, добавим спереди 0
Result := Result + st;
//получаем и добавляем день:
st:= IntToStr(da);
if Length(st) = 1 then st:= '0' + st;
Result := Result + st + '_';
//теперь получаем и добавляем час:
st:= IntToStr(ho);
if Length(st) = 1 then st:= '0' + st;
Result := Result + st;
//получаем и добавляем минуты:
st:= IntToStr(mi);
if Length(st) = 1 then st:= '0' + st;
Result := Result + st;
//получаем и добавляем секунды:
st:= IntToStr(se);
if Length(st) = 1 then st:= '0' + st;
Result := Result + st + '_';
//теперь функция вернет префикс имени файла, например:
//20100205_010012_
end;
Здесь мы декодировали на составные части дату и время. Год уже имеет 4 цифры, поэтому его дополнительно обрабатывать не нужно. А вот месяц, день, час, минута или секунда могут состоять из одной цифры. Чтобы не запутаться, делаем проверку:
st:= IntToStr(mo); if Length(st) = 1 then st:= '0' + st; //если 1 символ, добавим спереди 0 Result := Result + st;
В переменную st мы получаем номер месяца. Если этот номер состоит из одной цифры, перед ней добавим '0'. И в конце прибавим результат в переменную Result. Таким же образом мы проверяем и остальные данные. Эта функция гарантирует нам не только уникальность имени файла, но и правильную сортировку файлов по дате. К тому же по имени сразу видно - когда был создан файл.
Теперь сделаем доступной кнопку bStartBackup, если пользователь отметил радиокнопку RB1 (Ручное копирование). Для этого сгенерируем для RB1 событие onClick:
{Щелкнули по "Ручное копирование"}
procedure TfMain.RB1Click(Sender: TObject);
begin
//если отмечено Ручное копирование, делаем
//кнопку доступной:
if RB1.Checked then
bStartBackup.Enabled:= True;
end;
Точно также сделаем кнопку недоступной, если щелкнули по RB2 (Автоматическое копирование). Для этого сгенерируем событие onClick для RB2:
{Щелкнули по "Автоматическое копирование"}
procedure TfMain.RB2Click(Sender: TObject);
begin
//если отмечено Автоматическое копирование, делаем
//кнопку недоступной:
if RB2.Checked then
bStartBackup.Enabled:= False;
end;
Соображаем дальше. Копирование у нас будет начинаться либо по нажатию кнопки, либо по IBBS, вначале объявим ее в разделе private, после функции GetName:
private
{ Private declarations }
function GetName():String;
procedure BackupCopy;
Установите курсор на процедуру и нажмите <Ctrl+Shift+C>, чтобы сгенерировать тело процедуры. Ее код:
{Процедура реализации резервного копирования}
procedure TfMain.BackupCopy;
var s : String; //для получения префикса файла
begin
//получим префикс:
s:= GetName();
//очистим Memo1, если там что то есть:
Memo1.Clear;
//задаем параметры компонента IBBackupService:
IBBS.ServerName := 'MyServ';
IBBS.LoginPrompt:= False;
IBBS.Params.Add('user_name=sysdba');
IBBS.Params.Add('password=masterkey');
IBBS.Active := True;
//начинаем копирование
try
IBBS.Verbose:= True;
IBBS.DatabaseName:= 'c:\DataBases\first.gdb';
IBBS.BackupFile.Clear;
IBBS.BackupFile.Add('c:\DataBases\Backup\' +
s + 'first.gbk');
IBBS.ServiceStart;
//пока не дошли до конца, записываем параллельно лог в Memo1:
while not IBBS.Eof do
Memo1.Lines.Add(IBBS.GetNextLine);
finally
IBBS.Active:= False;
end;
//сохраним лог в файл:
Memo1.Lines.SaveToFile('c:\DataBases\Backup\' +
s + 'first.log');
end;
Здесь мы вначале получили в переменную префикс имени файла. Зачем это нужно? Нам требуется:
Эти действия будут производиться в разное время, поэтому чтобы префикс копии совпадал с префиксом лог-файла, мы и сохраняем его в переменную s:
//получим префикс: s:= GetName();
Далее мы очищаем Memo1, если вдруг там уже был текст. После чего приступаем к настройкам IBBS:
IBBS.ServerName := 'MyServ';
IBBS.LoginPrompt:= False;
IBBS.Params.Add('user_name=sysdba');
IBBS.Params.Add('password=masterkey');
IBBS.Active := True;
Тут все достаточно прозрачно. Поскольку копирование будет производиться на серверном ПК, то будет использован IP -адрес. При этом следует обратить внимание на свойство Protocol. Это свойство позволяет выбрать протокол соединения. Поскольку программа будет загружаться там же, где установлен и где хранится база данных, оставляет значение по умолчанию - Local. Если программа будет работать с удаленным сервером, то здесь нужно будет выбрать один из сетевых протоколов, обычно выбирают TCP.
Далее с помощью SYSDBA и его пароль. Если вы изменили пароль (а это обязательно нужно сделать в реальной системе), вместо " masterkey " укажите свой пароль. В конце мы делаем IBBS активным.
Само копирование мы помещаем в блок try…finally…end, поскольку копирование может быть и неудачным, если БД разрушена:
try
IBBS.Verbose:= True;
IBBS.DatabaseName:= 'c:\DataBases\first.gdb';
IBBS.BackupFile.Clear;
IBBS.BackupFile.Add('c:\DataBases\Backup\' + s + 'first.gbk');
IBBS.ServiceStart;
//пока не дошли до конца, записываем параллельно лог в Memo1:
while not IBBS.Eof do
Memo1.Lines.Add(IBBS.GetNextLine);
finally
IBBS.Active:= False;
end;
Здесь свойство
В свойство
Свойство first.gbk". А затем стартуем копирование методом
Параллельно копированию в компонент Memo1 помещаем отчет о текущем действии, от начала копирования до конца. Метод GetNextLine компонента IBBS неактивным.
И в самом конце процедуры записываем полученный в Memo1 лог в файл с таким же именем, как резервная копия, но расширением *.log:
//сохраним лог в файл:
Memo1.Lines.SaveToFile('c:\DataBases\Backup\' +
s + 'first.log');
Теперь для ручного копирования нам осталось сгенерировать процедуру нажатия на кнопку "Начать копирование", откуда вызовем процедуру BackupCopy:
{Нажали на кнопку "Начать копирование"}
procedure TfMain.bStartBackupClick(Sender: TObject);
begin
BackupCopy;
end;
Попробуйте выполнить ручное копирование. Напомню, что сервер должен быть запущен, а папка " C:\DataBases\Backup " должна физически существовать на диске. Как только вы нажмете на кнопку, копирование начнется. Закончится оно примерно такой строкой:
gbak: closing file, committing and finishing. 38912 bytes written.
Зайдите в папку Backup - там должна появиться пара файлов с одинаковым именем и расширениями *.gbk и *.log. Это и есть наши резервная копия и лог-файл.
Займемся автоматическим копированием. Выделите таймер и сгенерируйте для него событие onTimer, которое будет срабатывать каждую минуту ( Interval =60000):
{Сработал таймер}
procedure TfMain.Timer1Timer(Sender: TObject);
var
ho, mi, se, ms : Word; //для времени
begin
//если включено ручное копирование, просто выходим:
if RB1.Checked then Exit;
//иначе декодируем время
DecodeTime(Time, ho, mi, se, ms); //декодируем время
//если нужный час и нужная минута, начинаем копирование:
if (ho = SE1.Value) and (mi = SE2.Value) then BackupCopy;
end;
Вначале мы проверяем, не включено ли ручное копирование. Если включено, то сразу выходим, ничего не делая. Затем мы снова декодируем время, чтобы посмотреть - совпадают ли данные в компонентах SpinEdit с текущими часом и минутой. И если совпадают, вызываем процедуру BackupCopy.
Заметим, что таймер может сработать не сразу, как системные часы покажут нужное время. Ведь в таймере учитываются и секунды. Значит, если копирование у нас должно начаться в 01:00, а когда программа была запущена, ваше системное время показывало, скажем, 25 секунд, то и таймер сработает в 01:00:25. Что, в принципе, не столь важно.
Сохраните проект, скомпилируйте и запустите. Попробуйте установить время авто-копирования на минуту-другую больше, чем показывают ваши системные часы (должна быть выделена радиокнопка "Автоматическое копирование"). Дождитесь, когда системное время сравняется с указанным. Возможно, придется подождать еще несколько секунд, после чего резервное копирование начнется автоматически. Опять появится пара файлов в папке Backup. Таким образом, достаточно выделить радиокнопку RB2 (у нас она выделена по умолчанию), и не выключать на сервере программу, чтобы обеспечить регулярное резервное , вашей базой данных и администраторской программой должен быть включен в назначенное время (серверные компьютеры обычно работают в режиме 24/7, то есть 24 часа в сутки, 7 дней в неделю).
Напоследок заметим, что компонент
| Переключатель | Описание |
|---|---|
| Игнорировать ошибки контрольных сумм. Применяется обычно при попытке восстановить поврежденную БД. | |
Игнорировать ошибки двухфазного подтверждения limbo -транзакций. Также применяется при восстановлении поврежденной БД. |
|
| При включенном переключателе в резервную копию попадут только метаданные. Можно использовать, если вы хотите получить пустую БД с таблицами, индексами, генераторами, триггерами и проч., но без записей. | |
| Не делать сборку мусора во время резервного копирования. | |
| Делать резервную копию в старом стиле. Как правило, не применяется. | |
Делать резервную копию, непереносимую на другие версии (а также и ). |
|
| Преобразует внешние файлы во внутренние таблицы. Также может применяться при восстановлении поврежденной БД. |
Как видно из таблицы, резервное копирование можно делать с некоторыми параметрами. Мы не использовали этих параметров, то есть, делали копию с параметрами "по умолчанию". В результате получили обычную резервную копию, для чего и нужна наша программа. Если же доведется ремонтировать базу данных, то делать это нужно утилитами и gback, которые предоставляют гораздо больше возможностей, чем указанные переключатели.
Определимся с задачей. Нам нужно:
Windows или файловым менеджером.Для восстановления базы данных сделаем новую вкладку. Выделите компонент PC1 ( PageControl ). Чтобы выделить именно компонент, а не страницу на нем, нужно щелкать правее ярлычка вкладки. Щелкнем по нему правой кнопкой и выберем команду New Page. В свойстве Name новой вкладки вместо TabSheet1 напишем TSh2, а в свойстве Caption - "Восстановление базы". Внешний вид вкладки представлен на рисунке ниже:
(рис 27.2) Вкладка "Восстановление базы"Здесь у нас будет:
InterBase Admin. Компонент предназначен для восстановления базы данных InterBase из резервной копии. Имеет достаточно широкие возможности настроек.Сделаем дизайн вкладки, как на рисунке 27.2. Вначале ставим панель Panel, свойство Align = alTop, свойство Caption очищаем, высоту делаем немного меньше половины страницы.
На панель сверху устанавливаем Label, Edit и Button. В свойстве Caption компонента Label выводим текст "Выберите резервную копию, из которой будем восстанавливать БД:", свойство Text компонента Edit1 очищаем. Кнопку делаем квадратной, размерами под Edit1: Height = Width = 21. В свойстве Caption помещаем "…". Свойство Name переименуем в bOpenBak.
Ниже на панель помещаем панельку GroupBox, в свойстве Caption которой напишем "Параметры восстановления". Внутрь GroupBox1 поместим шесть компонентов CheckBox, которые назовем соответственно CB1 … CB6 (сверху - вниз по первой колонке, и сверху - вниз по второй). В свойстве Caption этих флажков поместим соответственно текст:
"Отключить индексы"
"Отключить
"Отключить проверку внеш.ключей"
"Записывать данные для таблиц отдельно"
"Создать новую БД"
"Заполнить страницы на 100%"
Подгоните размеры компонентов, как на рисунке. У флажка CB5 свойство Checked переводим в True, так как нам по умолчанию нужно создать новую базу данных. Кроме того, включим еще и флажок CB2, чтобы не восстанавливать shadow -файл.
Ниже помещаем Label с текстом "Размер буфера страницы:", а рядом помещаем SpinEdit, свойство Name которого переименуем в SE3. В справочнике Delphi размер буфера страницы у компонента Delphi и по умолчанию установим именно этот размер. Для этого сделаем следующие настройки SE3:
MinValue = 1000 (минимальное значение) MaxValue = 5000 (максимальное значение) Increment = 100 (шаг приращения/убывания) Value = 3000 (текущее значение)
Ниже разместим еще один Label с текстом "Размер страницы:", а правее поместим ComboBox. Переименуем его свойство Name в CBox1, откроем редактор свойства Items и впишем построчно следующие возможные размеры страниц в :
1024 2048 4096 8192
В свойство Text поместим рекомендованное значение 8192.
Правее этих компонентов поместим обычную кнопку, в свойстве Caption которой напишем "Начать восстановление БД", а свойство Enable переведем в False (мы сделаем кнопку доступной, когда пользователь выберет какую-нибудь резервную копию, из которой требуется сделать восстановление). Свойство Name кнопки переименуйте в bStartRestore.
Ниже панели Panel поместим Memo (оставим имя по умолчанию Memo2 ). Свойство Align переведем в alClient, в свойстве ScrollBars выберем ssVertical, чтобы появилась вертикальная прокрутка, а также очистим текст в свойстве Lines.
Займемся не визуальными компонентами. OpenDialog переименуем в OD1. Откроем редактор свойства Filter и в столбец Filter Name впишем "Резервные копии БД", а в столбец Filter - "*.gbk". В свойстве InitialDir укажем папку с нашими резервными копиями "C:\DataBases\Backup". Также в свойстве DefaultExt укажем расширение файлов по умолчанию "gbk" (без точки).
Перейдем к компоненту IBRS. Как видите, этот компонент также имеет сложное раскрывающееся свойство Options, в котором можно настроить следующие переключатели:
| Переключатель | Описание |
|---|---|
| Сделать индексы в базе данных неактивными. | |
| При восстановлении БД не восстанавливать ее |
|
Отключить проверку Foreign Key ). Делать это нужно осторожно, предварительно сделав копию метаданных. |
|
| Восстановить метаданные и данные для каждой таблицы по одной записи за раз. Бывает полезно, если обычное восстановление не получается из-за разрушенных данных или нарушенных |
|
| При значении True можно будет восстанавливать копию поверх существующей базы данных. Крайне не рекомендуется. | |
| При True создается новый файл базы данных. | |
| При восстановлении заполнять страницы базы на 100%, вместо положенных по умолчанию 80%. Чтобы не ухудшить производительность БД, делать это стоит лишь с базами "только для чтения". |
Для шести из семи этих переключателей у нас имеются соответствующие компоненты ComboBox, мы опустили только переключатель
Займемся кодированием. Сгенерируйте процедуру нажатия на кнопку "…". Ее код:
{Щелкнули по кнопке "..."}
procedure TfMain.bOpenBakClick(Sender: TObject);
begin
//если диалог состоялся:
if OD1.Execute then begin
//укажем выбранный файл
Edit1.Text:= OD1.FileName;
//делаем доступной кнопку "Начать восстановление БД"
bStartRestore.Enabled:= True;
end; //if
end;
Остается реализовать само восстановление. Сгенерируйте процедуру нажатия на кнопку "Начать восстановление БД". Ее код:
{Щелкнули по кнопке "Начать восстановление БД"}
procedure TfMain.bStartRestoreClick(Sender: TObject);
var st: String;
begin
//получим префикс для восстановленной БД и лога:
st := GetName();
//Очистим Memo2:
Memo2.Clear;
//устанавливаем начальные параметры:
IBRS.ServerName:= 'MyServ';
IBRS.LoginPrompt:= False;
IBRS.Params.Add('user_name=sysdba');
IBRS.Params.Add('password=masterkey');
IBRS.Active:= True;
//начинаем восстановление
try
IBRS.Verbose:= True; //вести лог
IBRS.Options:= []; //очистим свойство Options
//далее мы включаем в Options переключатель,
//если его ComboBox включен:
if CB1.Checked then IBRS.Options :=
IBRS.Options + [DeactivateIndexes];
if CB2.Checked then IBRS.Options :=
IBRS.Options + [NoShadow];
if CB3.Checked then IBRS.Options :=
IBRS.Options + [NoValidityCheck];
if CB4.Checked then IBRS.Options :=
IBRS.Options + [OneRelationAtATime];
if CB5.Checked then IBRS.Options :=
IBRS.Options + [CreateNewDB];
if CB6.Checked then IBRS.Options :=
IBRS.Options + [UseAllSpace];
//устанавливаем размер буфера страницы:
IBRS.PageBuffers:= SE3.Value;
IBRS.PageSize:= StrToInt(CBox1.Text); //размер самой страницы
//очищаем свойство с именем БД, которая должна получиться:
IBRS.DatabaseName.Clear;
//устанавливаем новое имя с префиксом:
IBRS.DatabaseName.Add('c:\DataBases\Restore\' +
st + 'first.gdb');
//очищаем свойство с именем резервной копии:
IBRS.BackupFile.Clear;
//устанавливаем новое имя из OpenDialog:
IBRS.BackupFile.Add(OD1.FileName);
//стартуем восстановление:
IBRS.ServiceStart;
//параллельно ведем лог, пока не наступит конец восстановления:
while not IBRS.Eof do
Memo2.Lines.Add(IBRS.GetNextLine);
finally
IBRS.Active:= False; //деактивируем компонент
end; //try
//запишем полученный лог в файл:
Memo2.Lines.SaveToFile('c:\DataBases\Restore\' +
st + 'first.log');
//теперь очистим Edit1 и снова сделаем кнопку недоступной:
Edit1.Text:= '';
bStartRestore.Enabled:= False;
end;
Как видно из листинга, работа компонента Delphi в примере работы компонента
Свойство PageSize устанавливает размер страниц восстанавливаемой базы данных. Как уже говорилось в прошлых лекциях, при восстановлении БД из резервной копии можно изменить размер страниц этой БД. Например, если старая версия БД имела размер 4096, то при восстановлении можно установить размер страниц 8192.
Код процедуры подробно комментирован, и в дополнительных пояснениях не нуждается. Сохраните проект, скомпилируйте его и попробуйте сделать восстановление из какой-нибудь резервной копии БД, которую делали ранее. Восстановление закончится, когда в Memo2 появится строка " ", а в папке C:\DataBases\Restore появятся два файла: восстановленная база данных и лог-файл.
Определимся с задачей:
SYSDBA, редактирование запрещаем. Ведь при резервном копировании и восстановлении мы жестко задавали имя SYSDBA и пароль masterkey (если вы изменили этот пароль, значит у вас свой вариант). И если администратор изменит этот пароль, программа не сможет работать, вам придется изменить этот пароль в программе и перекомпилировать ее. Впрочем, в дальнейшем вы можете усложнить программу, добавив в нее, например, работу с ini -файлом (см. лекцию №18 курса "Введение в программирование на Delphi "). В этом случае, вы сможете прописать в ini -файл новый пароль пользователя SYSDBA, если администратор изменит его, и в дальнейшем использовать новый пароль. Если же администратор не будет менять пароля, то можно использовать пароль по умолчанию, " masterkey " (или ваш вариант).SYSDBA.Для реализации этой задачи создадим новую вкладку, которую назовем TSh3, а в свойстве Caption напишем "Пользователи". Внешний вид вкладки показан на следующем рисунке:
(рис 27.3 ) Вкладка "Пользователи"Здесь мы установили компонент ListBox, свойство Name которого переименовали в LB1, а свойство Align перевели в alLeft, чтобы компонент занял всю левую часть окна. Измените размеры компонента, как на рисунке.
В правой части установили три простых кнопки Button. Свойства Name кнопок переименовали в bAddUser, bModifyUser и bDeleteUser, а в свойстве Caption, соответственно, прописали "Добавить", "Редактировать" и "Удалить".
Кроме того, мы добавили не визуальный компонент . Компонент для краткости обращения переименовали в IBSS. Этот компонент предназначен для работы с пользователями, зарегистрированными в , и позволит нам редактировать, добавлять и удалять пользователей.
Несмотря на кажущуюся простоту окна, реализация работы с пользователями немного сложнее предыдущих примеров. В отличие от компонентов first.gdb, а с системной БД ,
что накладывает некоторые ограничения. Например, имя, отчество и фамилию пользователя не получится вносить русскими буквами, так как isc4.gdb создавалась не в кодировке WIN1251. Кроме того, в целях безопасности, компонент
Для доступа к данным пользователя компонент
Первым делом нам нужно при создании главной формы заполнить компонент private добавим новую процедуру ReloadUsers:
private
{ Private declarations }
function GetName():String;
procedure BackupCopy;
procedure ReloadUsers;
Реализация этой процедуры:
{Перечитываем данные о пользователях}
procedure TfMain.ReloadUsers;
var i: Integer; //для счетчика
begin
//вначале очистим ListBox:
LB1.Clear;
//заполняем список пользователями:
IBSS.ServerName:= 'MyServ';
IBSS.LoginPrompt:= False;
IBSS.Params.Add('user_name=sysdba');
IBSS.Params.Add('password=masterkey');
IBSS.Active:= True;
try
IBSS.DisplayUsers; //получаем информацию о пользователях
for i:=0 to IBSS.UserInfoCount -1 do
LB1.Items.Add(IBSS.UserInfo[i].UserName);
finally
IBSS.Active:= False;
end; //try
end;
Здесь все достаточно прозрачно. Вначале очищаем ListBox от возможных старых записей. Затем настраиваем сервис
Метод
LB1.Items.Add(IBSS.UserInfo[i].UserName);
мы добавляем в ListBox имя очередного пользователя. Теперь нужно вызвать эту процедуру при создании формы. Для этого сгенерируйте для главной формы событие OnCreate, в котором сделаем вызов этой процедуры:
ReloadUsers;
Проверить результат можно, скомпилировав и загрузив программу. При загрузке, в окне ListBox должны отобразиться зарегистрированные пользователи.
Далее нам придется сделать новую форму - редактор данных пользователей. Выберите команду File -> New -> Form. Форму назовите fEditor, в свойстве Caption напишите "Редактирование данных пользователя", сохраните новый модуль под именем Editor.
Свойство BorderStyle формы переведем в bsDialog, чтобы пользователь не мог менять размеры окна, а в свойстве Position выберем poMainFormCenter.
Внешний вид новой формы показан на рисунке ниже:
(рис 27.4 ) Форма fEditorИтак, у нас имеется:
В свойстве Caption первого GroupBox напишем "Обязательные данные", а второго - "Дополнительные данные".
На верхний GroupBox помещаем три Label и заполняем текст, как на рисунке. Затем помещаем три Edit. У первого свойство Name переименуем в eUser, у второго - ePass, и у третьего - ePass2.
Выделите одновременно (с нажатой Shift ) компоненты ePass и ePass2. В свойстве PasswordChar укажите символ "*", чтобы скрыть реальный пароль. Не забудьте удалить текст из свойства Text всех компонентов Edit.
На нижний GroupBox также помещаем три Label и заполняем текст, как на рисунке. Затем помещаем три Edit. У первого свойство Name переименуем в eName, у второго - eMiddle, и у третьего - eLast. Очищаем свойство Text у всех Edit.
Ниже расположим две кнопки, которые переименуем соответственно, в bAccept и bCancel, а свойства Caption - как на рисунке.
Теперь немного подумаем. Эта форма будет показываться в двух случаях: при добавлении нового пользователя, и при редактировании существующего. В первом случае пользователь еще не имеет пароля, во втором - имеет. Причем при редактировании существующего пользователя, администратор может как сменить старый пароль, так и не трогать его. Как узнать, менялся ли пароль существующего пользователя, если мы не имеем возможности вывести его с помощью ePass и ePass 2 какой-нибудь длинный пароль "по умолчанию", например, двадцать единичек. А при сохранении результата редактирования будем смотреть - если в ePass пароль "по умолчанию", значит сохранять пароль не нужно. В случае же добавления нового пользователя ePass и ePass2 заполняться не будут.
Пойдем дальше. Как уже говорилось, база данных isc4.gdb не поддерживает кодировку win1251, следовательно, русские буквы в нее помещать нельзя. Значит, при вводе текста во все шесть компонентов Edit придется делать проверку - что вводит пользователь. Если английские буквы или цифры, или BackSpace, то ничего не делаем, иначе запрещаем символ. Чтобы шесть раз не писать один и тот же код, в разделе private создадим функцию KeyCan:
private
{ Private declarations }
function KeyCan(c:Char):Boolean;
Код реализации функции представлен ниже:
{Проверяем допустимость символа}
function TfEditor.KeyCan(c: Char): Boolean;
begin
case c of
'A'..'z': Result:= True; //англ. буквы разрешаем
'0'..'9': Result:= True; //цифры разрешаем
#8 : Result:= True; //BackSpace разрешаем
else Result:= False; //остальное запрещаем
end; //case
end;
Теперь нам нужно реализовать проверку во всех шести Edit. Выделите первый, и сгенерируйте для него событие OnKeyPress. В процедуру поместите только одну строку:
if not KeyCan(Key) then Key:= #0;
То же самое проделайте с оставшимися пятью Edit. Таким образом, мы реализовали проверку на ввод допустимых символов. Если пользователь попытается ввести что-нибудь, кроме английских букв, цифр или BackSpace, то его действия будут игнорироваться.
Пойдем дальше. Если мы редактируем нового пользователя, то в ePass и ePass2 у нас будет текст в двадцать единиц. Если администратор захочет изменить пароль, то он изменит текст в ePass. Значит, для ePass нужно сгенерировать событие OnChange, в котором очищаем текст у ePass2:
ePass2.Text:= '';
Теперь подумаем вот о чем. Окно редактора пользователей мы будем вызывать из главной формы, там же мы будем сохранять изменения, если администратор нажмет кнопку "Подтвердить" в окне редактора. А как из главной формы узнать - хочет ли администратор сохранить изменения, или нет? Введем глобальную переменную izmen в модуль редактора пользователей:
var fEditor: TfEditor; izmen: Boolean;
Теперь для формы редактора пользователей сгенерируем событие OnShow, в котором сразу пропишем:
izmen:= False;
В событии нажатия на кнопку "Подтвердить" вписываем код:
izmen:= True; Close;
А при нажатии на кнопку "Отменить" просто закрываем форму:
Close;
Теперь, если администратор закроет эту форму иначе, чем нажатием на "Подтвердить", мы ничего предпринимать не будем. Осталось сделать проверки на то, ввел ли администратор логин нового пользователя, совпадают ли пароли в ePass и ePass2? Для этого сгенерируем событие OnClose для формы редактора:
{при закрытии формы}
procedure TfEditor.FormClose(Sender: TObject; var Action: TCloseAction);
begin
//если изменения не нужно делать, просто выходим:
if not izmen then Exit;
//Иначе делаем проверку. Если нет имени пользователя:
if eUser.Text = '' then begin
ShowMessage('Введите имя пользователя!');
Action := caNone; //запрещаем покидать форму
eUser.SetFocus; //переводим фокус на имя пользователя
end;
//Если пароль не '11111111111111111111', делаем проверку
//совпадает ли ePass и ePass2:
if ePass.Text <> '11111111111111111111' then
if ePass.Text <> ePass2.Text then begin
ShowMessage('Введите правильный пароль!');
Action := caNone; //запрещаем покидать форму
//очистим ePass и ePass2:
ePass.Text:= '';
ePass2.Text:= '';
ePass.SetFocus; //переводим фокус на имя пользователя
end;
end;
Комментарии достаточно подробны, чтобы вы смогли разобраться с кодом.
С этой формой закончили, вернемся на главную форму. Не забудем сразу же командой File -> Use Unit подключить к ней модуль Editor.
Реализуем добавление нового пользователя. Сгенерируйте обработчик нажатия на кнопку "Добавить". Код обработчика следующий:
{Нажали "Добавить"}
procedure TfMain.bAddUserClick(Sender: TObject);
begin
//вначале очистим данные, которые могут быть
//в редакторе пользователей:
fEditor.eUser.Text:= '';
fEditor.ePass.Text:= '';
fEditor.ePass2.Text:= '';
fEditor.eName.Text:= '';
fEditor.eMiddle.Text:= '';
fEditor.eLast.Text:= '';
//теперь показываем форму:
fEditor.ShowModal;
//если изменений делать не нужно, просто выходим:
if not Editor.izmen then Exit;
//иначе готовим компонент IBSS к созданию нового пользователя.
IBSS.Active:= True;
try
//теперь вводим данные нового пользователя:
IBSS.UserName:= fEditor.eUser.Text;
IBSS.Password:= fEditor.ePass.Text;
IBSS.FirstName:= fEditor.eName.Text;
IBSS.MiddleName:= fEditor.eMiddle.Text;
IBSS.LastName:= fEditor.eLast.Text;
//вызываем метод AddUser, который добавляет пользователя:
IBSS.AddUser;
finally
IBSS.Active:= False; //закрываем компонент
end; //try
//перечитываем информацию о пользователях:
ReloadUsers;
end;
Поскольку пользователь у нас новый, данных на него пока никаких нет. Значит, вначале мы очистим все Edit на форме редактора, и только потом покажем ее.
Когда администратор закончит работу с формой, и закроет ее, начинает работу следующая строка процедуры:
//если изменений делать не нужно, просто выходим: if not Editor.izmen then Exit;
То есть, если администратор не нажал кнопку "Подтвердить", мы просто выйдем, ничего не делая. Если же процедура работает дальше, то нужно сохранить нового пользователя:
IBSS.Active:= True;
try
//теперь вводим данные нового пользователя:
IBSS.UserName:= fEditor.eUser.Text;
IBSS.Password:= fEditor.ePass.Text;
IBSS.FirstName:= fEditor.eName.Text;
IBSS.MiddleName:= fEditor.eMiddle.Text;
IBSS.LastName:= fEditor.eLast.Text;
//вызываем метод AddUser, который добавляет пользователя:
IBSS.AddUser;
…
Как видите, при сохранении данных мы используем свойства компонента ReloadUsers для обновления данных.
Сохраните проект, скомпилируйте его и попробуйте добавить нового пользователя. Пользователь должен появиться в окне ListBox, кроме того, он должен быть виден и в утилите , в разделе Users дерева серверов.
Редактирование выбранного пользователя вызывается кнопкой "Редактировать". Сгенерируйте этот обработчик. Вот его код:
{Нажали "Редактировать"}
procedure TfMain.bModifyUserClick(Sender: TObject);
var i: Integer; //счетчик
begin
//если не один пользователь не выбран, просто выходим:
if LB1.ItemIndex = -1 then begin
ShowMessage('Выберите пользователя!');
Exit;
end
//если выбран SYSDBA, тоже выходим:
else if LB1.Items[LB1.ItemIndex] = 'SYSDBA' then begin
ShowMessage('Редактировать пользователя SYSDBA нельзя.');
Exit;
end; //else if
//иначе редактируем
//установим у IBSecurityService выбранного пользователя:
IBSS.Active:= True;
IBSS.UserName:= LB1.Items[LB1.ItemIndex];
//найдем в IBSS индекс нужного пользователя:
for i:= 0 to IBSS.UserInfoCount - 1 do
if IBSS.UserInfo[i].UserName =
LB1.Items[LB1.ItemIndex] then break;
//теперь i содержит индекс пользователя
//заполняем редактор пользователей данными:
fEditor.eUser.Text:= IBSS.UserInfo[i].UserName;;
//раз пользователь уже есть, выводим единички в качестве пароля:
fEditor.ePass.Text:= '11111111111111111111';
fEditor.ePass2.Text:= '11111111111111111111';
fEditor.eName.Text:= IBSS.UserInfo[i].FirstName;
fEditor.eMiddle.Text:= IBSS.UserInfo[i].MiddleName;
fEditor.eLast.Text:= IBSS.UserInfo[i].LastName;
//теперь покажем редактор:
fEditor.ShowModal;
//если изменений делать не нужно, просто выходим:
if not Editor.izmen then Exit;
//иначе сохраняем их:
try
//Имя пользователя менять не будем.
//Сохраним пароль, если там не единички:
if fEditor.ePass.Text <> '11111111111111111111' then
IBSS.Password:= fEditor.ePass.Text;
//Далее сохраняем остальные параметры:
IBSS.FirstName:= fEditor.eName.Text;
IBSS.MiddleName:= fEditor.eMiddle.Text;
IBSS.LastName:= fEditor.eLast.Text;
//сохраняем изменения физически методом ModifyUser:
IBSS.ModifyUser;
finally
IBSS.Active:= False;
end; //try
//перечитываем информацию о пользователях:
ReloadUsers;
end;
Подробно разбирать весь код не будем, так как комментариев достаточно, но остановимся на некоторых моментах. Строка
IBSS.UserName:= LB1.Items[LB1.ItemIndex];
делает в компоненте IBSS текущим пользователем того, который в данный момент выбран в списке ListBox. Затем мы находим индекс этого пользователя в списке. Зачем это нужно? Дело в том, что данные о пользователе можно вытащить только через свойство должны отобразиться эти данные.
Это самая простая операция с
{Удаление пользователя}
procedure TfMain.bDeleteUserClick(Sender: TObject);
var s: String; //для формирования строки
begin
//если не один пользователь не выбран, просто выходим:
if LB1.ItemIndex = -1 then begin
ShowMessage('Выберите пользователя!');
Exit;
end;
//иначе продолжаем. формируем запрос:
s:= 'Вы действительно желаете удалить пользователя ' +
LB1.Items[LB1.ItemIndex] + '?';
//попросим подтверждения. выходим, если не подтвердили:
if Application.MessageBox(PChar(s), 'Удаление пользователя',
MB_YESNOCANCEL+MB_ICONQUESTION) <> IDYES then Exit;
//если не вышли, значит удаляем пользователя
IBSS.Active:= True;
try
//делаем пользователя текущим:
IBSS.UserName:= LB1.Items[LB1.ItemIndex];
//удаляем его методом DeleteUser:
IBSS.DeleteUser;
finally
IBSS.Active:= False;
end; //try
//перечитываем информацию о пользователях:
ReloadUsers;
end;
Тут все почти так же, как в предыдущих примерах, за исключением нового метода
Вкладка Delphi по компонентам.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.