Робота з клієнтськими наборами даних, Delphi, Програмування, статті

Mike Goblin, Майстри Delphi

Введення

У цій частині буде розглянуто застосування клієнтських наборів даних в dbExpress. Згідно ієрархії класів в Kylix до клієнтських наборів даних відносяться класи TSQLClientDataSet і TClientDataSet. Останній з них є частиною технології MIDAS. Так як на сьогоднішній день підтримка даної технології в Kylix до кінця не реалізована, то основну увагу ми приділимо розгляду TSQLClientDataSet.

Компоненти класу TSQLClientDataSet призначені для створення двухзвенних додатків клієнт сервер. Так само як і односпрямовані набори даних, вони використовуються для роботи з сервером БД через TSQLConnection. З іншого боку багато з методів і подій класу TSQLClientDataSet характерні для клієнтського датасета в технології MIDAS. Насправді TSQLClientDataSet – це гібрид, який містить в собі об’єкти однонаправленої набору даних, клієнтський набір даних і об’єкт провайдера для застосування внесених змін на сервері БД. “Запихання під капот” цих об’єктів дозволило істотно спростити розробку двухзвенних додатків баз даних в dbExpress.

Найпростіший проект

Робота з TSQLClientDataSet буде проілюстрована на прикладі простої бази даних службовців організації. В якості сервера БД обраний Interbase 6, т.к він входить в поставку Kylix. Попередньо необхідно створити базу даних з таблицею EMPLOYEERS, описаної наступним чином:

/* Table: EMPLOYEERS, Owner: SYSDBA */
CREATE TABLE "EMPLOYEERS" 
(
  "ID"	INTEGER NOT NULL,
  "NAME"	VARCHAR(200) NOT NULL,
 PRIMARY KEY ("ID")
);Сreate GENERATOR "EMP_GEN";
SET TERM ^ ;
/* Triggers only will work for SQL triggers */
CREATE TRIGGER "EMPLOYEERS_BEFORE_INS" FOR "EMPLOYEERS" 
ACTIVE BEFORE INSERT POSITION 0
AS
	BEGIN
          NEW.ID = GEN_ID(EMP_GEN,1);
	END
 ^
COMMIT WORK ^
SET TERM ;^

Вставимо кілька записів у створену таблицю. Текст запиту на вставку в таблицю виглядає так:
Insert into Employeers (Name) values ​​’петов’;
Insert into Employeers (Name) values ​​’Сидоров’;

Далі запустимо IDE Kylix і створимо новий додаток. На головній формі програми розмістимо наступні компоненти із закладки dbExpress і встановимо для них нижчезазначені властивості
sc_conn: TDBConnection – налаштувати для з’єднання з створеної БД. (Як це зробити див. “Коннект – є коннект”). Св-во Connected – встановити true.
scd_emp:TSQLClientDataSet
    DBConnection – sc_conn
    CommandText – select ID,NAME from EMPOYEERS

Подвійним кліком миші викличемо редактор полів. В редакторі полів правою кнопкою миші викличемо спливаюче меню і в ньому виберемо пункт Add all fields. При цьому поля набору даних буде визначено явним чином. Виберемо поле ID і встановимо його властивість Required в false, щоб зняти необхідність ручного введення значення ID при вставці користувачем нового запису. Після цього св-во Connected компоненту sc_conn встановимо в false.

ds_src:TDataSource
		DataSet:scd_emp
DBNavigator1:TDBNavigator
		DataSource - ds_src
		Align - alTop
Panel1:TPanel
		Align - alBottomCaption - "" (порожній рядок)
DBGrid1:TDBGrid
		DataSource - ds_src
		Align - alTop

На Panel1 розмістимо 4 (Button) кнопки c іменами b_connect, b_disconnect, b_count, b_fetch (заголовки – Caption – connect, disconnect, get count, fetch all соответсвтенно) і один напис (Label).
На подію onClick кнопки b_connect навісом обробник з наступним кодом

Sc_conn.Connected:=true;
Scd_emp.Active :=true;

На подію onClick кнопки b_disconnect навісом обробник з наступним кодом

Sc_conn.Connected:=false;
Scd_emp.Active :=false;

