Предпочтительный способ записи приложений баз данных Delphi с транзакциями и компонентами, учитывающими данные

каков предпочтительный способ записи приложений баз данных Delphi с использованием транзакций, а также компонентов, учитывающих данные?

Я должен написать клиентское приложение, которое получает доступ к таблицам InnoDB и делает некоторые мастер-детали внутри транзакций. Проведя некоторое исследование транзакций (с общей точки зрения), я смиренно делаю вывод, что компоненты, не знающие данных, и SQL с ручной кодировкой были бы "идеальным совпадением" транзакций; но компоненты, знающие данные не будет. Кажется, они не созданы друг для друга.

У меня есть реальная необходимость использовать транзакции, но, с другой стороны, я не мог просто выбросить компоненты с данными, потому что они значительно упрощают вещи.

может кто-нибудь просветить меня? Я гуглил его, но не нашел никакого полезного ответа. Возможно, потому, что мой английский недостаточно хорош, что мои ключевые слова ограничены.

кстати, я использую Delphi 7 и в настоящее время оцениваю UniDAC как библиотека доступа к данным.

спасибо.

редактировать

пример для описания аспекта моего вопроса:

представьте себе форму с 2 DBGrids на нем. Первая сетка-MasterGrid, а над ней находятся следующие кнопки: Добавить, изменить и удалить. Вторая сетка-DetailGrid. Если пользователь нажимает Add, то он идет следующим образом:

  • подключение.Starttransaction тогда
  • мастер.Добавьте затем Master.Пост тогда мастер.Edit (так что master dataset имеет первичный ключ автоинкремента, и теперь он доступен для редактирования)
  • показать форму редактирования модально, в которой пользователь заполняет основные записи, а также добавить некоторые подробные записи с помощью другой формы.
  • если пользователь нажмет OK, приложение будет делать Master.Сообщение и соединение.Совершать. Если пользователь нажмет "отмена", приложение выполнит подключение.Отмена.

Я знаю, что транзакции должны быть как можно короче, но вы можете видеть выше, что транзакция только так коротка, как скорость заполнения пользователем формы.

Если бы я использовал компоненты, не связанные с данными, я бы сделал пользовательскую вставку SQLs на основе пользовательского ввода, а затем выполнил SQL между StartTransaction и Commit. Таким образом, я могу добиться очень короткой сделки.

правка 2

благодарю всех вас за участие. Я выбираю ответ от vcldeveloper, потому что это самое близкое решение к моей текущей потребности.

5 ответов


другие упоминали использование комбинации DatasetProvider и ClientDataset для пакетного обновления, но в случае использования компонентов ADO или UniDAC вам не нужен дополнительный уровень DatasetProvider + ClientDataset, потому что и ADO, и UniDAC поддерживают пакетные обновления.

на ADO, что вы должны сделать, это установить LockType вашего набора данных ltBatchOptimistic. Для Юнидак, вы должны установить CacheUpdate свойство правда.

Это изменение позволяет вашему набору данных кэшировать все изменения, внесенные в его набор записей в памяти, и отправлять их все вместе в базу данных только при вызове UpdateBatch метод (ADO) или ApplyUpdates метод (UniDAC).

теперь вы должны позволить своему пользователю вставлять / редактировать запись в главном наборе данных и любые записи, которые он/она хочет в наборе данных details, используя любые компоненты данных, которые вам нравятся. Все изменения будут кэшироваться. Когда ваш пользователь закончит, вы можете запустить новую транзакцию и сначала вызвать UpdateBatch (или ApplyUpdate в случае UniDAC) для основного набора данных, а затем для набора данных details, и если все пойдет хорошо, зафиксируйте транзакцию.

это сделает ваши транзакции короткими без необходимости дополнительного уровня ClientDataset.

в отношении


Я понимаю ваш вопрос, я думаю. При открытии TADODataSet, например, с 10 строками данных для редактирования в форме, с компонентами, учитывающими данные, существуют ситуации, в которых вы хотите кэшировать все изменения, внесенные во все 10 строк (и, возможно, удаления и вставки), и зафиксировать его как один пакет. Вы не можете открыть транзакцию при первом изменении, так как это заблокирует других пользователей, изменяющих те же данные. Транзакции должны быть как можно короче.

