Обработка ошибок firebird delphi

FIBPlus: Обработка ошибок базы данных

Еще одним из несомненных достоинств FIBPlus являются средства обработки ошибок базы данных. В данной статье мы рассмотрим эти средства.

Основной компонент, используемый в обработке ошибок, — TpFibErrorHandler. Он позволяет централизованно обрабатывать ошибки базы данных.

Подготовка к обработке ошибок

Для иллюстрации возьмем базу данных FIBSAMPLE.GDB, используемую в большинстве примеров по FIBPlus. В нашем примере мы будем использовать две таблицы из этой базы данных: TREFCOUNTRY и PERSON. Они имеют следующее объявление:

CREATE TABLE TREFCOUNTRY (
    NAME         DNAME30,
    FULLNAME     DNAME60,
    CODCTR       DCODCTR NOT NULL,
    CAPITAL      DNAME30,
    REGION       DNAME30,
    DESCRIPTION  DDESCR
);
 
CREATE TABLE PERSON (
    CODPERS     INTEGER NOT NULL,
    FIRST_NAME  DNAME20,
    LAST_NAME   DNAME20,
    COUNTRY     DCODCTR
);
ALTER TABLE PERSON ADD CONSTRAINT "PERS CHECK LASTNAME NOTNULL"
 check (last_name is not null);
ALTER TABLE PERSON ADD CONSTRAINT "PERS CHECK LASTNAME VALUE"
 check (last_name not containing '***');
ALTER TABLE PERSON ADD CONSTRAINT "PERS PRIMARYKEY"
 PRIMARY KEY (CODPERS);
ALTER TABLE TREFCOUNTRY ADD CONSTRAINT "Country PRIMARY KEY"
 PRIMARY KEY (CODCTR);
ALTER TABLE PERSON ADD CONSTRAINT "PERS FOREIGN KEY"
 FOREIGN KEY (COUNTRY) REFERENCES TREFCOUNTRY (CODCTR);

Создайте в Delphi или C++Builder новый проект ErrorHandler. Положите на форму следующие компоненты:

StatusBar1: TStatusBar;
Panel1: TPanel;
Panel2: TPanel;
Splitter1: TSplitter;
Splitter2: TSplitter;
Memo1: TMemo;
DBGrid1: TDBGrid;
DBGrid2: TDBGrid;
BExit: TSpeedButton;
BRefresh: TSpeedButton;
BSRollback: TButton;
BSCommit: TButton;
Database1: TpFIBDatabase;
WriteTransaction: TpFIBTransaction;
CountryData: TpFIBDataSet;
DSCountry: TDataSource;
PersData: TpFIBDataSet;
DSPerson: TDataSource;
ErrorHandler1: TpFibErrorHandler;

Примечание. Поскольку вы используете компонент TpFibErrorHandler, вы должны явно указать модуль fib в uses для Delphi:

Свойство DBName компонента Database1 ссылается на базу данных FIBSAMPLE.GDB. Компонент CountryData типа TpFIBDataSet ссылается на таблицу TREFCOUNTRY. Компонент PersData ссылается на таблицу PERSON. Используя SQL Generator, сгенерируйте обычным образом все операторы SQL для этих компонентов.

Компонент DSCountry свяжите с CountryData. Свойство DataSource у DBGrid1 установите в DSCountry. Компонент DBGrid1 отображает содержимое набора данных CountryData. Аналогичным образом свяжите компонент DSPerson с PersData. Свойство DataSource у DBGrid2 установите в DSPerson. Компонент DBGrid2 отображает содержимое PersData.

В поле Memo1 будет отображаться информация об ошибках.

[Image]

Рис. 1. Проект ErrorHandler. Обработка ошибок базы данных

Напишите следующие обработчики событий щелчка по кнопкам Rollback и Commit:

procedure TFormMain.BSRollbackClick(Sender: TObject);
begin
  WriteTransaction.RollbackRetaining;
  CountryData.FullRefresh;
  StatusBar1.Panels.Items[1].Text :=
    IntToStr(CountryData.RecordCount);
  DBGrid1.SetFocus;
end;
 
procedure TFormMain.BSCommitClick(Sender: TObject);
begin
  WriteTransaction.CommitRetaining;
  CountryData.FullRefresh;
  PersData.FullRefresh;
  DBGrid1.SetFocus;
end;

Напишите следующий обработчик события щелчка по кнопке Refresh:

procedure TFormMain.BRefreshClick(Sender: TObject);
begin
  CountryData.FullRefresh;
  PersData.FullRefresh;
  StatusBar1.Panels.Items[1].Text :=
    IntToStr(CountryData.RecordCount);
end;

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

Свойства и особенности компонента TpFIBErrorHandler

Центральная часть этой программы — компонент ErrorHandler1 и обработчик его события OnFIBEventError.

Установите в True все подсвойства свойства Options этого компонента: oeException, oeForeignKey, oeLostConnect, oeCheck, oeUniqueViolation. Это позволит перехватывать и обрабатывать в программе все основные типы исключений при работе с базой данных.

[Image]

Рис. 2. Характеристики компонента ErrorHandler1.

Компонент TpFibErrorHandler также содержит следующие свойства (только для чтения):

Таблица 1. Свойства только для чтения компонента TpFibErrorHandler.

Свойство

Значение

ConstraintName

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

ExceptionNumber

Номер пользовательского исключения (exception), вызвавшего ошибку. Если ошибка вызвана не исключением, то значение будет –1.

LastError

Тип последнего исключения — объект класса TKindIBError:
keNoError — ошибка отсутствует,
keException — обработано пользовательское исключение,
keForeignKey — обработано нарушение внешнего ключа,
keLostConnect — соединение с базой данных потеряно,
keSecurity — обработано нарушение полномочий пользователя,
keUniqueViolation — обработано нарушение уникального значения,
keCheck — обработано нарушение ограничения CHECK,
keOther — обработан иной тип ошибки.