Призначення розміщених компонентів наступне
Sc_conn – з’єднання з базою даних
Sc_emp – набір даних для роботи з таблицею БД employers
Ds_src – представлення даних sc_emp для компонентів для користувача інтерфейсу “чутливих” до даних.

Запустимо на виконання наш проект, при цьому передбачається, що сервер interbase вже запущений. При натисканні кнопки b_connect в сітці даних (DBGrid) можна буде бачити записи таблиці employeers.

Навігація по записах

Методи навігації по записах аналогічні односпрямованим наборам даних.

Додавання, видалення та редагування записів

Для додавання записів існують чотири методи






















Метод Опис
Append Додавання порожній записи в кінець набору даних. Курсор поміщається на додану запис і набір даних переходить в режим редагування.
Insert Додавання порожній записи в поточну позицію набору даних. Курсор поміщається на додану запис і набір даних переходить в режим редагування.
AppendRecord(const Values: array of const)
Додавання запису в кінець набору даних. Поля передаються через параметр Values
InsertRecord(const Values: array of const) Додавання запису в поточну позицію набору даних. Поля передаються через параметр Values

Приклади додавання записів:

/ / Використання Append
scd_emp.Append;
scd_emp.FieldByName('ID').Value:=-1;scd_emp.FieldByName ('Name'). Value: = 'Петров';
scd_emp.Post;
/ / Використання AppendRecordscd_emp.AppendRecord ([1, 'Петров']);

Визначено декілька подій, пов’язаних з вставкою нового запису

















Подія Опис
BeforeInsert Подія, що генерується перед вставкою нового запису в набір даних.
AfterInsert Подія, що генерується після вставкою нового запису в набір даних
OnNewRecord Подія, що генерується при вставці нового запису в набір даних

При необхідності скасувати вставку записи всередині обробника події можна викликати метод Abort.
Порядок виклику подій

  1. BeforeInsert
  2. OnNewRecord
  3. AfterInsert

Для видалення поточного запису призначений метод Delete, події BeforeDelete і AfterDelete генеруються до і після видалення запису відповідно.
Приклад:

scd_emp.Delete;

Переклад набору даних в режим редагування здійснюється викликом методу Edit. При цьому перевірити доступність редагування можна, проаналізувавши властивість CanModify. Ще одним корисним методом є метод CheckBrowseMode. Даний метод автоматично підтверджує або скасовує зроблені зміни перед тим, як буде здійснений перехід на наступний запис в наборі даних.

Події BeforeEdit і AfterEdit виникають відповідно перед і після редагування запису.

Підтвердження і відкат зроблених змін

Так як клієнтський набір даних буферизирует зроблені зміни, то застосовується двоступенева підтвердження зроблених змін. Перший ступінь – це запис зроблених змін в буфер набору даних, друга – запис змін з буфера в сервер БД.

Запис змін в буфер здійснюється викликом методу Post. Події BeforePost і AfterPost генеруються перед і після підтвердження змін. Багато хто з компонентів для користувача інтерфейсу для роботи з даними викликають метод Post автоматично при переході на наступний запис набору даних.

Скасування запису в буфер набору даних здійснюється викликом методу Cancel. Події BeforeCancel і AfterCancel генеруються перед і після підтвердження змін.

Зміни, зроблені в буфері, SQLClientDataSet зберігає у властивості Delta. Кількість змін зберігається у властивості ChangeCount. Запис зроблених змін з буфера в БД здійснюється викликом ApplyUpdates. Як параметр функції передається максимальна кількість помилок, допустимих до завершення методу. Функція повертає кількість виниклих помилок. Якщо в результаті застосування змін кількість помилок не перевищило заданого, то успішно передані записи видаляються з властивості Delta (тобто вважаються переданими на сервер БД), інакше всі записи вважаються не переданими.
Приклад:

/ / Передача змін з буфера в БД
if scd_emp.ChargeCount > 0 then
if scd_emp.ApplyUpdates(10) > 0 thenApplication.MessageBox (‘Виявлено помилки “);

При виклику ApplyUpdates SQLClientDataSet генерує набір SQL операторів для передачі кожної вставленої, віддаленій і зміненої записи в БД.

