Форум программистов, компьютерный форум, киберфорум
krapotkin
Войти
Регистрация
Восстановить пароль
Блог. Двадцать пять лет Делфи-практики

В этом блоге я буду публиковать ответы на вопросы, которые постоянно приходится повторять на форуме.
Здесь можно это сделать более развернуто и спокойно.

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

Начав с Делфи-2 двадцать пять лет назад, я прошел все версии, испробовал массу технологий, включая работу с БД, с графикой DirectX, связью с серверами и интернетом, разработку на Андроид и IOS, и многое, многое другое.
________________________________________ ________________________________________ ____
P.S. все, о чем здесь написано, всего лишь измышления из головы.
совпадения с реальными людьми и фактами случайны.

Что не так с ADO

Запись от krapotkin размещена 12.06.2018 в 08:54
Показов 3921 Комментарии 5
Метки db, delphi

Технология и компоненты ADO в силу некоторых причин на этом форуме пользуется абсолютно незаслуженной популярностью.
На самом же деле, ОСОБЕННО если вы новичок, то вы получаете не более простое, а наоборот, наиболее сложное решение ваших задач по работе с БД.

Как правильно подключаться к БД
Ваша программа и БД только во время разработки будет лежать в определенном вами месте. Как только вы перейдете на другой компьютер, все начнет ломаться. Поэтому делайте так:
Путь к БД

ADOTable - плохо
Вернемся к работе с БД
Самый казалось бы простой способ - это подключаться к таблицам через ADOTable.
И это капец как плохо.
Если вы обучаетесь работать с БД, вы сразу должны иметь в виду, что к одной БД всегда подключается много пользователей.
Если одна ваша программа утянет к себе какую-нибудь таблицу и начнет у себя ее менять, другая, третья, то при записи выяснится, что у нас несколько копий одной и той же таблицы. В лучшем случае вторая копия сотрет первую, третья - вторую, и мы получим - кто последний сохранился, те данные и будут жить в БД. Жестоко. А если к этим данным привязаны (а должны быть привязаны! это же реляционная БД) данные из других таблиц, то все рассыплется окончательно.
Так что лучший вариант, если один залез в БД, другого уже не пускать.
Отличная система получилась? Один сервер - один клиент. Один магазин - одна касса. Один паспортный стол - одно окно, и т.д.
Я уже уверен, что ваши будущие клиенты оценят )))

ADOQuery - лучше
Так что хочешь - не хочешь, придется писать запросы и действовать через ADOQuery
Что нужно знать.
Запросы пишутся на универсальном языке SQL и бывают четырех видов
SELECT, UPDATE, INSERT, DELETE
Только SELECT возвращает вам набор данных (датасет) для работы с ними или отображения на экране
Если в Query - SELECT, то запрос "открывается". Нужно писать Query.Open.
Если в Query - INSERT, UPDATE, DELETE, то запрос "исполняется" и соответственно мы пишем Query.ExecSQL
учебник по SQL здесь
http://www.sql.ru/docs/sql/u_sql/
справочников много, например вот
https://www.w3schools.com/sql/default.asp

Где размещать компоненты
Конечно, на отдельном модуле данных. Он похож на форму, только невидим во время работы программы.
На нем будет один компонент подключения к БД ADOConnection, и к нему будут подключаться все ваши датасеты.

Для начинающих писателей хочу порекомендовать два способа создания модуля данных в программе.
Каждый из них начинается с File -- New -- Other -- Delphi Files -- Datamodule
В свойство Name модуля данных напишите DM. Нажмите Ctrl+S и дайте файлу имя например UDM.pas
Не забудьте во всех модулях (юнитах) своей программы, которые будут обращаться к БД, вписать в USES обращение к UDM!
Примерно так
Delphi
1
2
uses
  Windows, Classes, Types, ......., UDM;
Теперь первый вариант
После этого зайдите в Project -- Options -- Forms и удалите из списка AutoCreate ВСЕ формы, КРОМЕ модуля данных и главной формы вашего приложения. При этом модуль данных поставьте в списке первым.
По поводу форм читайте здесь
В событии OnCreate модуля данных впишите подключение к БД способом, который я описал выше в разделе "Как правильно подключаться к БД"

