На протяжении всего курса нам не раз приходилось сталкиваться с этим термином. Тема " непростая, но очень необходимая для понимания. Эта лекция посвящена теории
В лекции № 14 мы упоминали, что
Поясним эту суть на классическом примере перевода денег в банке с одного счета на другой.
Допустим, наша БД работает без
Теперь предположим, что в середине этой операции произошел какой-то сбой: отключился сервер БД, например. В первом случае деньги будут потеряны - они ушли с одного счета, но не дошли до другого. Во втором случае деньги "размножатся" - появятся на втором счету, но при этом останутся и на первом. И в том, и в другом случае произойдет нарушение целостности БД - данные станут недостоверны.
Однако все SQL -серверы баз данных работают с применением не исключение, более того, предоставляет гораздо более гибкие инструменты для управления SQL -серверы. Если произошел какой то сбой при переводе денег, то
Более 20 лет назад исследователи Тео Хендер и Андреас Рютер опубликовали обзор, в котором описывали принципы поддержания целостности БД в много-клиентской среде. Эти принципы принято называть
Под согласованностью понимается, что целостность базы данных не нарушится, несмотря на то, была ли подтверждена
На стороне сервера за согласованность отвечают ограничения CHECK, ограничения ссылочной целостности и триггеры. Программист, тем не менее, должен тщательно спроектировать механизмы бизнес-логики.
В базе данных может выполняться множество
Если
Все действия над базой данных, совершаемые в клиентском приложении, происходят внутри (в контексте) Delphi "по умолчанию". В серьезных приложениях БД это недопустимо, так как может привести к многочисленным конфликтам.
Впрочем, компоненты доступа к данным неявно производят запуск
try
//какие то действия над данными...
IBTransaction1.Commit; //подтверждаем
Except
//произошла ошибка
ShowMessage('Невозможно выполнить операцию!');
IBTransaction1.Rollback; //делаем откат транзакции
end; //try
В данном примере, если
В базе данных имеется специальная область, которая называется
Активной называется
Подтвержденной называется
Отмененной называют
Неопределенной транзакцией ( Limbo ) называют
Каждая
Если попытается пометить предыдущую оригинальную запись, как удаленную, а версию завершенной сохраняет изменения, то в новую запись помещается и идентификатор
Если
Если же
Однако могут возникать и конфликты ? Если он пометит версию записи Т1 как оригинал, а старую запись, как удаленную, то в версии Т2 окажутся ложные данные! Действия в этом случае будут зависеть от параметров этих
Если
Таким образом, говорят, что имеет многоверсионную архитектуру ( очень быстро восстанавливается, благодаря именно является первым SQL -сервером, который поддерживает многоверсионную архитектуру.
Наряду с преимуществами такого подхода, в базе данных со временем накапливается "мусор". Каждая
Но как удалять мусор? Можно ли удалить версию
Предположим, что стартовала
В присутствует механизм удаления старых версий, который запускается новыми
Таким образом, молодые
Поскольку на сервере одновременно может выполняться множество
В данном контексте, сборкой мусора занимается старейшая активная
Как уже упоминалось выше, каждая
Мы имеем возможность гибко управлять параметрами
| Параметр | Описание |
|---|---|
| Разрешается чтение данных. | |
| Разрешается запись данных. | |
| При возникновении конфликта с другой |
|
| При возникновении конфликта с другой |
|
| Read_committed Rec_Version | |
| Read_committed No_Rec_Vesion | |
| Создает |
|
| Создает |
Программист может задать
Read Write
Какие параметры устанавливать, решается для каждой конкретной задачи. Однако можно сделать такие рекомендации:
Если
Read Read_Committed Rec_Version NoWait
Если
Read Concurrency NoWait
И, наконец, если
Write Concurrency NoWait
Для закрепления материала создадим приложение, работающее с таблицей Tovar нашей базы данных. Приложение будет использовать две
Прежде всего, убедитесь, что сервер запущен. Далее, создайте в Delphi новый проект. На главную форму поместите DBNavigator, сетку DBGrid и простую кнопку:
(рис 24.1) Главная форма приложенияПока мы не подключим сетку и навигатор к таблице, данные не будут отображаться. Сохраните проект в отдельную папку под именем IBXTrans. Поскольку и навигатор, и сетка нам нужны только для чтения данных, выделите навигатор, откройте сложное свойство VisibleButtons и сделайте невидимыми все кнопки, кроме nbFirst, nbPrior, nbNext, nbLast и nbRefresh. Отрегулируйте ширину навигатора, чтобы кнопки стали квадратными, как на рисунке выше. А у сетки свойство ReadOnly переведите в True. С главной формой закончили.
Командой File -> New -> Data Module создайте новый модуль данных. Свойство Name окна переименуйте в
В модуль поместите следующие компоненты из вкладки InterBase:
IBDatabase, IBTable, IBQuery, два компонента
Займемся настройкой этих компонентов.
Выделите компонент IBDatabase. Его свойство Name переименуйте в IBDB, чтобы было короче. Откройте свойство Params (откроется окно редактора параметров). Там укажите следующие параметры:
user_name=sysdba password=masterkey lc_ctype=win1251
Обратите внимание, все параметры можно писать маленькими буквами, пробелы перед- и после знака "=" недопустимы. Далее, в свойство DatabaseName найдите и поместите файл нашей базы данных First.gdb. Чтобы при загрузке программы не запрашивался логин и пароль, свойство LoginPrompt переведите в False. После этого свойство Connected переведите в True. Если вы все сделали правильно, база откроется, ошибок не возникнет.
Теперь займемся компонентами IBTrans1. Этот компонент будет создавать
read read_committed rec_version
В свойстве DefaultDatabase выберите IBDB, а у IBDB в свойстве DefaultTransaction в свою очередь, выберите IBTrans1. После чего переведите свойство IBTrans1 в True.
Вторую IBTrans2. Она будет нужна только для записи новых данных в таблицу. Поэтому в свойстве Params мы введем следующие параметры:
write concurrency nowait
В свойстве DefaultDatabase этой IBDB, а вот свойство
Перейдем к компоненту IBTable. Свойство Name переименуйте в TTovar, в свойстве Database выберите IBDB, в свойстве Transaction выберите читающую IBTrans1. Теперь в свойстве TableName найдите и откройте таблицу TOVAR, после чего свойство
Выделите DataSource и переименуйте Name в DSTovar. В свойстве DataSet выберите таблицу TTovar. Теперь можно перейти на главную форму, выделить сетку и навигатор, и в их свойстве DataSource выбрать . Данные должны отобразиться в сетке, а некоторые кнопки навигатора станут недоступны.
Вернемся в модуль данных. Выделите запрос IBQuery. Свойство Name переименуйте в Q1. В свойстве Database выберите IBDB, а в свойстве Transaction - IBTrans2. Более ничего делать не нужно, запросы будем строить программно.
Для добавления новых записей и редактирования существующих создадим еще одну форму командой File -> New -> Form. Окно редактора будет очень простым:
(рис 24.2) Окно редактораФорму назовите fEditor, сохраните в папку проекта, дав модулю имя Editor (так как последним мы открывали файл First.gdb, то при попытке сохранения окна Delphi по умолчанию предложит папку с БД. Измените ее на папку с проектом.). Командой File -> Use Unit подключите к редактору модуль DM. На форме расположите два компонента Label, два простых Edit и две кнопки, как на рисунке 24.2. Не помешает в свойстве BorderStyle формы выбрать bsDialog, а в свойстве Position - poMainFormCenter. И не забудьте очистить текст у компонентов Edit, а у кнопок и компонентов Label изменить свойство Caption в соответствии с рисунком.
Вернемся к главной форме. Командой File -> Use Unit подключите к главной форме модуль Editor. Сгенерируйте событие нажатия на кнопку "Добавить запись". В полученной процедуре впишите код закрытия Q1 (на случай, если ранее запрос был активен), и вызова редактора:
{Добавить запись}
procedure TfMain.Button1Click(Sender: TObject);
begin
fDM.Q1.Close;
fEditor.ShowModal;
end;
Таким образом, мы будем добавлять новую запись. А для редактирования существующей записи, выделите сетку DBGrid и сгенерируйте для нее событие OnDblClick. То есть, открывать редактор мы будем по двойному щелчку на записи. При этом в таблице TTovar станет текущей нужная нам запись. Прежде, чем вызывать редактор, нам нужно в Q1 получить нужную запись. Для этого мы создадим запрос, откроем Q1 и только потом вызовем редактор. Итак, код события OnDblClick следующий:
{Двойной щелчок по сетке - редактируем запись}
procedure TfMain.DBGrid1DblClick(Sender: TObject);
begin
fDM.Q1.SQL.Clear;
fDM.Q1.SQL.Add('select * from Tovar where ID = '+
IntToStr(fDM.TTovar.FieldByName('ID').AsInteger));
fDM.Q1.Open;
fEditor.ShowModal;
end;
Как видно из кода, в запросе Q1 мы генерируем запрос вроде такого:
SELECT * FROM TOVAR WHERE ID = 3
Разумеется, номер ID будет зависеть от того, по какой записи мы щелкнули. При открытии Q1, в нее попадет лишь одна интересующая нас запись. Больше ничего в главной форме делать не нужно. Переходим к окну редактора.
Тут у нас может быть два варианта: либо мы добавляем новую запись ( Q1 закрыта), либо редактируем существующую ( Q1 открыта и содержит нужную запись). В первом случае нам нужно будет очистить компоненты Edit, если там был текст, а во втором наоборот, вписать в них значения полей Nazvanie и Stoimost.
В раздел глобальных переменных добавим переменную i, необходимую для хранения ID записи:
var fEditor: TfEditor; i: Integer; //идетификатор записи
Затем выделим форму редактора, и сгенерируем для нее событие OnShow. Код события следующий:
{При показе редактора}
procedure TfEditor.FormShow(Sender: TObject);
begin
if fDM.Q1.Active then
i := fDM.Q1.Fields[0].AsInteger
else i := 0;
//очищаем или заполняем эдиты:
if i = 0 then begin
Edit1.Text := '';
Edit2.Text := '';
end //if
else begin
Edit1.Text := fDM.Q1.Fields[1].AsString;
Edit2.Text := fDM.Q1.Fields[2].AsString;
end; //esle
end;
Как видно из приведенного кода, как только форма станет видимой, мы проверяем - активна ли Q1. Если да, то в переменную i прописываем идентификатор текущей записи, иначе i делаем равным нулю. Это нам нужно для того, чтобы в дальнейшем знать, с какой записью работать. Ведь в Q1 будут помещаться другие запросы, и она уже не будет содержать нужную запись.
Далее, если i = 0 (новая запись), мы очищаем компоненты Edit, иначе считываем в них значения второго и третьего поля запроса ( Nazvanie и Stoimost ).
Пойдем дальше. Для кнопки "Отменить" введем код простого закрытия формы:
//закрываем форму: Close;
Так как пользователь может закрыть окно не только кнопкой "Отменить", но и клавишами <Alt + F4> или просто нажав на крестик в правом верхнем углу окна, то проверку на активность
Сгенерируем для формы редактора событие OnClose, в котором будем проверять, не в работе ли IBTrans2, и если да, то сделаем откат
if fDM.IBTrans2.InTransaction then
fDM.IBTrans2.RollbackRetaining;
Прежде, чем перейдем к кнопке "Подтвердить", подумаем вот о чем: для в вещественных числах между целой частью и дробной должен быть разделитель точка, а пользователь может ввести запятую. Кроме того, в существующих записях разделитель также отображается как запятая, и если мы при показе формы автоматически заполним Edit2, то там тоже будет запятая. Значит, для Edit2 сгенерируем событие OnChange, где устроим простую проверку:
{Изменение данных в Edit2}
procedure TfEditor.Edit2Change(Sender: TObject);
var
ind: Byte;
s: String;
begin
s:= Edit2.Text;
for ind:= 1 to Length(s) do
if s[ind] = ',' then s[ind]:= '.';
Edit2.Text := s;
end;
Здесь, если в тексте будет обнаружена запятая, то она автоматически поменяется на точку. Однако приложение демонстрационное, поэтому мы не делаем проверку на то, что пользователь может ввести не только цифру, точку или запятую, но и какой-нибудь другой символ, например, пробел или букву. Вы можете сделать более детальную проверку, если желаете.
Нам осталось лишь сгенерировать нажатие на кнопку "Подтвердить". И здесь у нас будет два варианта: либо мы добавляем новую запись ( i = 0 ), и тогда мы используем оператор INSERT, либо мы изменяем существующую. В последнем случае будет применяться оператор UPDATE. Код нажатия на кнопку следующий:
{Подтвердить}
procedure TfEditor.Button1Click(Sender: TObject);
begin
//очищаем SQL-запрос:
fDM.Q1.SQL.Clear;
//создаем новый запрос, в зависимости от показателя i:
if i = 0 then begin //если i = 0, то это новая запись
fDM.Q1.SQL.Add('insert into Tovar(Nazvanie, Stoimost)');
fDM.Q1.SQL.Add('values('+QuotedStr(Edit1.Text)+', '+Edit2.Text+')');
end //if
else begin //модифицируем существующую запись
fDM.Q1.SQL.Add('update Tovar set Nazvanie='+
QuotedStr(Edit1.Text)+
', Stoimost='+
Edit2.Text+' where ID ='+IntToStr(i));
end; //else
try
//производим изменения:
fDM.Q1.ExecSQL;
//подтверждаем транзакцию:
fDM.IBTrans2.CommitRetaining;
except
ShowMessage('Изменения данных не прошли!');
fDM.IBTrans2.RollbackRetaining;
end; //try
//обновляем НД TTovar:
fDM.TTovar.Refresh;
//закрываем форму:
Close;
end;
В случае добавления записи формируется запрос типа
INSERT INTO TOVAR(Nazvanie, Stoimost) VALUES('Товар', 10.00)
В случае редактирования существующей записи формируется другой запрос:
UPDATE TOVAR SET Nazvanie = 'Товар', Stoimost = 10.00 WHERE ID = 3
Разумеется, название товара, его стоимость и идентификатор ID будут зависеть от того, что именно введет пользователь в компоненты Edit, и какую строку в таблице выберет.
Обратите внимание, что у компонента IBTrans2 вместо методов
Сохраните проект, скомпилируйте и попробуйте вводить новые значения и редактировать существующие. Проект использует две
В заключение заметим, что в проектах, которые интенсивно работают с базой данных, совершенно недопустимым будет использование только одной
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.