При передачі змін на сервер БД виникає задача визначення відповідності зміненої записи з буфера набору даних і запису в БД (тобто формування частини where SQL запиту). Властивість UpdateMode визначає цей критерій. Можливі значення св-ва наведені нижче

Наявність події OnUpdateData дозволяє встановити параметри оновлення для кожного запису, переданої на сервер БД.

Відкат всіх зроблених змін здійснюється за допомогою методу CancelUpdates. Даний метод очищає св-во Delta, таким чином, скасовуючи всі зміни в буфері набору даних.

Відкат останньої виконаної операції виконується викликом UndoLastChange. Передача True як параметр методу UndoLastChange змушує курсор переміщатися на відкочуємося запис.

Але й це ще не все! Можна відкочувати назад на довільну кількість операцій (тут під операцією розуміється вставка, редагування, видалення). Для цього існують так звані точки збереження (SavePoint).
Техніка така:

Якщо трохи помізкувати, то використовуючи точки збереження, можна організувати не тільки Undo, а й Redo.

Залишилося внести деякі доробки в наш проект, щоб зроблені зміни були відправлені на сервер БД. Для цього виконаємо наступні дії:
1. Оголосимо глобальну змінну id типу integer. Робиться це в секції var модуля головної форми, дана секція буде виглядати так

var
Form1:Tform1;Id: integer; / / Лічильник для поля id, оголошений нами
2. В обробнику події AfterPost scd_emp инициализируем змінну id
id:=-1;
3. В обробнику BeforePost scd_emp використовуємо id для заповнення поля id фіктивним значенням (реально значення присвоюється на сервері).

If scd_empID.IsNull then
Begin
Scd_empID.Value:=id;
Dec(id);
End;
4. В обробнику події BeforeRefresh scd_emp організуємо відправлення даних на сервер.

if scd_emp.ChangeCount > 0 then
if scd_emp.ApplyUpdates(0) > 0 then
Abort
else
id:=-1;

Запустимо отримане додаток, спробуємо вводити або змінювати записи – до натискання кнопки поновлення DBNavigator1 всі наші зміни не будуть відображатися на сервері БД. Закриття програми із змінами, не відправлені на сервер, призводять до втрати цих змін.

Обробка помилок

Обробка помилок також ділиться на обробку помилок роботи з буфером і обробку помилок передачі даних на сервер БД. Для обробки помилок вставки, видалення і редагування в компоненті TSQLDataSet існують кілька видів подій


















Подія Опис
OnDeleteError Виникає при наявності помилок видалення запису
OnEditError Виникає при наявності помилок редагування або вставки записи
OnPostError Виникає при наявності помилок запису зроблених змін в буфер клієнтського набору даних

Обробники вищеперелічених подій в якості одного з параметрів отримують параметр Action типу TDataAction. Змінюючи значення цього параметра в обробнику можна варіювати реакцію на що відбулася помилку. Можливі значення

Клонування таблиці

Зазначено в розділі Робота з локальними базами даних в Kylix.

Робота з локальними базами даних в Kylix

Під локальними ми будемо розуміти бази даних, файли яких розташовані в файлах на локальному диску комп’ютера або в локальній мережі. Доступ до цих файлів здійснюється додатком безпосередньо.

В Delphi 5, продовжувачем якої є Kylix, для роботи з локальними базами даних використовувалося кілька підходів.


  1. Використання бібліотек BDE, ADO, ODBC для доступу до локальних баз формату DBase, Paradox.
  2. Використання TСlientDataSet для роботи з локальними базами даних формату cds або xml. Формати даних файлів є винаходом Borland.

В Kylix розробка компонентів для створення локальних баз даних першого типу віддана на відкуп розробникам сторонніх фірм. Пов’язано це насамперед з тим, що дані формати даних є отмирающими, тим більше що конвертіровніе їх у формат xml не викликає великих труднощів.

Другий тип баз даних, що одержав найменування MyBase, надає додаткові можливості, такі як

Для ілюстрації всього вищесказаного створимо додаток для перегляду і редагування замовлень.

Створення заготовки програми. Меню File / New Application створить проект з порожньою формою. Додамо модуль даних – File / New. У діалозі вибрати пункт DataModule.

