ТОР 5 статей: Методические подходы к анализу финансового состояния предприятия Проблема периодизации русской литературы ХХ века. Краткая характеристика второй половины ХХ века Характеристика шлифовальных кругов и ее маркировка Служебные части речи. Предлог. Союз. Частицы КАТЕГОРИИ:
|
Коротко о других компонентах ADOСамый базовый объект ADO - это ADOCommand - команда, которая передается серверу, для того чтобы считать или изменить данные (ADO всегда работает в режиме клиент-сервер, даже с локальными). После выполнения команды база данных может вернуть записи (если это была команда на считывание данных) или ничего не вернуть (если это, скажем, была команда на удаление данных). Команды - это ничто иное, как строки, написанные на языке SQL - если вы не знаете этот язык - изучите его - уверяю вас - в нем гораздо меньше ключевых слов чем в Delphi, который вы уже знаете.
Пара компонентов TADOTable и TADOQuery - предназначены для того чтобы программистам привыкших работать с BDE и компонентами TTable и TQuery было бы легче перейти на ADO. Они имеют сходные с TTable и TQuery методы - но, увы все же не совпадают с ними полностью - поэтому перевести проект, использующий BDE на ADO одним щелчком кнопки как правило не получится. Компонент TADODataset - это компонент, который имеет возможности TADOTable и TADOQuery одновременно - т.е. может быть использован как для выполнения SQL- запроса так и для прямого обращения к какой-либо таблице. TADOStoredProc - компонент для работы с хранимыми процедурами. В следующей части мы поговорим о свойстве Properties компонентов ADO, которые, к сожалению, почти не освещены в справке от Borland и о некоторых отличиях, с которыми придется столкнуться программисту, переносящему проект с BDE на ADO. Часть 2 Я полагаю, что те, кто читают эти строки могут написать небольшую программку на Delphi, подключающуюся к базе данных с использованием ADO и имеют некоторое представление о языке SQL?
Я рад, что Вы остались здесь - тогда продолжим.:-) Для изучения я приготовил небольшой примерчик - это примитивная база данных Access 2000, состоящая из двух таблиц, такой вот структуры: Первая таблица - воображаемые телефонные абоненты - в ней четыре поля - автоинкрементный код абонента, его имя, телефонный номер, и код выхода на междугороднюю линию - по умолчанию этому полю присваивается значение 8. Примечание 1: Примечание 2: Update Criteria Как я уже, кажется, говорил ADO всегда работает по принципу клиент/сервер - даже если Вы открываете локальную таблицу на своем компьютере. Это хорошо конечно, но иногда может сбить с толку, если не разобраться что именно происходит «за кулисами». А теперь пользователь исправил фамилию одного из абонентов. Теперь становится понятным, почему нужны поля с первичным ключем - иначе движок ADO не имел бы ни малейшего понятия - как ему найти на сервере строку, которую надо изменить. Примечание 1: Примечание 2: Пока все нормально, но предположим Вы - не единственный пользователь базы данных, и кто-то еще успел изменить данные до Вас. Что произойдет? просто-напросто запишет Ваш вариант изменений поверх чужого. Хорошо если это просто фамилия - а если это сумма? Кто-то добавил к этой сумме 100 долларов, а вы, не зная об этом - вписали 10? А если это была сумма, которую Вам должны заплатить? Не пойдет! Тут на арену выходит первое из динамических свойств - Update Criteria. Итак, первое свойство, о котором мы поговорим, называется Update Criteria - оно может принимать следующие значения
Внимательный читатель конечно уже поймал меня на маленькой хитрости - так как значение свойства по умолчанию adCriteriaUpdCols - значит мне не удастся вписать 10 долларов если кто-то уже вписал 100 - я получу сообщение об ошибке. Если эта цифра не равна единице - система считает, что произошел конфликт, связанный с оптимистической блокировкой при конкурирующем обновлении и выдает сообщение об ошибке «Row cannot be located for updating и т.д.» (Строка для обновления не найдена) Все вроде ясно - но тут есть одна тонкость. Разработчики баз данных SQL Server'а часто используют так называемые триггеры - специальные SQL команды, которые могут срабатывать при добавлении или изменении данных в таблице, с которой Вы работаете. В результате Ваша программа может получить значение number of rows affected вовсе не после Вашей команды, а после команды, запущенной триггером. И если Ваша команда сработала и успешно изменила запись, но триггер выполнил какую-то другую команду, которая не изменила ни одной записи к Вам придет значение number of rows affected равное нулю и выскочит сообщение об ошибке которой на самом деле не было. Что же делать, как говаривал Достоевский? (вопрос «Кто виноват» опустим) Я могу предложить такой вариант выхода - во всех триггерах первой строчкой всегда пишите команду SET NOCOUNT ON - это заставит SQL Server не считать число строк, затронутых командами триггера - и вы, таким образом, будите получать правильное значение rows affected. Update Resync Следующее свойство, о котором я хотел рассказать, называется Update Resync. Итак, после того как мы добавляем или изменяем строку, изменения записываются на сервер. Но бывают ситуации, когда сервер сам добавляет кое-что к вновь введенным данным. Самый распространенный случай - в строке есть поле типа Identity (автоинкрементное) - значение которого устанавливается сервером. Другие случаи - поля со значениями по умолчанию (если такому полю ничего не присвоить - его значение будет установлено сервером), поля типа TimeStamp (при каждом изменении строки в это поле сервер записывает время изменения с точностью до миллисекунд). Кроме того, запись, которую Вы добавили или обновили, может быть изменена сработавшим триггером.
Стоп. Посмотрим, что тут произойдет. Так как значение свойства Update Criteria по умолчанию равно adCriteriaUpdCols на сервер будет отправлена команда вида: Update Abonents Set Code = '9' where AbonentID = 15 AND Code IS NULLВсе верно - передается ключевое поле и исходное значение того поля которое надо изменить, но это исходное значение должно быть равно 8! Наша программа не знала о том, что сервер вписал значение по умолчанию - в результате мы получим сообщение об ошибке «Не удается найти строку для обновления. Некоторые значения могли быть изменены со времени последнего чтения». Некоторые Delphi программисты предлагают обновлять весь набор данных после каждого изменения - по-моему это не очень элегантный способ - ведь нужно обновить только одну строку! Но как?
Итак по умолчанию принято значение adResyncAutoIncrement - это значит что после добавления каждой записи движок ADO выдает команду select @@IDENTITY - и обновляет значение соответствующего поля на клиенте, отображая значение ID вновь добавленной строки. Увы, данная команда не поддерживалась в Access до версии 2000 - именно с этим связанна проблема, описанная в статье - Дениса Иванова Примечание: Вопрос на засыпку - какое значение нужно установить свойству Update Resync в нашем примере, чтобы при добавлении абонента сразу же считывалось значение по умолчанию поля Code? Верный ответ - ADODataSet1.Properties['Update Resync'].Value:= adResyncAutoIncrement + adResyncInserts; Другой верный ответ - или adResyncAll ADODataSet1.Properties['Update Resync'].Value:= adResyncInserts + adResyncInserts;Примечание: В результате сразу после добавления нового абонента cursor engine сперва считает значение AbonentId которое получила вновь добавленная строка, а зачем, считает с сервера строку, используя полученное значение AbonentId - таким образом мы сразу увидим все изменения которые сервер произвел над нашей новой строкой и переоткрывать набор данных не понадобится. Я написал небольшую программку, позволяющую по играться со свойством Update Resync вот ее внешний вид:
Вы можете скачать ее и поэкспериментировать - добавьте нового абонента, не внося его код, а потом исправьте код во вновь добавленной записи. Вы получите сообщение об ошибке. Несколько слов о свойстве adResyncConflicts. В случае если Вы пытаетесь изменить запись, которую уже изменили до Вас - и установлено Update Criteria равное adResyncConflicts с сервера считываются значения полей записи, вызвавшей конфликт и помещаются в свойство Underlying Values каждого элемента масcива Fields соответствующего Recordset. Обратите внимание как в моей прораммке-примере отлавливается событие RecordChangeComplete - если на этом событии переменная Reason равна erUndoUpdate - это значит что нужно отменить изменения - что я и пытаюсь сделать - но сработает это только если Update Criteria равно adResyncConflicts. На этом пока все.
Не нашли, что искали? Воспользуйтесь поиском:
|