Второй вариант более продвинутый и более верный в реальной жизни.
Его лучше применять, когда у вашей программы есть еще какие-нибудь настройки. В том числе и к какой БД подключаться.
Как хранить настройки программы, есть рекомендация тут
А весь процесс старта программы вместе с чтением настроек и подключением к БД я описал тут
Так вот. В этом случае логично все делать при создании главной формы - и прочитать настройки и создать модуль данных
Delphi
1
DM := TDM.Create(Self);
Поэтому мы опять же идём в Project -- Options -- Forms, но удаляем из списка AutoCreate вообще ВСЕ формы, кроме главной формы

Теперь при подключении DB-компонентов, например TDataSource, в свойстве Dataset у вас будут отображаться и те Query, которые находятся на вашем модуле данных. Если это не так, значит, вы пропустили мимо ушей мое замечание насчет Uses несколькими строками выше.

Соответственно, в программе обращение к компоненту Query1 на модуле данных будет выглядеть так:
Delphi
1
dm.Query1.блаблабла
Так что с редактированием?
Допустим, мы сделали запрос в БД, и показали его результаты в DBGrid.
Теперь нам нужно а) редактировать текущую запись б) вставить новую запись в) удалить текущую запись
Как ни прискорбно, но ПОСЛЕ всех трех операций никакого результата в гриде вы не увидите. Ведь мы смотрим на РЕЗУЛЬТАТ запроса, который выполнили некоторое время назад, когда еще ничего не изменяли, не вставляли и не удаляли.
Никакой магической связи между этими данными и БД нет!!!
Хотите обновить информацию - делайте запрос заново.
Delphi
1
2
Query.Close;
Query.Open;
Что хочется сразу сказать.
Дорогие писатели! Не жмотьтесь, не пытайтесь все запросы исполнять в одном Query компоненте
Лучше всего все запросы, хотя бы все SELECT'ы, заранее, в design-time, собрать в отдельные Query в нашем модуле данных и оставить их в покое. Если вам нужны условия в этих запросах, то прочтите это


Так вот начнем с конца. в) Удаление.
https://www.w3schools.com/Sql/sql_delete.asp
Основной идеей работы с БД является то, что любая запись должна иметь КЛЮЧ - поле, или набор полей, по которому эту запись ОДНОЗНАЧНО можно найти среди всех других. Чаще всего это какое-то поле с названием ID, и чаще всего автоинкрементное, чтобы разработчику не пришлось придумывать его самостоятельно.
Обычно это поле всегда нужно включать в запрос, но почти всегда его не нужно показывать пользователю, т.к. это служебная информация.
Поэтому для удаления записи из qYourQuery берем к-нить (ДРУГОЙ!!!) query, назовем его qDelete, и выполняем
(вы же уже прочли про параметры?)))
Delphi
1
2
3
dm.qDelete.SQL.Text := 'DELETE from ВАШАТАБЛИЦА WHERE ID=:ID';
dm.qDelete.Parameters.ParamByName('ID').Value := dm.qYourQuery.FieldByName('ID').AsInteger;
dm.qDelete.ExecSQL;
и не забудем, что теперь нужно переоткрыть ваш запрос
Delphi
1
2
dm.qYourQuery.Close;
dm.qYourQuery.Open;
Запись исчезла

а) редактирование записи
Компонент DataSource обеспечивает некторое волшебство в вашей программе. Когда вы перемещаетесь по DBGrid любым способом, то точно так же перемещается указатель на текущую запись в подключенном к нему датасете. И наоборот. Если выполнить любую команду на перемещение в датасете - First, Last, Next, то в DBGrid'е вы сразу же переместитесь на текущую запись. Отсюда мораль. Если вы дважды кликнете на DBGrid, то можно просто открыть новую форму и работать в ней с текущей записью вашего датасета.
Вот тут я уже говорил, что самое частое использование форм это - модальный диалог.
Напишем вызов формы редактирования текущей записи по двойному клику на DBGrid'е
Delphi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
procedure TMainForm.DBGrid1DblClick(Sender:TObject);
begin
  RunEditForm();
end;
 
procedure TMainForm.RunEditForm();
var f:TEditForm;
  ID:integer;
begin
  f:=TEditForm.Create(NIL);
  try
    // подготавливаем форму к показу. заполняем ее поля. настраиваем настройки
    f.Load(); // загрузка в Edit'ы текущих значений из датасета
    // показываем модально и ждем результата
    if f.ShowModal()=mrOK then
    begin
      f.Save(); // сохранение данных из формы в БД
      // а это - чтобы после переоткрытия опять встать на ту запись, которую редактировали, а не на первую, как обычно
      ID:=dm.qYourQuery.FieldByName('ID').AsInteger;
      dm.qYourQuery.Close;
      dm.qYourQuery.Open;
      dm.qYourQuery.Locate('ID',ID,[]);
    end;
  finally
    // и уничтожаем форму
    f.free;
  end;