Створення файлу бази даних. В модуль даних помістимо компонент ClientDataSet з закладки DataAccess. Св-во Name встановимо – Clients. Даний датасет буде зберігати інформацію про замовників. Для створення файлу бази даних необхідно вказати поля та їх типи. Зробити це можна двома способами:
a) Визначити FieldDefs
b) Створити об’єкти полів явним чином.

Особисто я віддаю перевагу визначити FieldDefs, а потім на їх основі створити об’єкти полів :))

Отже, подвійний клік на св-ве FieldDefs компонента Clients відкриє діалог роботи з визначеннями полів. Додамо такі визначення полів

















Ім’я (Name) Тип (Type) Розмір (Size)
ID ftAutoInc 0
Name ftString 50
Де ID – лічильник, Name – ім’я клієнта

Правою кнопкою мишки кликнемо на Clients і виберемо в спадному меню пункт CreateDataSet, а потім Save To MyBase Xml UTF-8 table. У діалозі вкажемо ім’я xml файлу, який буде зберігати дані про клієнтів – Clients.xml.

Було б непогано, щоб при старті програми ClientDataSet читав дані зі створеного нами xml файлу. Для цього св-во FileName має дорівнювати повного імені (з шляхом) xml файлу. Для Clients це / шлях до файлу / Clients.xml.

Тепер визначимо поля явно на основі FieldDefs. Подвійний клік на Clients, в діалозі правою кнопкою миші викликаємо контекстне меню, вибираємо пункт Add all fields. Потім DataSource – ds_Clients, розмістимо в модулі даних і зв’яжемо c Clients (св-во DataSet компонента ds_Clients встановимо рівним Clients).

Формат xml таблиці БД, відкат змін

Подивимося, як всередині влаштований xml файл бази даних. Після створення датасета типовий файл БД виглядає так:


<?xml version=”1.0″ encoding=”UTF-8″ standalone=”yes” ?>
<DATAPACKET Version=”2.0“>
<METADATA>
<FIELDS>
<FIELD attrname=”ID” fieldtype=”i4” readonly=”true” SUBENGINE=”Autoinc” />
<FIELD attrname=”Name” fieldtype=”string” WIDTH=”50” />
</FIELDS>
<PARAMS DEFAULT_ORDER=”” AUTOINCVALUE=”1” />
</METADATA>
<ROWDATA />
</DATAPACKET>
Рядок 1
У рядку 1 розташований заголовний тег
Рядок 2
Кореневий тег документа, а от далі й починаються теги, на які варто звернути увагу, зокрема на рядки 3 і 10.
Рядок 3
Рядок 10

Так от всю таблицю можна розділити на дві частини: дані про структуру таблиці БД, що зберігаються у файлі (метадані) і власне самі записи. Як ви вже здогадалися, метадані зберігаються в теге METADATA, а записи в ROWDATA, природно що при створенні нової таблиці БД тег ROWDATA буде порожнім.

Усередині тега METADATA розташовані опису полів таблиці (тег FIELDS і вкладені в нього теги) та інша службова інформація (порядок сортування за замовчуванням, початкове значення автоінкрементального лічильника).

Тепер давайте запустимо наш додаток, вставимо в таблицю новий запис, закриємо додаток і подивимося як змінився xml файл.

Змінився тег PARAMS, тепер він виглядає так:


<PARAMS CHANGE_LOG=”1 0 4” AUTOINCVALUE=”2” DEFAULT_ORDER=”” />і тег ROWDATA став непустою:
<ROWDATA>
<ROW RowState=”4” ID=”1” Name=”e?AI?” />
</ROWDATA>

Уважно подивившись, на зміни ми побачимо, що всередині таблиці ведеться журнал операцій. Це дає можливість відкоту зроблених змін. Детальніше це описано вище для SQLClientDataSet. Додамо, що виклик методу MergeChangeLog робить всі зміни, зроблені до його виклику недоступними для відкоту, тобто очищає журнал дій.

Якщо ж Вам зовсім не потрібно, щоб журнал вівся, в runtime встановіть св-во LogChanges: = false.

Обробка помилок, що можуть виникати при описаних діях аналогічна нагоди описаному для SQLClientDataSet.

Клонування таблиці

