Партнерка на США и Канаду по недвижимости, выплаты в крипто
- 30% recurring commission
- Выплаты в USDT
- Вывод каждую неделю
- Комиссия до 5 лет за каждого referral
[В начало]
Пессимистическая блокировка
В данном контексте терминами «оптимистическая» и «пессимистическая» характеризуется ожидание программиста относительно возможности возникновения конфликтов при обновлении содержащихся в БД данных одновременно несколькими пользователями. Пессимистическая блокировка предполагает, что вероятность возникновения конфликта велика. Иными словами, пользователи в одно и то же время модифицируют содержащиеся в БД данные, и высока вероятность того, что два пользователя в одно и то же время попытаются модифицировать одну и туже запись базы. Чтобы предотвратить подобный конфликт, запись блокируется в момент, когда начинается редактирование. Запись остается заблокированной до тех пор, пока редактирование завершается или отменяется. Если какой-либо другой пользователь попытается отредактировать ту же самую (заблокированную) запись, он не сможет этого сделать: возникнет исключение «Обновление невозможно, запись заблокирована».
Этот подход хорошо знаком программистам, которые ранее работали с настольными базами данных, такими как dBase и Paradox. Преимущество состоит в том, что пользователь знает, что если он начал редактировать запись, то сможет успешно завершить редактирование и внести модификации в базу. Недостаток — в том, что пользователь полностью контролирует блокирование записи. Если пользователь хорошо освоил работу с приложением, редактирование одной записи может занять всего пару секунд, однако в клиент-серверной среде с множеством пользователей даже пара секунд может показаться вечностью. С другой стороны, ничего не подозревающий пользователь может начать редактирование записи и уйти на обед. В этом случае запись останется заблокированной до тех пор, пока он не вернется на свое рабочее место. Если не предпринять каких-либо специальных мер, все это время никто не сможет отредактировать заблокированную запись. Чтобы избежать подобного, зачастую используют таймер: если клавиатура и мышь длительное время остаются в бездействии, программа автоматически разблокирует запись.
Еще одна проблема, связанная с пессимистическим блокированием, заключается в том, что для пессимистического блокирования требуется курсор, работающий на стороне сервера. Ранее мы уже говорили о том, что местоположение курсора влияет на типы доступных курсоров. Сейчас мы видим, что местоположение курсора влияет также на способы блокирования. Позднее в данной главе мы подробнее обсудим преимущества использования курсоров, работающих на стороне клиента. Если вы примете решение воспользоваться этими преимуществами, значит, вы не сможете воспользоваться пессимистической блокировкой.
[В начало]
Обновление данных
Поддержка обновляемых соединений (updatable joins) является одной из основных причин, по которым программисты используют компонент ClientDataSet (или кэшируемые обновления в BDE). Запрос, подразумевающий соединение двух таблиц, возвращает пользователю единую таблицу, и пользователь хочет обладать возможностью модифицировать записи этой таблицы. Рассмотрим следующее SQL-выражение:
SELECT * FROM Products, Suppliers WHERE Suppliers. SupNo=Products. SupNo |
Этот запрос возвращает список продуктов с указанием поставщиков, которые выполняют поставку этих продуктов. Механизм BDE рассматривает любое SQLсоединение так таблицу, предназначенную только для чтения. Дело в том, что добавление, обновление и удаление записей в объединенной таблице неоднозначно. Например, если пользователь добавляет в объединенную таблицу новую запись, надо ли добавлять в таблицу нового поставщика и новый продукт или можно ограничиться только добавлением продукта? Архитектура ClientDataSet/Provider позволяет вам указать первичную обновляемую таблицу (в этой книге об этом не рассказывается) и выполнить дополнительную настройку SQL-обновлений. Об этом частично рассказано в главе 14, а кроме того, я расскажу об этом в главе 16. Технология ADO поддерживает механизм кэширования обновлений, который называется пакетными обновлениями (batch updates) и функционирует приблизительно так же, как аналогичный механизм BDE. В следующем разделе я более подробно рассмотрю механизм пакетных обновлений ADO. Однако для того, чтобы решить проблему обновления SQL-соединений вы можете обойтись и без помощи этого механизма. Дело в том, что ADO поддерживает обновление SQL-соединений. В программу JoinData добавлен компонент ADODataset, основанный на приведенном ранее SQL-выражении. Если вы запустите программу, вы сможете отредактировать одно из полей и сохранить изменения в базе (для этого достаточно переместиться на другую запись). Никаких ошибок не возникнет, так как ADO успешно выполнит обновление БД. Дело в том, что в отличие от BDE в ADO используется более практический подход. В рамках ADO, когда выполняется соединение нескольких таблиц, каждое поле знает, к какой таблице оно принадлежит. Если вы обновляете поле в таблице Products и публикуете изменения в базе, для обновления формируется SQL-выражение UPDATE, которое обновляет значение поля в таблице Products базы данных. Если помимо поля таблицы Products вы изменяете также поле таблицы Suppliers, значит, генерируются два SQL-выражения UPDATE — по одному для каждой таблицы.
При добавлении новой строки в SQL-соединение механизм ADO ведет себя подобным же образом. Если вы вставляете строку и добавляете значения только для полей таблицы Products, значит, генерируется только одно SQL-выражение INSERT, которое добавляет новую запись в таблицу Products. Если вы вводите значения для полей обеих таблиц, генерируется два SQL-выражения INSERT — по одному для каждой таблицы. Важен порядок, в котором выполняются эти выражения, так как новый продукт может ссылаться на нового поставщика, поэтому информация о поставщике должна добавляться в таблицу Suppliers в первую очередь. Серьезная проблема возникает, если выполняется удаление строки из объединенной таблицы. При попытке выполнить удаление строки объединенной таблицы вы увидите сообщение об ошибке. Конкретный текст сообщения зависит от версии ADO, а также от используемой базы данных. Это сообщение может сбить вас с толку, так как, скорее всего, оно не будет иметь отношения к истинной причине проблемы. Проблема связана с тем, что невозможно удалить запись, на которую ссылаются другие записи. В нашем примере вы, скорее всего, увидите сообщение о том, что невозможно удалить запись о продукте, потому что существуют другие записи, ссылающиеся на эту запись. Однако если вы проведете пару экспериментов, вы обнаружите, что ошибка возникает вне зависимости от того, существуют ли в базе другие записи, ссылающиеся на удаляемый продукт, или таких записей нет. Чтобы понять причину проблемы, необходимо воспользоваться таким же подходом, какой используется при добавлении новых записей в объединенную таблицу. В случае удаления строки объединенной таблицы механизм ADO генерирует два SQL-выражения DELETE: одно для таблицы Suppliers, а второе — для таблицы Products. Выражение DELETE для таблицы Products выполняется успешно, а вот выражение DELETE для таблицы Suppliers дает сбой — поставщик продуктов не может быть удален из таблицы, так как с ним, как правило, связано несколько ссылающихся на него записей о продуктах.
СОВЕТ
Если вы хотите посмотреть, какие именно SQL-выражения генерируются в результате выполнения той или иной команды ADO, имейте в виду, что, используя SQL Server, вы можете просмотреть эти выражения при помощи инструмента SQL Server Profiler.
Даже если вы понимаете, как именно работает этот процесс, полезно взглянуть на проблему глазами пользователя. Я могу поспорить, что когда пользователь удаляет запись из объединенной таблицы, в 99 процентах случаев он намерен удалить только запись о продукте, оставив запись о поставщике без изменений. К счастью, вы можете добиться такого результата, если воспользуетесь специальным динамическим свойством Unique Table. С его помощью вы можете указать, что удаление строки имеет отношение только к таблице Products, но не к таблице Suppliers. Для этого используется следующий код:
ADOQuery1.Properties['Unique Table'].Value := 'Products'; |
Присвоить значение этому свойству на этапе проектирования нельзя, поэтому данное выражение следует разместить в обработчике события OnCreate.
[В начало]
Пакетные обновления (Batch updates)
При использовании механизма пакетных обновлений любые изменения, вносимые пользователем, накапливаются в локальной памяти. Позже полный пакет этих изменений за одну операцию может быть внесен в базу данных. Очевидно, что подобный подход обеспечивает выигрыш в производительности, однако существуют и другие преимущества, делающие его удобным. В частности, при использовании механизма пакетных обновлений пользователь может выполнять изменения даже тогда, когда он отключен от базы данных. Эта возможность может употребляться для обеспечения работы пользователя в автономном режиме, при применении технологий наподобие Briefcase (Портфель), а также в веб-приложениях, основанных еще на одном механизме ADO, который называется RDS (Remote Data Services). Вы можете включить пакетные обновления для любого набора данных ADO. Для этого необходимо присвоить свойству LockType значение ltBatchOptimistic перед тем, как набор данных будет открыт. Кроме того, вы должны присвоить свойству CursorLocation значение clUseClient, так как пакетные обновления обрабатываются механизмом ADO Cursor Engine. В результате любые изменения, вносимые пользователем в набор данных, будут сохраняться в области delta (в этой области хранится список изменений). Набор данных будет выглядеть так, как будто данные были изменены, однако на самом деле информация об изменениях хранится в памяти — эти изменения не внесены в базу данных. Чтобы опубликовать изменения в БД (перенести их из памяти в базу данных), необходимо обратиться к методу ApplyBatch (этот метод эквивалентен методу ApplyUpdates механизма BDE):
ADODataSet1.UpdateBatch; |
Если вы хотите отменить все накопленное в памяти, воспользуйтесь методом CancelBatch — это аналог метода CancelUpdates. В рамках механизмов пакетных обОбновление новлений ADO, кэшируемых обновлений BDE и механизма кэширования Client - DataSet используется много других методов и свойств со схожими именами. Например, как и в BDE, свойство UpdateStatus набора данных ADO может использоваться для того, чтобы узнать состояние некоторой записи: была ли запись добавлена, модифицирована, удалена или она не подвергалась каким-либо изменениям. Это свойство весьма удобно, если вы хотите выделить модифицированные записи цветом или отобразить их состояние в строке состояния. Существуют некоторые различия в синтаксисе, например вместо вызова RevertRecord в ADO используется вызов CancelBatch(arCurrent);. Одна весьма полезная возможность механизма кэшированных обновлений BDE отсутствует в ADO: механизм слежения за тем, существуют ли неопубликованные в БД обновления. В BDE для этой цели используется свойство UpdatesPending. Это свойство содержит в себе значение True в случае, если в набор данных были внесены изменения, которые еще не опубликованы в базе данных. Это свойство удобно использовать в обработчике события OnCloseQuery:
procedure TForm1.FormCloseQuery( Sender: TObject; var CanClose: Boolean); begin CanClose := True; if ADODataSet1.UpdatesPending then canClose := (MessageDlg('Существуют изменения, которые еще не опубликованы в БД' #13 + 'Завершить приложение в любом случае?', mtConfirmation, [mbYes, mbNo], 0) = mrYes); end; |
Однако, обладая необходимыми знаниями и изобретательностью, вы можете написать свою собственную функцию ADOUpdatesPending. Чтобы написать такую функцию, вы должны знать, что наборы данных ADO поддерживают свойство FilterGroup, которое функционирует примерно так же, как фильтр. В отличие от свойства Filter стандартного набора данных, которое фильтрует данные, исходя из некоторого условия, свойство FilterGroup может выполнять фильтрацию, исходя из состояния записи. Существует несколько состояний фильтрации, и одно из них соответствует значению fgPendingRecords. Это состояние соответствует записям, которые были модифицированы, но информация об изменении которых еще не была опубликована в базе данных. Таким образом, чтобы взглянуть на все изменения, которые были сделаны, но не опубликованы, достаточно выполнить две строки кода:
ADODataSet1.FilterGroup := fgPendingRecords; ADODataSet1.Filtered := True; |
Естественно, после выполнения этих команд в наборе данных будут присутствовать записи, которые были удалены, при этом поля этих записей будут пустыми, — это не очень удобно, так как вы не сможете понять, какая именно запись была удалена. (Первая версия ADOExpress функционировала иначе: для удаленных записей отображались все значения полей.)
Чтобы решить проблему UpdatesPending, вам потребуется использовать клонирование наборов данных (об этой возможности рассказывалось ранее). Функция ADOUpdatesPending настраивает свойство FilterGroup таким образом, чтобы в наборе данных присутствовала только информация о сделанных, но не опубликованных изменениях. Теперь надо проверить, присутствует ли в наборе данных хотя бы одна запись. Если хотя бы одна запись присутствует, значит, некоторые изменения еще не опубликованы в базе данных. Если после фильтрации набор данных оказался пустым, значит, все сделанные ранее изменения уже опубликованы в БД. Однако если вы попытаетесь проверить количество записей в реальном наборе данных, перенастройка свойства FilterGroup приведет к смещению указателя на текущую запись — пользовательский интерфейс немедленно отреагирует на это. Во избежании этого, воспользуйтесь клоном набора данных:
function ADOUpdatePending(ADODataSet: TCustomADODataSet): boolean; var Clone: TADODataSet; begin Clone := TADODataSet. Create(nil); try Clone. Clone(ADODataSet); Clone. FilterGroup := fgPendingRecords; Clone. Filtered := True; Result := not (Clone. BOF and Clone. EOF); Clone. Close; finally Clone. Free; end; end; |
В этой функции вы клонируете изначальный набор данных, настраиваете свойство FilterGroup, а затем проверяете, находится ли указатель одновременно в начале и в конце набора данных. Если это так, значит, набор данных пуст.
[В начало]
Оптимистическая блокировка
Ранее мы с вами рассмотрели использование свойства LockType и обсудили принцип функционирования пессимистической блокировки. В данном разделе мы рассмотрим оптимистическую блокировку. Оптимистическая блокировка не только является предпочтительной для высокопроизводительных систем, помимо этого именно такой тип блокировки используется при выполнении пакетных обновлений.
Оптимистическая блокировка предполагает, что возникновение конфликта между двумя пользователями, пытающимися отредактировать одну и ту же запись одновременно, маловероятно. Отсюда следует, что любому пользователю разрешается редактировать любую из записей в любое время. Последствия конфликтов обрабатываются в момент сохранения изменений в базе данных. Таким образом, конфликты рассматриваются как исключение из правила. Если два пользователя попытаются сохранить изменения одной и той же записи, выполнить это удастся только первому пользователю, второму пользователю будет отказано. Подобное поведение реализуется в приложениях, работающих в рамках модели Briefcase (Портфель), а также в веб-приложениях, в которых отсутствует постоянное соединение с базой данных и поэтому невозможно реализовать пессимистическую блокировку. В отличие от пессимистической, оптимистическая блокировка не требует длительного блокирования ресурсов: запись блокируется только в момент обновления. Таким образом, в среднем потребление ресурсов ниже, а база данных более масштабируема.
Давайте рассмотрим пример. Представьте, что у вас есть компонент ADODataSet, соединенный с таблицей Customer из примера dbdemos. mdb; при этом свойство LockType обладает значением ltBatchOptimistic, а содержимое набора данных отображается в сетке DBGrid. Предположим также, что у вас есть кнопка, при щелчке на которой происходит обращение к методу UpdateBatch. Запустите две копии этой программы (это программа BatchUpdates). Начните редактирование записи в первой копии программы. Для простоты в процессе демонстрации я использую один компьютер, однако все то же самое произойдет и в случае, если редактирование одной и той же записи будет выполняться на двух компьютерах.
1. Выберите компанию Bottom-Dollar Markets в Канаде и измените ее имя на Bottom-Franc Markets.
2. Сохраните изменения в базе, для этого просто перейдите к другой записи и щелкните на кнопке пакетного обновления.
3. Во второй копии программы найдите ту же самую запись и измените имя компании на Bottom-Pound Markets.
4. Перейдите к другой записи и щелкните на кнопке пакетного обновления. Обновление не сработает.
Как и в случае с другими сообщениями об ошибках ADO, текст сообщения будет зависеть не только от версии ADO, но также от того, насколько точно вы повторили описанную последовательность действий. В ADO 2.6 на экране появится следующее сообщение об ошибке: Row cannot be located for updating. Some values may have been changed since it was last read (Не удается обнаружить строку для обновления. Некоторые значения строки могли быть изменены с момента последнего чтения).
В этом заключается вся суть оптимистической блокировки. Для обновления записи выполняется следующее SQL-выражение:
UPDATE CUSTOMER SET CompanyName="Bottom-Pound Markets" WHERE CustomerID="BOTTM" AND CompanyName="Bottom-Dollar Markets" |
Выражение идентифицирует запись, используя первичный ключ и значение поля CompanyName, какими они были в момент чтения записи из базы в память. Ожидается, что в результате выполнения этого выражения будет модифицирована одна запись таблицы. Однако в рассмотренном нами примере в результате выполнения выражения ни одна из записей не модифицируется. Такой исход возможен, только если запись была удалена, изменился первичный ключ или изменяемое поле было изменено кем-то до вас. В любом из этих случаев обновление окончится неудачей.
Если бы второй пользователь попытался изменить значение поля ContactName (но не CompanyName), тогда выражение обновления выглядело бы следующим образом:
UPDATE CUSTOMER SET ContactName="Liz Lincoln" WHERE CustomerID="BOTTM" AND ContactName="Elizabeth Lincoln" |
В нашем случае обновление было бы выполнено успешно, так как другой пользователь не изменит ни первичный ключ, ни контактное имя. Это поведение напоминает режим Update Where Changed механизма BDE. Вместо свойства UpdateMode, используемого в BDE, в рамках ADO используется динамическое свойство Update Criteria набора данных. В следующем списке перечислены допустимые значения этого динамического свойства.
Константа | Способ идентификации записей |
adCriteriaKey | Только столбцы, входящие в состав первичного ключа |
adCriteriaAllCols | Все столбцы |
adCriteriaUpdCols | Только столбцы первичного ключа и модифицированные столбцы |
adCriteriaTimeStamp | Только столбцы первичного ключа и столбец отметки времени |
Не следует думать, что один из этих режимов будет предпочтительным для всего вашего приложения. На практике выбор режима определяется тем, информация какого характера содержится в таблице. Предположим, в таблице Customer присутствуют только три поля: CustomerID (идентификатор), Name (имя) и City (город). Изменение любого из этих полей никак не влияет на значения других полей таблицы, поэтому в подобной ситуации вполне можно использовать режим adCriteriaUpdCols (этот режим используется по умолчанию). Однако если помимо перечисленных полей в таблицу входит еще поле PostalCode (почтовый индекс), тогда обновление этого поля должно быть в обязательном порядке согласовано с обновлением поля City (город). Иными словами, нельзя допустить, чтобы один пользователь модифицировал поле PostalCode и в то же самое время другой пользователь модифицировал поле City без всякого согласования с первым пользователем. В этом случае более безопасным будет режим обновления adCriteriaAllCols. Следует также рассказать о том, как ADO осуществляет обработку ошибок в процессе обновления нескольких записей. В BDE и ClientDataSet для этой цели вы можете воспользоваться событием OnUpdateError, которое позволит вам отреагировать на ошибку, связанную с обновлением записи, и разрешить проблему перед тем, как перейти к следующей записи. В ADO такой возможности нет. Вы можете следить за прогрессом, успехом или неудачей пакетного обновления при помощи событий OnWillChangeRecord и OnRecordChangeComplete, однако вы не можете изменить содержимое обновляемой записи и попытаться заново внести ее в базу, как это возможно в рамках BDE и ClientDataSet. Проблема состоит также в том, что если ошибка возникает в ходе пакетного обновления, процедура обновления не останавливается, а продолжает выполняться. Пакетное обновление выполняется до самого конца, то есть до тех пор, пока все изменения не будут внесены в базу (при этом могут возникнуть другие ошибки). В результате вы можете получить сбивающее с толку или неправильное сообщение об ошибке. Если в ходе пакетного обновления возникло несколько ошибок (не удалось обновить несколько записей), ADO 2.6 отобразит на экране следующее сообщение: Multistep OLE DB operation generated errors. Check each OLE DB status value, if available. No work was done (В ходе выполнения многошаговой операции OLE DB возникли ошибки. Если возможно, проверьте каждое из состояний OLE DB. Никакой работы не было сделано). Проблема заключается в последнем предложении: No work was done. ADO сообщает нам, что ничего не было сделано, однако это не так. Запись, ставшая причиной ошибки, действительно не была обновлена, однако все остальные записи успешно опубликованы в базе данных.
[В начало]
Разрешение конфликтов, связанных с обновлением данных
В процессе обновления разумным будет следующий подход: пользователь вносит в набор данных необходимые изменения, затем выполняется пакетное обновление, при обновлении некоторых записей возникают ошибки, когда процесс обновления полностью завершается, пользователю выдается перечень проблемных записей и предоставляется возможность разрешить каждый из конфликтов. Чтобы получить перечень всех проблемных записей, можно воспользоваться значением fgConflictingRecords свойства FilterGroup:
ADODataSet1.FilterGroup := fgConflictingRecords; ADODataSet1.Filtered := True; |
Для каждого поля каждой проблемной записи можно сообщить пользователю три ключевых значения: значение поля, которое содержалось в записи в момент, когда эта запись была впервые прочитана из БД; значение поля, которое было присвоено полю этим пользователем; значение, которое содержится в БД на текущий момент (то есть во время попытки обновления). Для этого служат следующие свойства Tfield.
Свойство | Описание |
NewValue | Значение, занесенное в поле пользователем |
CurValue | Значение, которое обнаружено в базе в момент обновления |
OldValue | Значение, которое содержалось в базе в момент первого чтения записи |
Те, кто работает с компонентом ClienDataSet, знают о существовании удобного диалогового окна ReconcileErrorForm. Это диалоговое окно формируется автоматически, оно показывает пользователю старые, новые и текущие значения полей проблемной записи и позволяет пользователю выбрать метод решения конфликта. К сожалению, в ADO аналог этого диалогового окна отсутствует. Класс TReconcileErrorForm был разработан специально для компонента ClientDataSet, поэтому его очень сложно адаптировать для использования с наборами данных ADO. Следует также сказать о порядке функционирования упомянутых свойств класса TField. Эти свойства основаны на объектах ADO Fields, на которые они ссылаются. Это означает, что порядок функционирования этих свойств целиком и полностью определяется провайдером OLE DB, который вы используете. Остается надеяться, что используемый вами провайдер корректно поддерживает возможности, в которых вы нуждаетесь. Большинство провайдеров хорошо справляются с этой задачей, однако провайдер Jet OLE DB возвращает одно и то же значение для свойств CurValue и OldValue. Говоря точнее, в качестве текущего значения поля возвращается значение, которое содержалось в поле в момент первого чтения записи из базы данных. Иными словами, Jet не позволяет определить значение, присвоенное полю другим пользователем (если только вы сами не предпримете каких-либо дополнительных действий, чтобы осуществить это). Если вы используете провайдер SQL Server OLEDB, вы сможете обратиться к свойству CurValue только после выполнения метода Resync набора данных, при этом параметр AffectRecords должен быть равен значению adAffectGroup, а параметр ResyncValues — содержать значение adResyncUnderlyingValues. Вот соответствующий код:
adoCustomers. FilterGroup := fgConflictingRecords; adoCustomers. Filtered := true; adoCustomers. Recordset. Resync(adAffectGroup, adResyncUnderlyingValues); |
[В начало]
Отключенные наборы записей
Теперь, когда вы знаете о пакетных обновлениях, мы можем приступить к изучению еще одной возможности ADO: отключенных наборах записей. Отключенный набор записей (disconnected recordset) — это набор записей, который отключен от подключения к базе данных. Пользователь может работать с таким набором записей в точности так же, как он работает с обычным, подключенным набором записей. Это весьма впечатляющая возможность: между подключенным и отключенным наборами записей фактически не существует различий. Свойства и возможности фактически идентичны. Чтобы отключить набор записей от подключения, необходимо присвоить свойству CursorLocation значение clUseClient, а свойству LockType — значение ltBatchOptimistic. После этого вы присваиваете свойству Connection значение nil, в результате набор записей становится отключенным:
ADODataSet1.Connection := nil; |
После этого набор записей продолжает хранить в себе те же данные и поддерживать те же возможности навигации. Он позволяет добавлять, редактировать и удалять записи. Единственное отличие состоит в том, что вы не можете выполнить пакетное обновление, так как для этого вам потребуется подключиться к серверу БД. Чтобы восстановить соединение (и воспользоваться методом UpdateBatch), необходимо вновь настроить свойство Connection:
ADODataSet1.Connection := ADOConnection1; |
Эта возможность также поддерживается в BDE и в других технологиях работы с данными, однако для этого вы должны переключиться на использование Client- DataSet. Прелесть ADO состоит в том, что вы с самого начала можете разработать приложение так, будто бы вы понятия не имеете о возможности отключения набора записей от БД. Вы используете обычные компоненты dbGo самым обычным образом. После этого вы можете без лишних сложностей добавить в вашу программу возможность отключения от БД так, как будто вы только что узнали о существовании этой возможности. При этом вам не потребуется менять основной код вашего приложения.
Отключение набора записей может потребоваться для того, чтобы:
· минимизировать общее количество параллельных подключений к БД;
· обеспечить работу приложения в автономном режиме (в соответствии с моделью Briefcase).
О приложениях, работающих в рамках модели Briefcase (Портфель), мы поговорим в одном из следующих разделов.
Большинство бизнес-приложений, работающих в рамках концепции «клиентсервер», открывают таблицы базы данных и поддерживают постоянное подключение к БД в течение всего времени, пока таблица находится в открытом состоянии.
Однако на самом деле подключение необходимо только во время выполнения двух операций: извлечение данных из БД и обновление данных в БД. Представьте, что вы модернизируете вашу программу таким образом, чтобы она выполняла отключение от базы данных сразу же после того, как происходит открытие таблицы и извлечение данных. Как только необходимые данные передаются на клиентский компьютер, соединение разрывается. Вот соответствующий код:
ADODataSet1.Connection := nil; ADOConnection1.Connected := False; |
После этого соединение может потребоваться только в случае, когда пользователь желает выполнить пакетное обновление. Код выполнения обновления выглядит следующим образом:
ADOConnection1.Connected : = True; ADODataSet1.Connection := ADOConnection1; try ADODataSet1.UpdateBatch; finally ADODataSet1.Connection := nil; ADOConnection1.Connected := False; end; |
Если вы будете использовать подобный подход в вашем приложении, среднее количество соединений с БД, открытых в одно и то же время, будет минимальным — соединения будут открываться только на короткое время, когда они действительно нужны. Очевидно, что подобную клиент-серверную структуру существенно проще масштабировать. Сервер сможет обслуживать значительно большее количество клиентов. Существует также недостаток: формирование соединения с некоторыми (но не со всеми) БД требует значительного времени, в подобной ситуации пакетное обновление будет выполняться медленнее.
[В начало]
Накопление соединений (Connection Pooling)
Разговоры о разрыве и повторном открытии соединений наводят нас на мысль, нельзя ли использовать соединения повторно? Этот механизм называется Connection Pooling. При его использовании, когда приложение завершает работу с подключением, подключение не уничтожается, вместо этого оно откладывается в область накопления (пул) и затем может использоваться повторно. Этот процесс выполняется автоматически, конечно, при условии, что используемый вами провайдер OLE DB поддерживает его и этот механизм включен. В этом случае никаких дополнительных действий предпринимать не надо.
Производительность является основной причиной применения подобного механизма. Чтобы установить соединение с базой данных, зачастую требуется время. Если вы имеете дело с настольной базой данных, такой как Access, соединение устанавливается фактически мгновенно. Однако в клиент-серверной среде, например при использовании сервера Oracle, доступ к которому осуществляется через сеть, для формирования соединения может потребоваться несколько секунд. Таким образом, имеет смысл подумать о повторном использовании такого ценного ресурса, как соединение с БД.
При использовании механизма накопления ADO каждый раз, когда приложение «уничтожает» объект ADOConnection, этот объект добавляется в специальный пул (место временного хранения). Если в дальнейшем возникает необходимость создать новое соединение, система автоматически выполняет поиск подходящего соединения в пуле. Если обнаруживается существующее соединение, обладающее строкой подключения, совпадающей с той, в которой нуждается пользователь, это соединение используется повторно. Если подходящего соединения не обнаруживается, происходит его создание. Соединения остаются в пуле до тех пор, пока либо они не будут востребованы, либо приложение не завершит работу, либо не истечет время тайм-аута. По умолчанию продолжительность тайм-аута составляет 60 секунд, однако начиная с версии MDAC 2.5 это значение можно изменить. Для этого необходимо указать продолжительность тайм-аута в ключе реестра HKEY_CLASSES_ROOT\ CLSID\<CLSID-провайдера>\SPTimeout. Процесс накопления и повторного использования подключений выполняется в прозрачном режиме без каких-либо дополнительных действий со стороны разработчика. Аналогичный механизм накопления соединений функционирует в BDE при использовании MTS (Microsoft Transaction Server) и COM+, однако в отличие от BDE, механизм ADO выполняет накопление соединений самостоятельно, без помощи MTS или COM+.
По умолчанию механизм накопления соединений активизирован для всех провайдеров MDAC OLE DB реляционных баз данных (включая SQL Server и Oracle). Важным исключением является провайдер Jet OLE DB. Если вы используете ODBC, вы можете выбрать между накоплением соединений ODBC и накоплением соединений ADO, однако не рекомендуется использовать оба этих механизма одновременно. Начиная с версии MDAC 2.1 по умолчанию накопление соединений ADO включено, а накопление соединений ODBC отключено.
ПРИМЕЧАНИЕ
Вне зависимости от провайдера OLE DB накопление соединений не осуществляется в среде Windows 95.
К сожалению, в составе ADO отсутствуют инструменты, позволяющие следить за содержимым пула соединений. Однако для этой цели вы можете использовать инструмент SQL Server Performance Monitor, который снабдит вас информацией о подключениях к базе данных SQL Server.
Включить или отключить накопление соединений можно либо при помощи реестра, либо при помощи строки подключения. В реестре для этой цели используется параметр OLEDB_SERVICES, который расположен в разделе HKEY_CLASSES_ROOT\ CLSID\<CLSID-провайдера>. В этом параметре хранится битовая маска, при помощи которой вы можете отключить некоторые службы OLE DB, в том числе накопление соединений, журналирование транзакций и другие. Чтобы отключить накопление соединений с использованием строки подключения, добавьте в конце строки подключения последовательность символов ;OLE DB Services=-2. Чтобы включить накопление соединений для провайдера Jet OLE DB, добавьте в конце строки подключения последовательность ;OLE DB Services=-1. В результате будут включены все службы OLE DB.
[В начало]
Сохранение набора записей в постоянной памяти (Persistent Recordset)
Возможность сохранения набора записей на жестком диске является важной составной частью приложений Briefcase (Портфель). Используя эту возможность, вы можете сохранить содержимое набора записей в файл на локальном жестком диске. Позже вы можете загрузить данные из этого файла в набор записей. Помимо прочих преимуществ, эта возможность позволяет разработчикам создавать реальные однозвенные приложения — вы можете установить приложение базы данных, но при этом не устанавливать саму базу данных. Благодаря этому для установки программы требуется очень небольшое пространство на клиентском жестком диске. Чтобы сохранить набор данных на жестком диске, используется метод SaveToFile:
ADODataSet1.SaveToFile('Local. ADTG'); |
В результате обращения к этому методу данные вместе с областью дельта сохраняются на локальном жестком диске. Чтобы загрузить данные из файла, можно воспользоваться методом LoadFromFile, который принимает единственный параметр — имя загружаемого файла. Формат файла называется ADTG (Advanced Data Table Gram). Этот формат является собственностью компании Microsoft. Это весьма эффективный формат. При желании вы можете сохранить файл в формате XML, для этого необходимо передать методу SaveToFile второй параметр:
ADODataSet1.SaveToFile('Local. XML', pfXML); |
Следует иметь в виду, что (в отличие от ClientDataSet) ADO не содержит в себе собственной поддержки формата XML — для генерации XML-кода используется механизм MSXML. Следовательно, пользователь вашего приложения должен будет установить Internet Explorer 5.0 или загрузить MSXML с веб-узла компании Microsoft.
Если вы планируете сохранять данные локально в формате XML, помните о некоторых недостатках этого подхода:
· сохранение и загрузка файлов XML выполняется медленнее, чем сохранение и загрузка файлов ADTG;
· XML-файлы (создаваемые механизмом ADO, впрочем, как и любые другие XML-файлы) обладают значительным размером, их размер существенно превышает размер ADTG-файла, содержащего такие же данные (как правило, размер XML-файла в два раза превышает размер аналогичного ADTG-файла);
· формат XML, генерируемый ADO, является специфическим форматом Microsoft (это характерно для многих других компаний, поддерживающих собственный порядок генерации XML), это означает, что XML-код, генерируемый ADO, не может быть прочитан компонентом ClientDataSet и наоборот (к счастью, эту проблему можно решить, воспользовавшись входящим в состав Delphi компонентом XML Transform, который позволяет выполнять преобразование между различными структурами XML).
Если вы собираетесь использовать механизм сохранения набора записей только в рамках однозвенного приложения и не предполагаете использовать модель Briefcase (Портфель), тогда вы можете воспользоваться компонентом ADODataSet, присвоить его свойству CommandType значение cmdFile, а свойству CommandText — имя файла. Благодаря этому вы избавляетесь от необходимости обращаться к методу LoadFromFile вручную. Однако вам по-прежнему придется обратиться к методу SaveToFile. В приложении Briefcase (Портфель) этот подход будет слишком ограничивающим, так как в таких приложениях набор данных используется в двух режимах.
[В начало]
Модель Briefcase (Портфель)
Теперь, когда вы знакомы с пакетными обновлениями, отключенными наборами записей и сохранением наборов записей в локальных файлах, вы можете приступить к изучению модели Briefcase (Портфель). Основная идея состоит в том, чтобы обеспечить пользователю возможность работать с вашим приложением даже тогда, когда он не имеет возможности подключиться к базе данных, то есть когда пользователь путешествует или находится в офисе у клиента компании. Проблема заключается в том, что в офисе у клиента пользователь не может подключиться к корпоративной сети вашей компании, а значит, не может подключиться к серверу базы данных. Отсюда следует, что данные не могут попасть на пользовательский портативный компьютер и быть обновлены.
Чтобы обеспечить автономную работу пользователя, вам потребуются такие технологии, как отключенные наборы данных и сохранение наборов данных на локальном диске. Представьте себе, что вы разработали клиент-серверное приложение, которое обеспечивает доступ к серверу базы данных и полностью удовлетворяет запросам пользователя. Теперь пользователь требует, чтобы вы обеспечили работу этого приложения в автономном режиме, то есть даже тогда, когда нет возможности подключиться к серверу БД. Для этого вы должны добавить в приложение возможность сохранения данных на пользовательском локальном жестком диске. Иными словами, прежде чем пускаться в путешествие, пользователь должен отдать команду подготовки приложения к работе в автономном режиме. По этой команде каждая таблица сохраняется в локальном файле при помощи метода SaveToFile. В результате на пользовательском жестком диске возникает коллекция файлов ATDG или XML, которые являются отражением содержимого базы данных. После этого пользователь может отключить свой портативный компьютер от сети и продолжить работу с привычным для него приложением в автономном режиме.
Приложение должно автоматически определять, работает ли оно автономно или существует возможность подключения к сети. Для того чтобы определить это, вы можете попытаться подключиться к базе данных и проверить, открылось ли соединение. Также вы можете проверить присутствие локального файла портфеля или воспользоваться собственным специальным флагом. Если приложение работает в автономном режиме, вместо того чтобы подключаться к базе данных, оно должно извлечь данные из локального файла при помощи метода LoadFromFile. Когда требуется опубликовать данные в базе, вместо метода UpdateBatches приложение должно обращаться к методу SaveToFile для каждой из таблиц. Если возможна связь с базой данных через сеть, необходимо присвоить значение True свойству Connected компонента ADOConnection, а также свойству Active каждого из компонентов ADODataSet. Когда пользователь возвращается из путешествия, он должен внести информацию о сделанных им изменениях в базу данных. Для этого необходимо загрузить данные из локальных файлов, подключить наборы данных к базе данных и обратиться к методу UpdateBatch.
СОВЕТ
Полная реализация модели Briefcase (Портфель) содержится в примере BatchUpdates, о котором уже рассказывалось ранее.
[В начало]
Пара слов об
— это часть новой архитектуры. NET, разработанной компанией Microsoft. Архитектура. NET является попыткой компании Microsoft заново перепроектировать средства и инструменты разработки программного обеспечения для того, чтобы сделать их более удобными для создания веб-приложений. является новым развитием ADO, ориентированным на решение проблем, связанных с разработкой веб-систем и устраняющим многие недостатки устаревшей технологии ADO. Проблема ADO состоит в том, что эта технология основана на COM. Для одно- и двухзвенных приложений COM является вполне приемлемой платформой, однако в мире Веб использовать COM в качестве транспортного механизма фактически невозможно. Для COM характерны три основные проблемы, которые ограничивают использование этой технологии в Веб: во-первых, COM функционирует только в среде Windows, во-вторых, передача наборов записей требует маршализации COM, в-третьих, вызовы COM не могут проникать через корпоративные брандмауэры. Технология решает все три проблемы благодаря использованию XML.
Еще одной особенностью является разделение традиционного набора записей ADO на несколько отдельных классов. Вместо того чтобы решать множество проблем, каждый из классов предназначается для решения одной конкретной проблемы. Например, в присутствует класс DataSetReader, который обеспечивает доступ к данным только для чтения в режиме Forward-only (только вперед), при этом данные располагаются на стороне сервера. Благодаря всем этим ограничениям данный класс обеспечивает быстрое чтение результирующего набора данных. Класс DataTable функционирует как отключенный набор записей на стороне клиента. Класс DataRelation обладает общими чертами с провайдером MSDataShape OLE DB. В любом случае знание традиционной технологии ADO окажется чрезвычайно полезным при изучении новой технологии .
[В начало]
|
Из за большого объема этот материал размещен на нескольких страницах:
1 2 3 |