end;
всё просто. создали форму, заполнили ее, показали, сохранили результат, убили, чтобы не болталась...
в самой же форме редактирования мы расставляем edit'ы и пишем две процедуры, упомянутые в коде выше
Load() и Save()
(ясно ж, что для доступа к данным из модуля данных нам нужно в Uses у формы редактирования не забыть добавить UDM?)
из головы выдумаем форму - на ней два Edit'а - Фамилия, Имя, один DateTimePicker - Дата рождения - в режиме отображения даты, и один RadioGroup - пол М/Ж соответственно.
Delphi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
procedure TEditForm.Load();
begin
  eLastName.Text := dm.qYourQuery.FieldByName('LastName').AsString;
  eFirstName.Text := dm.qYourQuery.FieldByName('FirstName').AsString;
  dtBirthDate.Date :=  dm.qYourQuery.FieldByName('BirthDate').AsDateTime;
  if dm.qYourQuery.FieldByName('Gender').AsString = 'F' then
    rgGender.ItemIndex:=1
  else
    rgGender.ItemIndex:=0;
end;
 
procedure TEditForm.Save();
begin
  dm.qUpdate.Parameters.ParamByName('LastName').Value := eLastName.Text;
  dm.qUpdate.Parameters.ParamByName('FirstName').Value := eFirstName.Text;
  dm.qUpdate.Parameters.ParamByName('BirthDate').Value := dtBirthDate.Date;
  if rgGender.ItemIndex = 0 then
    dm.qUpdate.Parameters.ParamByName('Gender').Value := 'M'
  else
    dm.qUpdate.Parameters.ParamByName('Gender').Value := 'F';
  dm.qUpdate.ExecSQL;
end;
qUpdate - это наш запрос обновления записи
описание тут https://www.w3schools.com/Sql/sql_update.asp
ессссно используем параметры, потому что иначе проще убиться, чем написать запрос без ошибок


и осталась у нас б) вставка записи.
но постойте, не делать же другую форму для вставки?
делать то надо то же самое, только поля в начале пустые и параметры сохранять надо не в qUpdate а в qInsert.
(qInsert это еще один наш Query, в котором запрос на вставку данных)
пишется вот так https://www.w3schools.com/Sql/sql_insert.asp

сделаем офигенную хитрость
параметры в запросах назовем одинаково, а сам Query будем передавать в процедуру Save параметром!!!
итак!
Delphi
1
2
3
4
5
6
7
8
9
10
11
procedure TEditForm.Save(q:TQuery);
begin
  q.Parameters.ParamByName('LastName').Value := eLastName.Text;
  q.Parameters.ParamByName('FirstName').Value := eFirstName.Text;
  q.Parameters.ParamByName('BirthDate').Value := dtBirthDate.Date;
  if rgGender.ItemIndex = 0 then
    q.Parameters.ParamByName('Gender').Value := 'M'
  else
    q.Parameters.ParamByName('Gender').Value := 'F';
  q.ExecSQL;
end;
и всё!!!
теперь перепишем чучуть вызов RunEditForm()
Delphi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
procedure TMainForm.RunEditForm(InsertMode:boolean);
var f:TEditForm;
  ID:integer;
begin
  f:=TEditForm.Create(NIL);
  try
    if not InsertMode then
      f.Load(); 
    if f.ShowModal()=mrOK then
    begin
      if InsertMode then
        f.Save(dm.qInsert)
      else
      begin
        f.Save(dm.qUpdate);
        ID:=dm.qYourQuery.FieldByName('ID').AsInteger;
        dm.qYourQuery.Close;
        dm.qYourQuery.Open;
        dm.qYourQuery.Locate('ID',ID,[]);
      end;
    end;
  finally
    f.free;
  end;
end;
соответственно при вставке вызываем теперь RunEditForm(true);
при редактировании RunEditForm(false);