Что Я сделать в эскизном сценарии использовать следующие компоненты в цепочке:

TADOConnection >> TADODataSet >> TDataSetProvider >> TClientDataSet >> TDataSource >> TDBEdits etc.

теперь все изменения кэшируются в TClientDataSet, и вы можете вызвать его метод ApplyUpdates для разноски всех изменений в одной быстрой транзакции. Имейте в виду, что также можно использовать несколько TADODataSets и несколько TClientDataSets для структуры master-detail(-detail-etc) с вложенными наборами данных. Все изменения master-detail также могут кэшироваться и применяться в одном пакете в одной транзакции. См. справку и ресурсы в другом месте для получения всех подробностей о реализации этого. Сначала это нелегко. Но если вы поняли, что это легко и предлагает массу возможностей. (Автономное редактирование, изучение изменений перед их применением и т. д.)


чтобы избежать необходимости выполнять большие транзакции, я использую DataSetProviders и ClientDatasets (даже локально).

вы можете использовать это как своего рода кэш, и это дает вам лучшее из обоих миров. Для упрощения работы с пользовательским интерфейсом можно использовать элементы управления с поддержкой данных. Действия пользователя над наборами данных "записываются" ClientDataSets (вид кэша базы данных).

когда пользователь готов сохранить изменения в базу данных (например, данные счета-фактуры все на месте), вы вызываете ApplyUpdates метод для набора данных(ов).

в простейшем сценарии, где все наборы данных находятся в отношениях master-detail (вложенные поставщиком), поставщик запускает и фиксирует/откатывает транзакцию сам по себе, поэтому вы находитесь в ситуации all или nothing автоматически.

Если у вас более сложные отношения, вы можете вызвать StartTransaction перед началом применения обновлений для каждого вовлеченного набора ClientDataSet и в конце вызова фиксации или отката по мере необходимости). Логика для поставщика заключается в том, что если соединение имеет активную транзакцию при вызове ApplyUpdates, то оно ничего не делает с транзакцией, а просто разносит изменения в базу данных, предполагая, что вы контролируете транзакцию.

вы должны прочитать о TClientDataSet и как обращаться с OnReconcileError и экспериментировать с технологией, прежде чем поместить ее в производственные среды, но это для меня это очень, очень хорошо.

мои 2 цента.


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

общее решение, как уже было сказано, заключается в использовании промежуточного уровня (ClientDataSet). Но реальная проблема с вашим сценарием заключается в том, что вы не можете получить значение autoincrement для главной таблицы без Master.Добавить и освоить.Сообщение, и, следовательно, вы начинаете написать транзакция задолго до того, как она действительно потребуется.

поэтому, если вы не хотите использовать промежуточный уровень и по-прежнему использовать компоненты с данными с коротким написать транзакции вы должны подумать о базе данных, которая поддерживает получение значения autoincremented без выполнения INSERT (to master table). Пример:Жар база данных и компоненты доступа к данным FibPlus для Firebird полностью поддерживают эту функцию.


транзакции должны быть как можно короче нужны. Проблема в том, как различные базы данных обрабатывают блокировку. Базы данных, которые выполняют только блокировку на уровне строк и могут немедленно возвращаться из блокировки без ожидания, имеют гораздо меньшую вероятность взаимоблокировки. Обычно вставки менее проблематичны (хотя другой пользователь не будет видеть новые строки до фиксации, в зависимости от уровня изоляции), обновления и удаления более проблематичны. Совершение слишком часто может быть "плохим". Кэширование изменений и применение их в одной операции-еще одна возможность , но вы должны обрабатывать проблемы из-за других пользователей, меняющих записи. Нет "лучшего" решения - все зависит от реальных потребностей. Для некоторых приложений (и некоторых баз данных) сохранение записи заблокированной до тех пор, пока они изменяются, нормально, для других-нет. Пакетные обновления могут быть в порядке в некоторых сценариях, а не в других. Вы должны выбрать модель, которая лучше всего подходит для вашего приложения и базы данных.