Оскільки TClientDataSet тримає дані з таблиці в пам’яті, з’явилася можливість спільного використання одних даних двома датасетамі. Клонування здійснюється викликом методу CloneCursor

Procedure CloneCursor(Source:TCustomClientDataSet;Reset:Boolean;KeepSettings:Boolean = false)

Параметр Source – джерело клонованих даних
Параметри Reset і KeepSettings визначають установку св-в фільр, індекси, Read Only, MasterSource, MasterFields. Коли обидва параметри fasle зазначені св-ва копіюються з датасета-джерела, Reset: = true – Дані св-ва скидаються, KeepSettings: = true – залишаються без змін, при цьому сумісність їх з даними джерела клонування залишається на совісті програміста.

Установка відносин головний – підлеглий (master-detail)

Перший із способів – це завдання св-в MasterSource і MasterFields. Цей спосіб традиційний ще в Delphi і ми розглядати його тут не будемо – читайте книжки.

Новим способом організації відносини master-detail стало використання вкладених датасетов. Ось про це і піде мова. Припустимо ми хочемо мати інформацію про покупки зроблених клієнтом.

Спочатку очистимо датасет Clients – клацнемо правою кнопкою миші і в контекстному меню виберемо – Clear Data.

Введемо додаткове FieldDefs Orders – типу ftDataSet. Даний тип поля призначений для зберігання всередині себе датасетов. Набір полів вкладеного датасета визначається в свойсвте ChildDefs. Визначимо в ChildDefs наступні поля






















Ім’я (Name) Тип (Type) Розмір (Size)
ID FtAutoInc 0
OrderName ftString 20
Price ftCurrency 0
ID – лічильник, OrderName – опис замовлення, Price – ціна замовлення.

Залишилося тільки створити на основі створених визначень створити датасет (клацнувши правою кнопкою і вибравши Create DataSet), зберегти файл (Save to MyBase xml table) і на основі цих визначень явно створити поля (подвійний клік на Clients, права кнопка миші – add all fields). Відкривши створений xml файл ми побачимо наступне


<?xml version=”1.0″ encoding=”UTF-8″ standalone=”yes” ?>
– <DATAPACKET Version=”2.0″>
– <METADATA>
– <FIELDS>
<FIELD attrname=”ID” fieldtype=”i4″ readonly=”true” SUBENGINE=”Autoinc” />
<FIELD attrname=”Name” fieldtype=”string” WIDTH=”50″ />
– <FIELD attrname=”Orders” fieldtype=”nested”>
– <FIELDS>
<FIELD attrname=”ID” fieldtype=”i4″ SUBENGINE=”Autoinc” />
<FIELD attrname=”OrderName” fieldtype=”string” WIDTH=”20″ />
<FIELD attrname=”Price” fieldtype=”r8″ SUBENGINE=”Money” />
</FIELDS>
<PARAMS AUTOINCVALUE=”1″ />
</FIELD>
</FIELDS>
<PARAMS DEFAULT_ORDER=”” AUTOINCVALUE=”1″ />
</METADATA>
<ROWDATA />
</DATAPACKET>

Неважко переконатися в тому, що поле Orders містить в собі опис підпорядкованої таблиці. При цьому в сітці даних DBGrid1, розташованої на головній формі, з’явився новий стовпець Orders. При запуску програми і спробі редагування цього поля автоматично відкривається форма для редагування вкладеного набору даних.

Іншим способом організації взаємодії з вкладеним датасетом є розміщення в модулі даних додаткового ClientDataSet. Помістимо в модуль даних ще один компонент типу TClientDataSet, встановивши його ім’я Orders. Св-ву DataSetField компонента Orders зі списку привласнимо значення ClientsOrders. Все тепер користуючись компонентом Orders можна переглядати і редагувати вкладений датасет.

Переваги вищеописаного методу в тому, що вся база буде зберігатися в одному xml файлі, недолік ж – не можна розірвати зв’язок головний-підлеглий і як наслідок одночасно переглянути всі записи про замовленнях незалежно від обраного клієнта.

  
Увага! Передрук даноїстатті або її частини без узгодження з автором. Якщо ви хочете мати цюстаттю на своєму сайті або видати в друкованому вигляді, зв’яжіться з автором.
Автор статті:  Mike Goblin

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*