и все это нагорожено исключительно из-за того, что у нас нет адекватного компонента DBDataset
поэтому если есть любая возможность, не пользуйтесь ADO!
А альтернативные варианты есть!
https://www.cyberforum.ru/blog... g5298.html
Метки db, delphi
Размещено в Без категории
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Всего комментариев 5
Комментарии
  1. Старый комментарий
    Аватар для D1973
    "пользуется абсолютно незаслуженной популярностью"

    Не по теме:

    А`гхитипичная анти`геволюционная ситуация, батенька: ве`гхи не знают и знать не хотят, а низам все по`говну...


    Да студням вообще пофиг: что им дают, то и хавают... А преподы иногда просто другого не знают! Кое-где БД в Delphi преподаются на примере BDE и Paradox. т.к. препод это в свои студенческие годы усвоил (или не усвоил, а просто методичка с тех времен), а даже ADO для него - лес густой и темный...
    Поэтому говорить о популярности - не совсем точно, но грустно - однозначно...
    А за статью - спасибо!
    Запись от D1973 размещена 08.10.2018 в 16:03 D1973 вне форума
  2. Старый комментарий
    Аватар для Avazart
    Ок, если не ADO то что тогда использовать?
    Платные сторонние компоненты?
    Запись от Avazart размещена 08.10.2018 в 16:52 Avazart вне форума
  3. Старый комментарий
    Аватар для krapotkin
    эммм, а ссылка в конце статьи?
    Запись от krapotkin размещена 08.10.2018 в 17:00 krapotkin вне форума
  4. Старый комментарий
    Аватар для Avazart
    MySql, Oracle ? Да даже тот же SQLite ...
    Запись от Avazart размещена 08.10.2018 в 17:40 Avazart вне форума
  5. Старый комментарий
    Аватар для Usaga
    и все это нагорожено исключительно из-за того, что у нас нет адекватного компонента DBDataset
    поэтому если есть любая возможность, не пользуйтесь ADO!
    А мне кажется, что нагорожено тут исключительно потому, что кто-то очень любит кнопочно-ориентированный калокод писать и про "Репозитории" не слыхал. И что ADO - мостик к ODBC через который можно работать с любой СУБД - тоже. И про транзакции, видимо тоже не слыхал, раз такое выдаёт:

    Так что лучший вариант, если один залез в БД, другого уже не пускать.
    Уж не обижайтесь за резкие высказывания. Я не спец в дельфях, но критика ADO смахивает на банальное недопонимание этого самого ADO...
    Запись от Usaga размещена 08.10.2018 в 18:21 Usaga на форуме
 
Новые блоги и статьи
Скрипты Tampermonkey для CyberForum, ChatGPT, Claude и пр.
Jin X 06.09.2026
Скрипты Tampermonkey для CyberForum, ChatGPT, Claude и пр. Работая с форумом и нейросетями в браузере часто хочется что-то подкорректировать или добавить какого-то функционала. Ниже прикреплён. . .
Программа опроса у.з. расходомера SLS-720F
Argus19 02.09.2026
Программа опроса у. з. расходомера SLS-720F Программа опрашивает один раз в минуту три ультразвуковых расходомера SLS-720F через интерфейс RS-485 по протоколу Modbus RTU. Опрашиваются регистры. . .
Hyper-V: Компьютер должен поддерживать доверенный платформенный модуль 2.0.
Maks 31.08.2026
При установке Windows 11 на виртуальную машину Hyper-V 2-го поколения вылезла такая ошибка: Решение: в параметрах виртуальной машины, в разделе "Безопасность" (Security) активировать флаг. . .
Архитектура биовида Стива в Майнкрафте: Зачем бонобо кубический каннибализм
anaschu 30.08.2026
Кубический Вагинокапитализм в Minecraft: Математический инвариант ОДУ и рок Стивов-бонобо Главная задача разработанной «Модели Всего» — наглядно продемонстрировать наличие системной «судьбы». . .
Оттачиваю умение писать js программы.
russiannick 30.08.2026
Проектом выходного дня стало написание Книги шифров Виженера. Итогом стала версия 200, синий туман. Синий туман назван так, потому что замораживает текст под собой. Нажатие синих кнопок управляют. . .
мат медиц модель 30. презентация проекта
anaschu 27.08.2026
хоп хоп хоп хидахоп, а я кладую))
Как у меня протекала болезнь
zorxor 27.08.2026
Здравствуйте, друзья! Эта запись блога предназначена именно для вас - для моих дорогих друзей, которые знали меня лично. Чтобы ответить на вопрос - а что же со мной произошло на самом деле? Я учился. . .
Нашел вот забавное видео о измерениях. Лучшее что я видел на эту тему
kumehtar 26.08.2026
ILETXiw9bMQ Основная суть и тезисы по измерениям: 0D (Нулевое измерение): точка, не имеющая длины, ширины, высоты или объема. Объект не может перемещаться в 0D. 1D (Первое измерение):. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru