Сегодня картинок тоже будет не мало. В продолжение темы «Windows 7 x64, IIS, ISAPI и DataSnap XE2 в картинках» решил я поработать с базой данных. Получать от сервера ReverseString, конечно, весело («Оно работает!», «Уау!» и всё такое прочее), но на деле врядли кому-то такой сервер пригодиться. Поэтому решил расширить свой кругозор и доработать уже имеющийся ISAPI-модуль и клиента, чтобы они смогли обмениваться данными — сервер будет читать данные из базы данных и отправлять их клиенту. Клиент, в свою очередь, будет получать данные с сервера, работать с ними и отправлять изменения на сервер.
Вообще, т.к. наш ISAPI-модуль предназначен для IIS, то, наверное, логичнее было бы рассказать про работу c MSSQL, а не с MySQL. Но, признаюсь, попытка установки MSSQL Server на мой компьютер была неудачной по причине того, что меня просто откровенно задолбало выкачивать с сайта Microsoft тучу каких-то сервис-паков к дистрибутиву, которые в итоге просили ещё не меньшую кучу обновлений. В общем плюнул я на это дело и решил установить MySQL — всё-таки это очень популярная СУБД, да и провайдеры, предоставляющие Win-хостинг обычно дают доступ и к MSSQL и к MySQL (например, МастерХост). Да и, в принципе, какая нам сегодня разница, что за СУБД будет использоваться MSSQL, MySQL или вообще FireBird? Основная задаче сегодня — «научить» сервер и клиент работать с данными из базы, а её (БД) тип — дело второе.
Установка и настройка необходимого ПО
Итак, начнем с установки необходимого программного обеспечения и его настройки. Для сегодняшней работы скачал у установил следующее ПО:
- MySQL 5.0 с сайта mysql.ru
- Для работы с базами данных был скачан с mysql.com и установлен MySQL Workbanch
По умолчанию MySQL установится в Windows 7 x64 в директорию c:\Program Files (x86)\MySQL\MySQL Server 5.0\. В этой же директории Вам будет предложено хранить файлы БД — я при установки оставил все настройки без изменений.
Теперь подготовим нашу тестовую БД с помощью MySQL Workbench. Запускаем программу и создаем новое подключение:
В общем случае, настройки должны быть такими:
- HostName = 127.0.0.1 (или localhost)
- Port = 3306
- User = root
- Password = (заданный при настройке MySQL)
После того, как соединение создано, дважды кликаем по его названию в списке и создаем новую БД:
Я создал БД с такими настройками:
- Name = dsbase
- Collation = utf8 — utf8_general_ci
Проще было бы работать с кодировкой cp1251, но в Сети всё же чаще используется utf8 — с этой кодировкой и будем баловаться разбираться. Теперь остается последний шаг — создать таблицу в нашей базе. Для этого дважды щелкаем в списке БД по нашей только что созданной, чтобы она стала активной, жмем кнопку «Create New Table…» (или вызываем контекстное меню в дереве у узла Tables) и создаем в редакторе новую таблицу:
Я создал таблицу с названием dstable и четырьмя полями — id, имя, фамилия и день рождения. Какую таблицу создадите Вы — не важно, главное, чтобы она была в БД.
Теперь приступим к работе в Delphi. И первым делом немного переделаем наш ISAPI-модуль.
Настраиваем сервер на работу с MySQL
Когда я рассматривал работу над ISAPI-dll в прошлый раз, то выбрал для класса с серверными методами предка TComponent. Тогда мне не требовалась никакая работа с наборами данных или невизуальными компонентами, поэтому я ограничился самым простым классом из предложенных. Сегодня же нам потребуются и TSQLConnection и другие невизуальные компоненты, поэтому удобнее будет использовать другого предка, например, TDSServerModule. Удаляем старый проект сервера — в нем нет ничего, кроме того, что было создано мастером и создаем новый, как я рассказывал в прошлый раз, но только в качестве предка для класса серверных методов выбираем TDSServerModule. Теперь в менеджере проектов наш сервер будет выглядеть так:
Пока ServerMethodUnit1 ничего не содержит и сейчас мы это исправим. Первым делом создадим новое подключение для MySQL в Data Explorer. Настройки соединения у нас должны быть следующие:
- ServerName = localhost
- Database Name = dsbase (её мы и создавали в Workbench)
- User Name/Password = заданным при настройке MySQL.
Проверяем соединение — соединение есть:
Но это оно (соединение) есть у меня. А вот у Вас его может и не быть с самого начала. Например, Вы можете получить вот такое нехорошее сообщение:
Поэтому сделаем небольшое лирическое отступление и посмотрим как с такой ситуацией бороться.
«DBX Error: Driver could not be properly initialized…» Как победить?
Во-первых, надо проверить наличие библиотеки libmysl.dll на компьютере. Эта библиотека должна располагаться либо в директории с проектом, либо в следующих местах:
Для x86 | Для x64 |
$BDS\bin\ | $BDS\bin64\ |
Windows\System32 | Windows\SysWOW64\ |
Если же после того, как Вы положили библиотеку по одному из предложенных выше путей соединение так и не хочет работать, то, тогда внимательно смотрите на версию самой библиотеки, т.к. не каждая (даже скачанная с официального сайта) dll может заработать. У меня соединение нормально заработало только с библиотекой для x86, которую Вы можете скачать по ссылке ниже:
Скачиваете архив, распаковываете DLL, например, в Windows\System32 и соединение заработает. Ну, а если не заработает…делитесь решениями в комментариях.
Теперь с соединением проблем вроде бы быть не должно, поэтому снова возвращаемся к нашему серверу.
Создаем соединение с БД, читаем данные и отправляем их клиенту
Открываем модуль с серверными методами (по умолчанию — это ServerMethodsUnit1) и бросаем на него сначала такие компоненты:
- TSQLConnection
- TSQLDataSet
Для TSQLConnection устанавливаем следующие свойства:
- ConnectionName = dsMySQL (заданное в Data Explorer)
- Driver = MySQL
- VendorLib = LIBMYSQL.dll
- Connected = True
У TSQLDataSet в свойстве SQLConnection выбираем SQLConnection1, а также сразу зададим и свойство CommandText. Для этого вызовем редактор и составим SQL-запрос вида:
SELECT * FROM dstable
Теперь можете проверить, что в SQLDataSet поступают данный — переключите свойство Active в значение True — при этом, если всё было сделано верно, не должно вызываться никаких исключений.
Теперь разберемся с тем как переслать клиенту все, что было получено из БД. Клиент не в курсе, что там делает на другой стороне наш сервер, как и куда подключается, что считывает. Клиенту главное — получить набор данных в каком-либо удобоваримом для него виде. И для этого мы напишем очень просто серверный метод, который будет возвращать клиенты TDataSet. Открываем исходный код модуля с серверным классом и создаем такой метод:
type TServerMethods1 = class(TDSServerModule) //.....// private { Private declarations } public { Public declarations } //......// function GetDataSet: TDataSet; end; function TServerMethods1.GetDataSet: TDataSet; begin SQLDataSet1.Open; Result:=SQLDataSet1; end;
Всё. Большего нам пока от сервера и не требуется. Как только клиент вызовет метод GetDataSet наш SQLDataSet1 получить все данные из таблицы и отправиться прямиком на клиент. Теперь вернемся к разработке клиента.
Разработка клиента
Клиент может только получать данные от сервера, а может (что чаще всего происходит) получать, добавлять и редактировать данные на сервере. В зависимости от того в каком режиме будет работать клиент (Read Only или Read/Write) будет зависеть состав компонентов. Рассмотрим оба варианта работы клиента начнем с более простого.
Создаем клиент для чтения данных
Создаем новый проект Delphi и бросам на главную форму следующие компоненты:
- TSQLConnection
- TSqlServerMethod
- TDataSetProvider
- TClientDataSet
- TDataSource
- TDBGrid
- TDBNavigator
- TCheckBox
У Вас должно получиться что-то типа такого:
Теперь настроим компоненты. Про настройку TSQLConnection я рассказывал в прошлый раз, поэтому можете открыть ссылочку и прямо по картинкам провести настройку компонента. Единственное, что следует помнить, что ISAPI-модуль (наш сервер) должен работать, чтобы Вы смогли сгенерировать клиентский модуль с серверными методами.
Теперь настроим TSQLServerMethod. Вообще, этот компонент удобно использовать в том случае, если нам необходимо получить данные только для чтения. У TSQLServerMethod устанавливаем следующие свойства:
- SQLConnection = SQLConnection1;
- ServerMethodName = TServerMethods1.GetDataSet;
Можете проверить работу компонента — переключите свойство Active в True — если соединение работает корректно, то это действие приведет к тому, что свойство TSQLConnection.Connected также станет равным True.
У компонента TDataSetProvider в свойстве DataSet указываем SQLServerMethod1.
У ClientDataSet1 в свойстве ProviderName указываем DataSetProvider1.
У DataSource1 в свойстве DataSet указываем ClientDataSet1
У DBGrid1 в свойстве DataSource указываем DataSource1
У DBNavigator1 в свойстве DataSource указываем DataSource1
И, наконец, у CheckBox1 пишем обработчик OnClick:
procedure TForm19.CheckBox1Click(Sender: TObject); begin ClientDataSet1.Active:=CheckBox1.Checked; end;
И всё. Клиент для чтения данных с сервера готов к работе. Для порядка можете из DBNavigator1 исключить кнопки, связанные с модификацией данных, а можете и не убирать, если они вам не мешают. Чтобы убедиться, что все работает как часы — просто измените свойство Active у ClientDataSet1 на True и табличка DBGrid должна вывести вам все данные из БД, но т.к. я ещё не вносил никаких данных, то у меня вид окна программы стал таким:
В приведенном выше примере клиента мы создали вот такую связь компонентов:
Для чтения данных с сервера этого достаточно, для модификации — нет. Напишем теперь клиентское приложение, которое будет и читать и модифицировать данные.
Создаем клиент для чтения и редактирования данных
Для статьи я буду создавать новое приложение, а Вы, если хотите, можете изменять только что созданного клиента. Итак, чтобы создаем новый проект и бросаем на главную форму следующие компоненты:
- TSQLConnection
- TDSProviderConnection
- TClientDataSet
- TDataSource
- TDBGrid
- TDBNavigator
- TCheckBox
Внешний вид главной формы приложения у меня получился следующий:
Как видите, состав компонентов стал несколько другим. TDSProviderConnection необходим нам для того, чтобы обеспечить ссылку на TDataSetProvider на стороне сервера. Но, как Вы можете Видеть выше — никакого TDataSetProvider у нашего сервера нет. Поэтому на минуту вернемся к серверу, добавим в модуль ServerMethodsUnit1 компонент TDataSetProvider у которого в свойстве DataSet укажем SQLDataSet. Вид модуля у сервера теперь стал таким:
Теперь пересобирите ISAPI-модуль, выключите IIS, забросьте куда необходимо IIS-модуль и снова включите IIS.
Возвращаемся к нашему клиенту. Будем считать, что TSQLConnection у Вас уже настроен (тритий раз повторяться не буду). Переходим сразу к TDSProviderConnection и устанавливаем у него следующие свойства:
- SQLConnection = SQLConnection1
- ServerClassName = TServerMethods1
Теперь устанавливаем свойства у TClientDataSet:
- RemoteServer = DSProviderConnection1
- ProviderName = DataSetProvider1;
Свойство ProviderName — это список, который формируется после запроса к серверу и в это список ничего руками вписывать не следует. Если у вас не получается сделать вот так:
то одно из двух:
- Сервер не работает
- Не правильно настроен TDSProviderConnection или TSQLConnection
Теперь устанавливаем у DataSourse свойство DataSet как ClientDataSet1 и настраиваем свойства у DBGrid и DBNavigator.
У CheckBox1 снова пишем обработчик OnClick:
procedure TForm18.CheckBox1Click(Sender: TObject); begin ClientDataSet1.Active:=CheckBox1.Checked; end;
Этого будет достаточно, чтобы запустить все компоненты по цепочке. И остается решить вопрос с обновлением БД. Я не стал выдумывать ничего лишнего и сделал отправку изменений на сервер в ручном режиме. т.е. добавил на форму кнопку и определил обработчик OnClick следующим образом:
procedure TForm18.Button1Click(Sender: TObject); begin ClientDataSet1.ApplyUpdates(0); end;
Вот теперь можно запустить наш клиент и попробовать добавить/отредактировать/удалить записи.
Будьте уверены — данные отправятся на сервер и сохраняться в базе данных.
Как видите сегодня мы с Вами решили поставленную задачу, а также определились с отличиями в составе компонентов у клиента, который работает только на чтения и у клиента для полноценной работы с данными. Исходники всех трех проектов я выложу, но пока открытым остается один вопрос, который, полагаю, возникал у тех, кого заинтересовала тема ISAPI — как проводить отладку ISAPI-модуля? Вопрос на самом деле не большой, поэтому сейчас его быстренько и рассмотрим.
Отладка ISAPI-модулей в Delphi
Как Вы знаете, любую dll-ку в Delphi можно отладить, если в свойствах проекта прописать значение «Host Application». Указываем в свойстве exe-шник, который использует нашу dll, запускаем проект и спокойненько отлаживаем библиотеку. Чем тогда отличается ISAPI-модуль от обычной DLL? Ответ очевиден — ничем. Это точно такая же динамическая библиотека, экспортирующая методы и для её отладки нам просто необходимо найти нужный exe-файлик.
Немного покопавшись по Сети я нашел тот самый exe-файл:
C:\Windows\System32\inetsrv\w3wp.exe
w3wp.exe отвечает за запуск веб-приложения на IIS и обработку запросов от других приложений. Другими словами этот файл делает возможным работу нашего сайта, который мы создавали с вами для работы.
Свойства проекта, который мы будем отлаживать должны быть такими:
- Host Application = C:\Windows\System32\inetsrv\w3wp.exe
- Parameters = -debug
- Host Application = C:\Windows\System32\inetsrv\w3wp.exe
- Parameters = -debug -s 10
Чтобы отладить ISAPI-dll выполняем следующие действия:
- Останавливаем полностью работу IIS
- Ставим где надо точки останова
- Запускаем проект dll и убеждаемся, что процесс w3wp.exe стартовал:
4. Запускаем клиента, что-нибудь делаем и убеждаемся, что отладка проходит отлично:
Книжная полка
Описание: Рассмотрены практические вопросы по разработке клиент-серверных приложений в среде Delphi 7 и Delphi 2005 с использованием СУБД MS SQL Server 2000, InterBase и Firebird. Приведена информация о теории построения реляционных баз данных и языке SQL. Освещены вопросы эксплуатации и администрирования СУБД.
|
||
Название: О чем не пишут в книгах по Delphi
Описание: Рассмотрены малоосвещенные вопросы программирования в Delphi. Описаны методы интеграции VCL и API. Показаны внутренние механизмы VCL и приведены примеры вмешательства в эти механизмы. Рассмотрено использование сокетов в Delphi: различные режимы их работы, особенности для протоколов TCP и UDP и др.
|
Новый элемент форматирования? :)
(это я про цветные рамочки со значками)
Александр, угу. Надо же как-то внимание обращать на отдельные элементы в посте :)
Влад, спасибо за статью, очень интересно. Да и картинки помогают лучше понять статью. Кстати было бы лучше если бы картинки увеличивались в том же окне, а не в новом.
ildvild, спасибо за отзыв. Для картинок сейчас подыскиваю нормальный плагин.
Здравствуйте! Извините, что пишу комментарий не по теме статьи, но у меня есть небольшой вопрос. Я использую Idhttp, и на слабых либо загруженных каналах он начинает зависать, просто тормозит на каком-то моменте и не идет дальше (В очереди у него много операций). Что лучше всего сделать в таком случае?
Pter, idHTTP зависает? или интерфейс программы? Если интерфейс, то кидаем idAntiFreeze на форму и все, если idHTTP, ничего не делаем — ждем.
Vlad, зависает сам IdHttp, выполняя некую операцию в цикле. Он просто останавливает цикл, и дальше выполнение программы в этой процедуре не идет.
Pter, надо значит смотреть почему идёт «зависание» в цикле. Просто так из-за слабого канала idHTTP повиснуть не может он может только дольше обычного отправлять запрос и обрабатывать ответ. В общем код надо смотреть, т.к. вариантов много, например, запускать idHTTP в отдельном потоке — вариант?
12 лет назад сделал на Делфи 5 промежуточный сервер для доступа к БД.
Midas + TSocketConnection.
Примерно как у Вас, но с динамическим созданием TDataSetProvider и TQuery на среднем уровне.
Благодаря этому удалось избавиться от тяжеленного драйвера БД Оракл на клиентских машинах.
А будет ли работать клиент если с сервера передать целый класс DataModule? со всякими Adoconnection Adoquery Tdatasource ?
Zelenyy Sharik, да должен работать, но сам не проверял
Сделал, все работает как надо пока не замрешь минуты на 3-4. После этого выходит ошибка socket error #10054 connection reset by peer. Как с ней бороться.