Следующая таблица содержит описание значения подсвойств свойства Options.

Таблица 2. Список подсвойств свойства Options компонента TpFibErrorHandler.

Подсвойство

Значение

oeException

Обрабатываются пользовательские исключения. Текст ошибки не выводится, номер пользовательского исключения передается в свойстве компонента ExceptionNumber.

oeForeignKey

Нарушение значения внешнего ключа (foreign key).

oeLostConnect

Потеря связи с базой данных.

oeCheck

Нарушение ограничения CHECK.

oeUniqueViolation

Нарушение ограничения UNIQUE.

Вернемся к основной части нашей программы. Напишите следующий обработчик события OnFIBEventError для компонента ErrorHandler1:

procedure TFormMain.ErrorHandler1FIBErrorEvent(Sender: TObject;
  ErrorValue: EFIBError; KindIBError: TKindIBError;
  var DoRaise: Boolean);
var Lasterror: string;
    FKindIBError: string;
begin
  Memo1.Lines.Add(#13#10 + '===== ErrorHandler FIBErrorEvent =====');
  Memo1.Lines.Add('Sender.ClassName = ' + Sender.ClassName);
  Memo1.Lines.Add('Sender.Name = ' + (Sender as TComponent).Name);
  if Sender is TFIBQuery then
    Memo1.Lines.Add('Owner.Name = ' +
      (Sender as TFIBQuery).Owner.Name);
  if Sender is TpFIBStoredProc then
    Memo1.Lines.Add('Sender.StoredProcName = ' +
      (Sender as TpFIBStoredProc).StoredProcName);
  Memo1.Lines.Add('ConstraintName = ' +
    ErrorHandler1.ConstraintName);
  Memo1.Lines.Add('ExceptionNumber = ' +
    IntToStr(ErrorHandler1.ExceptionNumber));
  case ErrorHandler1.LastError of
    keNoError: Lasterror := 'keNoError';
    keException: Lasterror := 'keException';
    keForeignKey: Lasterror := 'keForeignKey';
    keSecurity: Lasterror := 'keSecurity';
    keLostConnect: Lasterror := 'keLostConnect';
    keCheck: Lasterror := 'keCheck';
    keUniqueViolation: Lasterror := 'keUniqueViolation';
    keOther: Lasterror := 'keOther';
  else
    Lasterror := 'Undefined';
  end;
  Memo1.Lines.Add('Lasterror = ' + Lasterror);
  Memo1.Lines.Add('SQLCode = ' + IntToStr(ErrorValue.SQLCode));
  Memo1.Lines.Add('IBErrorCode = ' +
   IntToStr(ErrorValue.IBErrorCode));
  Memo1.Lines.Add('Message = ' + ErrorValue.Message);
  Memo1.Lines.Add('IBMessage = ' + ErrorValue.IBMessage);
  Memo1.Lines.Add('SQLMessage = ' + ErrorValue.SQLMessage);
  case KindIBError of
    keNoError: FKindIBError := 'keNoError';
    keException: FKindIBError := 'keException';
    keForeignKey: FKindIBError := 'keForeignKey';
    keSecurity: FKindIBError := 'keSecurity';
    keLostConnect: Lasterror := 'keLostConnect';
    keCheck: FKindIBError := 'keCheck';
    keUniqueViolation: FKindIBError := 'keUniqueViolation';
    keOther: FKindIBError := 'keOther';
  else
    FKindIBError := 'Undefined';
  end;
  Memo1.Lines.Add('KindIBError = ' + FKindIBError);
//  DoRaise := False;
end;

Обработчику передаются следующие параметры: 1. ErrorValue — объект класса EFIBError. Класс содержит следующие свойства:

1.IBErrorCode — содержит код ошибки InterBase. Является наиболее информативным описателем ошибки. Существует около 400 различных кодов. Список кодов приведен в документе InterBase Language Reference в 5 главе Error Codes and Messages (таб).

  • IBMessage — содержит текст сообщения об ошибке.
  • SQLCode — содержит код ошибки SQLCODE.
  • SQLMessage — содержит сообщение об ошибке SQL.

2. KindIBError — объект класса TKindIBError. Может иметь значения, описанные в таблице 1, в свойстве LastError.

3. DoRaise — переменная логического типа. Позволяет указать, следует ли после обработки ошибки в данном обработчике вызывать повторно исключение. Если DoRaise присваивается значение True (по умолчанию), то после выполнения действий в обработчике ошибок будет вызвано стандартное исключение с выдачей соответствующих сообщений. Если же DoRaise имеет значение False, то действие, вызвавшее ошибку, отменяется, никаких сообщений не выдается. В нашем обработчике ошибок базы данных вся возможная информация об ошибке — с использованием свойств компонента TpFibErrorHandler и параметров, передаваемых в процедуру, — выводится в поле Memo1.

Внимание. Компонент TpFibErrorHandler позволяет обрабатывать множество ошибок базы данных. При этом ошибки подключения к базе данных в нем не обрабатываются (не путать с ошибкой при потере подключения и попытках возобновления подключения). Для этого следует использовать стандартные средства Delphi или C++Builder — блок try…except (Delphi) или try…catch (C++Builder). Не обрабатываются также такие ситуации, когда для компонента, работающего с базой данных (DataSet, Query, SoredProc и некоторые другие), не указана база данных или транзакция.

Пример перехвата ошибки подключения к базе данных.

try
  Database1.Connected := True;
except
  ShowMessage(’Ошибки при подключении к базе данных’);
  Application.Terminate;
end;

Обработка исключений

Теперь мы можем посмотреть, как работает наш обработчик ошибок.

Запустите программу на выполнение. Удалите значение первичного ключа (CODCTR) в любой строке в таблице TREFCOUNTRY (левый DBGrid). Это приводит к тому, что значение поля становится NULL, что недопустимо для первичного ключа. В поле Memo1 появится следующий текст:

========= ErrorHandler FIBErrorEvent =========
Sender.ClassName = TFIBQuery
Sender.Name = UpdateQuery
Owner.Name = CountryData
ConstraintName = 
ExceptionNumber = -1
Lasterror = keOther
SQLCode = -625
IBErrorCode = 335544347
Message = FormMain.CountryData.UpdateQuery:
The insert failed because a column definition includes validation constraints.validation error
for column CODCTR, value "*** null ***".
IBMessage = validation error for column CODCTR, value "*** null ***".
SQLMessage = The insert failed because a column definition includes validation constraints.
KindIBError = keOther
Обратите внимание на первые три строчки сообщения после заголовка: 
Sender.ClassName = TFIBQuery
Sender.Name = UpdateQuery
Owner.Name = CountryData

Здесь Sender.ClassName содержит имя класса объекта, вызвавшего исключение (TFIBQuery). Sender.Name — имя объекта (UpdateQuery), Owner.Name — имя владельца объекта: имя компонента TpFIBDataSet (CountryData). Если мы в этот же набор данных попытаемся добавить новую запись с пустым значением первичного ключа, то получим такое же сообщение, только значением Sender.Name будет InsertQuery.

Аналогичный результат будет получен, если вы попытаетесь установить в NULL значение первичного ключа (CODPERS) таблицы PERSON.

Попытка ввести дублированное значение первичного ключа в любую строку в таблице PERSON приводит к выдаче сообщения:

========= ErrorHandler FIBErrorEvent =========
Sender.ClassName = TFIBQuery
Sender.Name = UpdateQuery
Owner.Name = PersData
ConstraintName = PERS
ExceptionNumber = -1
Lasterror = keUniqueViolation
SQLCode = -803
IBErrorCode = 335544665
Message = violation of PRIMARY or UNIQUE KEY constraint "PERS PRIMARYKEY" on table "PERSON".
IBMessage = violation of PRIMARY or UNIQUE KEY constraint "PERS PRIMARYKEY" on table "PERSON".
SQLMessage = Invalid insert or update value(s): object columns are
constrained - no 2 table rows can have duplicate column values.
KindIBError = keUniqueViolation

Аналогичным образом можно проверить реакцию на нарушение других ограничений базы данных. Проверим результат обработки ошибки «потеря соединения». Для моделирования потери соединения завершите выполнение сервера InterBase/Firebird. Если запущена программа Guardian, следует завершить ее до завершения сервера. После этого нужно щелкнуть по кнопке Refresh. Это вызовет желаемую ошибку. В результате обработчик выдаст следующие сообщения:

========= ErrorHandler FIBErrorEvent =========
Sender.ClassName = TFIBQuery
Sender.Name = RefreshQuery
Owner.Name = CountryData
ConstraintName = 
ExceptionNumber = -1
Lasterror = keLostConnect
SQLCode = -901
IBErrorCode = 335544741
Message = FormMain.CountryData.RefreshQuery:
Unsuccessful execution caused by system error that does not preclude successful execution of
subsequent statements.connection lost to database.
IBMessage = connection lost to database.
SQLMessage = Unsuccessful execution caused by system error that does not preclude successful
execution of subsequent statements.
KindIBError =

Обратите внимание на значения SQLCode и IBErrorCode. SQLCode = -901. Если посмотреть значения SQLCode и IBErrorCode в документации по InterBase (Language Reference), то можно увидеть, что этому значению соответствует ровно 60 вариантов ошибок. Значение же IBErrorCode = 335544741 имеет только один тип ошибки: «connection lost to database», то есть, потеря соединения с базой данных. Еще раз следует подчеркнуть, что с целью обработки ошибок базы данных в программе наиболее полезным является именно параметр IBErrorCode, передаваемый обработчику ошибок.

Отдельно стоит посмотреть, как работает обработчик ошибок при вызове из хранимой процедуры или из триггера пользовательского исключения. Нам нужно добавить в нашу базу данных FIBSAMPLE.GDB исключение и простенькую хранимую процедуру, вызывающую это исключение.

Создать исключение можно следующим образом, используя файл скрипта:

CONNECT 'D:Указываем конкретный путь к базе данныхFIBSAMPLE.GDB'
USER 'SYSDBA' PASSWORD 'masterkey';
 
CREATE EXCEPTION EXCEPT1 'Exception 1';
commit;

Аналогично создается хранимая процедура после создания исключения:

CONNECT 'D: Указываем конкретный путь к базе данныхFIBSAMPLE.GDB'
USER 'SYSDBA' PASSWORD 'masterkey';
 
CREATE PROCEDURE EXEEXCEPT1
AS
begin
  EXCEPTION EXCEPT1;
end;
commit;

Для вызова исключения нужно также внести изменения в наш проект. Положите на форму кнопку с именем Exception и компонент TpFIBStoredProc. Для компонента TpFIBStoredProc задайте следующие характеристики:

[Image]

Рис. 3. Характеристики компонента TpFIBStoredProc обращения к хранимой процедуре

Напишите следующий обработчик щелчка по кнопке Exception:

procedure TFormMain.ExceptionClick(Sender: Tobject);
begin
  FIBStoredProc1.ExecProc;
end;

Запустите программу на выполнение, щелкните по кнопке Exception. В поле Memo1 отобразится следующее:

========= ErrorHandler FIBErrorEvent =========
Sender.ClassName = TpFIBStoredProc
Sender.Name = FIBStoredProc1
Sender.StoredProcName = EXEEXCEPT1
ConstraintName = 
ExceptionNumber = 1
Lasterror = keException
SQLCode = -836
IBErrorCode = 335544517
Message = Exception 1.
IBMessage = exception 1.
Exception 1.
SQLMessage = exception 268785020.
KindIBError = keException

Здесь Sender.ClassName имеет значение (TpFIBStoredProc), поскольку исключение мы получили при вызове хранимой процедуры. Sender.Name содержит имя компонента, через который была вызвана хранимая процедура (FIBStoredProc1). Sender.StoredProcName содержит имя хранимой процедуры.

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

Последний пример на конфликт одновременного изменения одной и той же записи разными клиентами. Запустите два экземпляра программы. Это будут два разных клиента. Измените любую запись в одном процессе и ту же запись в другом процессе. При переходе на следующую запись в DBGrid (при этом для изменяемой записи выдается Post) будет вызвано исключение:

========= ErrorHandler FIBErrorEvent =========
Sender.ClassName = TFIBQuery
Sender.Name = UpdateQuery
Owner.Name = CountryData
ConstraintName = 
ExceptionNumber = -1
Lasterror = keOther
SQLCode = -901
IBErrorCode = 335544345
Message = FormMain.CountryData.UpdateQuery:
Unsuccessful execution caused by system error that does not preclude successful execution of
subsequent statements.lock conflict on no wait transaction.
deadlock.
update conflicts with concurrent update.
IBMessage = lock conflict on no wait transaction.
deadlock.
update conflicts with concurrent update.
SQLMessage = Unsuccessful execution caused by system error that does not preclude successful
execution of subsequent statements.
KindIBError = keOther

Заключение

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

Добавил admin | Категория Заметки про IBExpert, Firebird | 14 Декабря 2013


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

Появится такое вот окно

Тут нас интересует имя исключения, текст исключения, и если нужно то и описание. И так добавляем информацию в нужные для нас поля.

Вводим имя исключения, текст исключения и не забываем все это дело скомпилировать.

Исключение можно создать с помощью SQL команд, для нашего исключения набор таких команд будет выглядеть следующим образом.

CREATE EXCEPTION EXP_FATAL_ERR 'Возникла непредвиденная ошибка';

И так наше исключение готово, давайте теперь поговорим о том как его можно применить, для того что бы воспользоваться нашим исключением мы пишем 


IF (ord_stat = 'shipped') THEN
BEGIN
EXCEPTION exp_fatal_err;
END

Например в тексте хранимой процедуры мы проверяем параметр ord_stat = ‘shipped’. Если этот параметр равен ‘shipped’ то возникает созданное нами исключение. На этом про исключения все, ознакомтесь с остальными статьями рублики IB Expert.

Дата публикации:

30.11.2008


Введение. Доступ к InterBase и Firebird из Delphi

В Delphi cуществует несколько способов работы с InterBase и Firebird.
Мне известны, как минимум, 3 способа организации доступа к данным через IBProvider:

  • dbGo (ADO Express) компоненты, работающие через библиотеку ADO.
  • прямой доступ к COM-интерфейсам ADO, минуя компоненты dbGo.
  • прямой доступ к COM-интерфейсам OLE DB при помощи сторонних VCL-компонент (OLE DB Direct/OLE DB Express).

Компоненты dbGo

dbGo — это VCL-компоненты от Borland, позволяющие работать с библиотекой ADO из Delphi и C++ Builder привычным для этих средств разработки способом. До Delphi 6 компоненты назывались ADOExpress.

Внешний вид панели компонентов dbGo в Delphi (RAD Studio 2007):

Data Link Firebird/InterBase

Состав dbGo-компонентов:

  • Компонент TADOConnection — подключение к базе данных.
  • Компонент TADODataSet — базовый набор данных. TADODataSet позволяет загружать данные таблиц, хранимых процедур и произвольных SQL запросов и передавать изменения обратно в БД. Важным условием является то, что выражение указанное в CommandText, обязательно должно возвращать набор данных. TADODataSet соответствует объекту ADODB.Recordset.
  • Компонент TADOTable является упрощенными вариантом TADODataSet. Он позволяет загружать в память целые таблицы.
  • Компоненты TADOQuery и TADOStoredProc предназначены для выполнения произвольных SQL-запросов и хранимых процедур.
  • Компонент TADOCommand соответствуют объеку ADODB.Command. Фактически объединяет в себе возможности трех компонентов: TADOTable, TADOQuery, TADOStoredProc.

Создание подключения TADOConnection

Компонент TADOConnection осуществляет соединение с хранилищем данных.
TADOConnection похож на компонент TSQLConnection.
Разница между ними в том, что при работе с TSQLConnection параметры соединения устанавливались с помощью свойств ConnectionName, DriverName и Params.
В TADOConnection все параметры подключения задаются через свойство ConnectionString.
Кроме того в качестве строки подключения в TADOConnection может быть указано имя файла с параметрами подключения.

Для хранения параметров подключения в Windows существует специальный тип
файлов Microsoft Data Link — это файл с расширением udl. С этим
расширением ассоциирован универсальный редактор подключений. IBProvider
поддерживает свои собственные табы, которые предоставляют удобный интерфейс для
формирования параметров соединения. Для того чтобы использовать udl файл в своем
приложении выполните следующие шаги:

  • Создайте пустой файл с расширением .udl
  • Откройте файл (Enter), появится связанный с данным расширением диалог для
    настройки подключения
  • В списке OleDb провайдеров выберете IBProvider v3:

Data Link Firebird/InterBase

  • Установите параметры подключения к БД и нажмите кнопку «Проверить подключение». На рисунке заданы следующие параметры:
    • Интегрированная аутентификация Firebird 2.1;
    • Источник — тестовая БД localhost:employee.fdb. Подключение определено без пути, т.к. БД прописана в файле aliases.conf;
    • Режим автоматического управления транзакциями: Включен;
    • Кодировка: WIN1251 или другая кодировка, поддерживаемая Firebird или InterBase;

Data Link: IBProvider data source

СОВЕТ
Всегда включайте в параметр Location сетевое имя компьютера на котором установлен сервер Firebird или InterBase.
Для локального подключения используйте localhost. Это позволит обеспечить совместимость.

Теперь добавьте на форму компонент TADOConnection.
Задайте свойство: Login Promt=false и установите в качестве строки подключения ConnectionString созданный ранеее файл подключения udl:

TADOConnection - Data Link Firebird/InterBase

Подробная информация о свойствах инициализации IBProvider.

Отображение данных через TADOTable

Компонент TADOTable позволяет загружать данные одной таблицы.

  • Найстройте объект TADOConnection:
    • Connected = true;
    • Login Promt = false;
  • Добавьте на форму объект TADOTable и настройте его следующим образом:
    • Connection = ADOConnection1;
    • TableName = имя таблицы из employee.fdb (CUSTOMER, EMPLOYEE, PROJECT и т.п.);
    • Active=true;
  • Добавьте компонент TDataSource с вкладки Data Access. Настройки:
    • DataSet = TADOTable1;
  • Добавьте компонент TDBGrid с вкладки Data Controls. Настройки:
    • DataSource = DataSource1;

В результате DBGrid отобразит данных той таблицы, которая была выбрана в объекте TADOTable:

TADOTable

Компонент TADODataSet

Компонент TADODataSet — это надстройка над объектом ADODB.Recordset. В отличии от TADOTable, TADODataSet
может загружать не только таблицы, но и множества, возвращаемые хранимыми процедурами или SQL-запросами.

Перед началом работы с TADODataSet установите свойство Connection = ADOConnection1.
Альтернативный вариант — эта указать строку подключения в свойстве ConnectionString.
Но я настоятельно рекомендую для хранения подключения использовать отдельный объект TADOConnection.

В свойстве CommandText укажите текст команды для загрузки данных.
Текстом команды могут являться:

  • SQL — запрос. (Примеры: «SELECT * FROM EMPLOYEE», «EXEC ALL_LANGS» и т.д.). При этом свойство CommandType=cmdText.
  • Имя таблицы. (Примеры: «EMPLOYEE», «CUSTOMER», «PROJECT»). CommandType=cmdTableDirect.
  • Имя хранимой процедуры. (пример: «ALL_LANGS», «SHOW_LANGS»). CommandType=cmdStoredProc.

Команда в CommandText должна возвращать набор данных. К примеру, в тексте нельзя использовать INSERT, DELETE или UPDATE, а
так же указывать хранимые процедуры, которые не возвращают набор данных (Recordset).

Перейдем к практическому примеру демонстрирующему возможности Delphi при работе c Firebird/InterBase:

  • Создадим простейший редактор служащих на основе TADODataSet.
  • Перемещаться по записям будем при помощи компонента TDBNavigator.
  • Список служащих отобразим в TDBGrid.
  • Для редактирования деталей воспользуемся контейнером TDBCtrlGrid в который поместим TDBLabel, TDBText, TDBComboBox.

Законченный пример показан на картинке:

TADODataSet

Далее я приведу последовательность действий для воспроизведения примера саммостоятельно:

  • Добавьте на форму и настройте компонент TADOConnection.
  • Добавьте компонент TADODataSet и настройте его следующим образом:
    • CommandText = «select EMP_NO, FIRST_NAME, LAST_NAME, FULL_NAME, JOB_COUNTRY, DEPT_NO from EMPLOYEE»;
    • CommandType = cmdText;
    • Active = true;
  • Добавьте компонент TDataSource с вкладки Data Access:
    • Установите свойство DataSet = ADODataSet1;
  • Добавьте на форму 2 компонента c вкладки Data Controls: TDBNavigator и TDBCtrlGrid:
    • Каждому установите свойство DataSource = DataSource1;
    • У TDBCtrlGrid установите RowСount = 1;

TDBCtrlGrid — это компонент-контейнер. Он может отображать как одну так и несколько записей в зависимости от значения свойства RowCount.
Внутри него расположены компоненты для редактирования данных.

TDBLabel — нередактируемое поле. При помощи этого компонента будем отображать колонку FULL_NAME.

Для редактирования полей FIRST_NAME, LAST_NAME добавим на форму 2 компонента TDBText, у которых установим свойство DataField.

Поле JOB_COUNTRY связано внешним ключом с таблицей COUNTRY. Для редактивания этого поля используем выпадающий список TDBComboBox содержащий коды стран. После
установки поля DataField = JOB_COUNTRY, необходимо заполнить список данными из таблицы COUNTRY.

Кроме описанных в примере, на вкладке Data Controls расположены компоненты для редактирования изображений (TDBImage), многострочного текста (TDBMemo), отображения списков и т.д.

Передача изменений в БД — технология обновляемых множеств

IBProvider поддерживает замечательную возможность OLE DB — технологию обновляемых множеств.
Благодаря этой технологии, в приведенном примере не пришлось прописывать в коде логику передачи изменений обратно в БД.
Все изменения передавались автоматически через обновляемые множества. На данный момент технологию обновляемых множеств поддерживает только IBProvider v2.
Пожалуйста учитывайте это при выборе провайдера для подключения.

Клиентские и серверные курсоры

Для перемещения по записям IBProvider использует курсоры. Они бывают серверные и клиентские.

Клиентский курсор — хранится на стороне клиента.
После выполнения запроса всё результирующее множество загружается в оперативную память или swap-файл драйвера.
Это может вызывать задержки при передаче больших объемов данных, особенно по сети.
По умолчанию в dbGo используется именно клиентский тип курсора. Это дает преимущество в быстродействии на небольших объемах данных. С клиентскими курсорами могут работать все три провайдера
из состава IBPRovider Professional Edition.

Серверный курсор используется при работе с большими наборами данных, которые невыгодно пересылать клиенту целиком.
В результате сразу после выполнения запроса клиенту возвращается курсор, а данные считываются по мере необходимости.
Кроме того реализация IBProvider, позволяет считывать BLOB-поля по мере обращения к ним, а не при получении всей записи.
Это так же повышает производительность, в случае если в одной записи содержится несколько BLOB-колонок с большими данными.
В текущей реализации серверный курсор можно использовать только с IBProvider v1, т.к. только он поддерживает 4-х байтные закладки, которые используются в dbGo для работы в режиме серверных курсоров.
Более новые версии IBProvider используют уже 8-байтные закладки.

За тип курсора отвечает свойство CursorLocation. Оно может принимать значения clUseServer и clUseClient.

Какой провайдер выбрать?

В состав IBProvider Professional входят три OLE DB провайдера, которые предоставляют разработчику различные возможности.

IBProvider v1 — на данный момент единственный провайдер, позволяющий работать с dbGo компонентами в режиме серверных курсоров. Это связанно с ограниченным размером закладок в dbGo = 4 байта.
Мы уже запланировали сделать IBProvider v3 совместимым с серверными курсорами dbGo в Delphi и C++ Builder. Как только это будет реализовано, IBProvider v1 станет историей.
Для использования провайдера, укажите в строке подключения Provider=LCPI.IBProvider.1

IBProvider v2 поддерживает технологию обновляемых множеств. Благодаря им появляется возможность передавать изменения обратно в БД без
явного указания текстов команд на вставку/удаление/обновление. Провайдер самостоятельно генерирует SQL-команды на основании select-выражений. Пример использования этой технологии я привел выше.
Для использования провайдера, укажите в строке подключения Provider=LCPI.IBProvider.2.
В наших ближайших планах реализовать поддержку обновляемых множеств в IBProvider v3.

IBProvider v3 самый современный и производительный из всех провайдеров. Он обладает уникальным набором технологий, поддерживает все кодовые страницы,
специальные возможности последних версий серверов Firebird и InterBase, 64-битные операционные системы и множество других полезных функций.
Если вы не планируете использовать обновляемые множества или серверные курсоры однозначно выбирайте IBProvider v3.
Для этого укажите в строке подключения Provider=LCPI.IBProvider.3 или Provider=LCPI.IBProvider.

Если вам необходимо использовать возможности всех трех драйверов в одном Delphi-приложении, то в качестве временного решения, рекомендую использовать три одинаковых подключения к БД с разными драйверами.
К примеру, все основные операции пропускать через IBProvider v3, обновляемые множества через IBProvider v2, а кешировать данные через серверные курсоры с IBProvider v1.
После того как обновляемые множества и поддержка серверных курсоров dbGo будет доступна в IBProvider v3 можно будет оставить только одно подключение.

Компонент TADOQuery

Компонент TADOQuery предназначен для выполнения SQL-команд. Его можно рассматривать, как аналог компонента TSQLQuery из dbExpress.
Связь с базой данных устанавливается через свойства Connection или ConnectionString.
Текст запроса записывается в свойство SQL.
Если запрос возвращает набор данных, следует использовать метод Open() или свойство Active=true.
Если запрос не должен возвращать набор данных, то его необходимо выполнять с помощью метода ExecSQL.
ExecSQL возвращает число записей, которые были обработаны во время выполнения запроса. Это же значение содержится в свойстве RowsAffected.

SQL-запросы с параметрами

Запросы могут быть параметризованными. При помощи параметров команды можно задавать условия и передавать данные серверу БД. Существуют два вида параметров: Именованные и позиционные. По умолчанию именованные параметры в IBProvider задаются через двоеточие (:), a позиционные через знак вопроса (?).

Для демонстрации работы компонента TADOQuery напишем пример, в котором рассмотрим три варианта его применения:

  • Выполнение запросов с именованными параметрами. ExecSQL и RowsAffected.
  • Выполнение запросов с позиционными параметрами. ExecSQL и RowsAffected.
  • Выполнение запросов возвращающих множество. Метод Open() или свойство Active.

Законченный пример показан на картинке. Для выборки множества, а так же для выпонлнения операций вставки/удаления используется компонент TADOQuery:

TADOQuery

Компоненты на форме связаны следующим образом:
TADOQuery запрашивает данные у БД, которая указана в TADOConnection и передает их в компонент-посредник TDataSource. TDBGrid умеет отображать данные, которые загружены
в TDataSource. Получается следующая схема взаимодействия: ADOConnection->ADOQuery->DataSource (вкладка Data Access)->DBGrid.

Рассмотрим первый вариант использования TADOQuery — выполнение запросов, которые не возвращают результирующее множество:

// перехватываем все ошибки обращения к БД Firebird/InterBase из Delphi
try

  // открываем подключенеи Delphi Firebird
  ADOConnection1.Open();
  ADOConnection1.BeginTrans();

  // Позиционные параметры
  with ADOQuery1 do
    begin
        // текст запроса
        with SQL do begin
          Clear;
          Add('DELETE FROM COUNTRY WHERE Country=? and Currency=?');
        end;

        with Parameters do begin
             Clear;
             // вариант 1
             AddParameter().Value :='Turkey';
             // вариант 2
             AddParameter();
             Items[1].Value :='Lira';
        end;

        // вставка одной записи
        ExecSQL();
        Memo1.Lines.Add ('ExecSQL. Affected Records: ' + IntToStr(ADOQuery1.RowsAffected));
  end;

  // Именованные параметры
  with ADOQuery1 do
    begin
        with SQL do begin
          Clear;
          Add('INSERT INTO COUNTRY (country, currency) VALUES (:c1, :c2)');
        end;

        // устанавливаем 2 параметра за раз
        Parameters.ParamValues['c1;c2'] := VarArrayOf (['Turkey', 'Lira']);

        ExecSQL();
        Memo1.Lines.Add ('ExecSQL. Affected Records: ' + IntToStr(ADOQuery1.RowsAffected));
    end;

    ADOConnection1.CommitTrans();
  except on E : Exception do
    // откатываем транзакцию в случае ошибки
    begin
      ADOConnection1.RollbackTrans();
      ShowMessage(E.ClassName+' db error: '+E.Message);
    end;
  end;

  // закрываем подключение
  ADOConnection1.Close();

В приведенном примере параметры команды устанавливаются двумя различными способами:

1. Одним выражением за раз:

ADOQuery1.Parameters.ParamValues['c1;c2'] := VarArrayOf (['Turkey', 'Lira']);

2. И каждый параметр отдельно:

with Parameters do begin
     Clear;
     AddParameter().Value :='Turkey';
     AddParameter();
     Items[1].Value :='Lira';
end;

Для выборки данных через TADOQuery используется SQL-выражение, содержащее команду SELECT, и метод Open():

try

// открываем подключение Delphi InterBase
ADOConnection1.Open();
ADOConnection1.BeginTrans();

  with ADOQuery1 do
   begin
     // текст запроса
     with SQL do begin
        Clear;
        Add('SELECT * FROM EMPLOYEE WHERE EMP_NO > ?');
     end;

     with Parameters do begin
        Clear;
        AddParameter().Value :='10';
     end;

     // открываем Recordset через метод Open(),
     // вместо этого так же можно установить свойство Active=true
     Open();
   end;

ADOConnection1.CommitTrans();

except on E : Exception do
 // откат транзакции в случае ошибки
 begin
    ADOConnection1.RollbackTrans();
    ShowMessage(E.ClassName+' db error: '+E.Message);
 end;

ADOConnection1.Close();

TADOStoredProc

Компонент TADOStoredProc позволяет выполнять хранимые процедуры InterBase и Firebird. Принцип работы TADOStoredProc такой же как и у TADOQuery:

  • Подключение задается в свойстве Connection или ConnectionString.
  • Метод Open() или свойство Active=true позволяют получить результирующее множество.
  • Метод ExecSQL позволяет выполнить процедуру и вернуть результат в наборе OUT-параметров.

Единственные отличием является свойство ProcedureName в котором определяется имя хранимой процедуры. Свойство SQL в компоненте TADOStoredProc отсутствует

Хранимая процедура может возвращать результат двумя способами:

  • в виде результирующего множества;
  • в виде выходных OUT-параметров;

Для получения результирующего множества используется оператор SELECT:

select * from stored_procedure_name(…)

Для выполнения процедуры, которая ничего не возвращает или возвращает OUT-параметры необходимо использовать инструкцию exec:

exec procedure stored_procedure_name

Компонент TADOCommand

TADOCommand — команда, которая передается серверу, для того чтобы считать или изменить данные. Компонент фактически объединяет в себе возможности 3-х рассмотренных компонентов: TADOTable, TADOQuery, TADOStoredProc.

Поведение TADOCommand меняется в зависимости от свойства CommandType. Оно может принимать следующие значения:

  • cmdText — текст команды содержит SQL-запрос. Поведение аналогично TADOQuery.
  • cmdStoredProc — в свойстве CommandText задано имя хранимой процедуры. Поведение соответствует компоненту TADOStoredProc.
  • cmdTable и cmdTableDirect — означают, что в тексте указано имя таблицы, которую необходимо загрузить. Для Firebird и InterBase значения cmdTable и cmdTableDirect идентичны. Соответствует TADOTable.

Работа с транзакциями

Механизмы управления транзакциями в IBProvider одинаковы для всех средств разработки.
Ранее я подробно расматривал транзакции в первой части руководства по работе с InterBase и Firebird в ADO .Net.
Рекомендую вам обратить внимание на следующие его главы:

  • Автоматическое управление транзакциями;
  • Уровни изоляции транзакций;
  • Именованные точки сохранения;
  • Commit Retain и Rollback Retain и Firebird;

Полезные ссылки

  • Примеры работы с Firebird из Delphi минуя dbGo, напрямую через COM-объекты ADO.
  • Скачать Firebird.

Теги статьи: Firebird delphi, InterBase Delphi, dbGo, ADO Express, Firebird OLE DB Direct/Express.


Дата публикации:

30.11.2008

. Права на материал принадлежат: IBProvider. При перепечатке ссылка на сайт https://www.ibprovider.com/rus обязательна.

 
Yrtimd
 
(2005-11-17 00:30)
[0]

// Delphi 7, FIBPlus 6.25, Firebird 1.5.2

Try
 DB.Open; // DB: TpFIBDatabase
Except
 on EFIBError do begin
   MessageDlg («Какая-то ошибка!»);
 end;
End;

Используя данный код, при ошибке подключения к БД, нельзя  установить конкретную причину (нет связи с сервером, автоизация, не найдена база данных и пр.).
Как обрабатывать ошибку, чобы выявить конкретную причину?


 
YuRock ©
 
(2005-11-17 10:29)
[1]

В IBX делается так:

Try
DB.Open; // DB: TpFIBDatabase
Except
on E: EIBError do Err( Format( "ErrorCode: %d, SQLErrorCode: %d, Error: "%s"", [ E.IBErrorCode, E.SQLCode, E.Message ] ) );
End;

Может, поможет… В FIB не знаю.


 
Виталий Панасенко
 
(2005-11-17 10:38)
[2]

EFIBError                  = class(EDatabaseError)
 private
   FSQLCode: Long;
   FIBErrorCode: Long;
   FSQLMessage :string;
   FIBMessage  :string;
   SenderObj :TObject;
 public
   constructor Create(ASQLCode: Long; const aMsg: String;Sender:TObject);
   constructor CreateEx(ASQLCode: Long; const IBMsg,SQLMsg: String;Sender:TObject);
   property    SQLCode    : Long read FSQLCode ;
   property    IBErrorCode: Long read FIBErrorCode ;
   property    SQLMessage :string read FSQLMessage;
   property    IBMessage  :string read FIBMessage;
 end;


 
Yrtimd
 
(2005-11-17 20:44)
[3]

Спасибо. Но необходимо не получить описание ошибки, а дифференцировать обработчик исключения таким образом:
case E of
 сервер не найден: do …
 база не найдена: do …
 ошибка авторизации пользователя: do …
end;


 
YuRock ©
 
(2005-11-17 22:32)
[4]

Автор, читай, что тебе пишут.

EFIBError.IBErrorCode и EFIBError.SQLCode ты думаешь просто так, ради объема тебе привели в примере?


 
Yrtimd
 
(2005-11-18 00:10)
[5]

А откуда узнать значения кодов ?..


Исключение – это
определенная пользователем ошибка,
которая имеет имя и связанный с ним
текст сообщения.

Для создания
исключения служит оператор:

CREATE EXCEPTION Имя
‘Текст
сообщения’;

Для возбуждения
исключения в теле триггера или хранимой
процедуры служит оператор:

EXCEPTION Имя;

При
возникновении исключения нормальный
ход выполнения прерывается и Firebird
ищет ближайший обработчик этого
исключения в текущем блоке BEGIN … END. Если
обработчика исключения в этом блоке
нет, то Firebird
поднимается на уровень выше (к внешнему
блоку BEGIN … END) и ищет обработчик там, и
т.д. Если обработчик исключения найден,
то выполняется его код и далее управление
передается на первый оператор за
обработчиком исключения. В случае если
было сгенерировано исключение, которое
никто не обрабатывает, это исключение
передается в клиентское приложение.
Чаще всего это приводит к выдаче
пользователю сообщения о произошедшей
ошибке.

Синтаксис оператора
для обработки исключений внутри тела
триггера или хранимой процедуры имеет
следующий вид:

WHEN {<ошибка>
[, <ошибка>
]
| ANY} DO

<составной
оператор>

<ошибка>
= {EXCEPTION Имя
| SQLCODE Номер
| GDSCODE Код_Ошибки}

Параметры, входящие
в этот оператор, описаны в табл. 2

Таблица 2

Параметр

Описание

ANY

Позволяет обработать
все типы ошибок

EXCEPTION
Имя

Обработка
исключения, сгенерированного
пользователем (оператором EXCEPTION)

SQLCODE
Номер

Обработка ошибки
SQL

GDSCODE
Код_Ошибки

Обработка
ошибки Firebird

Операторы языка хранимых процедур и триггеров

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

If … then … else

Для реализации
сложных алгоритмов сервер InterBase
поддерживает операторы ветвления.

IF (условие)

THEN <составной
оператор>

[ELSE
<составной
оператор>]

FOR SELECT … DO

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

FOR <выражение
select>

DO <составной
оператор>

WHILE … DO

Оператор цикла.

WHILE (условие)

DO <составной
оператор>

Задание

Лабораторную
работу следует выполнять в следующем
порядке:

  1. Создать аналогично
    предыдущим лабораторным работам рабочую
    папку и назвать ее «ЛР5».

  2. Скопировать в эту
    папку файл сценария, созданный при
    выполнении лабораторной работы №4.

  3. Открыть в приложении
    «IBExpert» этот сценарий.

  4. Исправить текст
    комментариев и сделать, чтобы база
    данных теперь создавалась в папке
    «ЛР5».

  5. Добавить в сценарий
    операторы создания генераторов,
    исключений и триггеров. Для каждого
    суррогатного первичного ключа должен
    быть создан соответствующий генератор
    и триггер.

  6. Создать триггеры,
    реализующие ссылочную целостность,
    заменив одно ограничение внешнего
    ключа. Команды создания этого внешнего
    ключа из сценария не удалять, а добавить
    команду удаления этого ограничения
    перед созданием триггеров. Также
    добавить оператор создания индекса по
    полю бывшего внешнего ключа. Триггеры
    должны обеспечивать каскадное обновление
    и удаление зависимых данных, а также
    запрет ссылки на отсутствующую запись.

  7. Создать не менее
    пяти триггеров, реализующих бизнес-правила
    в соответствии с выданным индивидуальным
    заданием. Для каждого триггера должны
    присутствовать комментарии, поясняющие
    выполняемые операции.

  8. Выполнить сценарий
    и сохранить его в папке «ЛР5».

  9. Зарегистрировать
    созданную базу данных в программе
    «IBExpert» и подключиться к ней.

  10. Создать в своей
    базе данных таблицу в диалоговом режиме.
    Она должна называться «TestTriggers» и
    содержать десять полей разных типов с
    произвольными именами. При создании
    поля суррогатного первичного ключа
    сразу указать необходимость создания
    для него генератора и триггера. Добавить
    в диалоговом режиме к этой таблице три
    триггера выполняющих произвольные
    действия. Скопировать в отчет сценарий
    создания этой таблицы и триггеров,
    который сгенерирует «IBExpert».

  11. Создать в папке
    «ЛР5» резервную копию базы данных.

  12. Создать
    и сохранить в папке «ЛР5» файл с
    отчетом о выполнении лабораторной
    работы, который должен называться
    «Отчет.doc».

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]

  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #

Понравилась статья? Поделить с друзьями:
  • Обработка ошибок react native
  • Обработка ошибок 1с пример
  • Обработка ошибок python яндекс практикум
  • Обработка ошибки 404 spring
  • Обработка ошибок python selenium