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

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

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

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

Что не так с ADO

Запись от krapotkin размещена 12.06.2018 в 08:54
Обновил(-а) krapotkin 12.06.2018 в 09:24

Технология и компоненты 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!
А альтернативные варианты есть!
http://www.cyberforum.ru/blogs/469693/blog5298.html
Размещено в Без категории
Просмотров 461 Комментарии 5
Всего комментариев 5
Комментарии
  1. Старый комментарий
    Аватар для D1973
    "пользуется абсолютно незаслуженной популярностью"

    Не по теме:

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


    Да студням вообще пофиг: что им дают, то и хавают... А преподы иногда просто другого не знают! Кое-где БД в Delphi преподаются на примере BDE и Paradox. т.к. препод это в свои студенческие годы усвоил (или не усвоил, а просто методичка с тех времен), а даже ADO для него - лес густой и темный...
    Поэтому говорить о популярности - не совсем точно, но грустно - однозначно...
    А за статью - спасибо!
    Запись от D1973 размещена 08.10.2018 в 16:03 D1973 вне форума
  2. Старый комментарий
    Аватар для Avazart
    Ок, если не ADO то что тогда использовать?
    Платные сторонние компоненты?
    Запись от Avazart размещена 08.10.2018 в 16:52 Avazart вне форума
    Обновил(-а) Avazart 08.10.2018 в 16:53
  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 на форуме
 
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2018, vBulletin Solutions, Inc.
Рейтинг@Mail.ru