Предпочтительный способ написания приложений базы данных Delphi с компонентами транзакций и данных - PullRequest
9 голосов
/ 30 сентября 2010

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

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

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

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

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

Спасибо.

РЕДАКТИРОВАТЬ

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

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

  • Connection.StartTransaction
  • Master.Append, затем Master.Post, затем Master.Edit (поэтому набор основных данных имеет первичный автоинкрементключ, и теперь он доступен для редактирования)
  • Показать форму редактирования модально, в которой пользователь заполняет основные записи, а также добавить некоторые подробные записи, используя другую форму.
  • Если пользователь нажимает кнопку ОК, приложение будет делать Master.Post и Connection.Commit.Если пользователь нажмет «Отмена», приложение выполнит Connection.Rollback.

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

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

РЕДАКТИРОВАТЬ 2

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

Ответы [ 5 ]

5 голосов
/ 30 сентября 2010

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

В набросанном сценарии я использую следующие компоненты в цепочке:

TADOConnection >> TADODataSet >> TDataSetProvider >> TClientDataSet >> TDataSource >> TDBEdits и т. Д.

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

4 голосов
/ 01 октября 2010

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

Для ADO вам нужно установить LockType вашего набора данных на ltBatchOptimistic . Для UniDAC необходимо установить для свойства CacheUpdate значение True .

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

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

Это сделает ваши транзакции короткими, не нуждаясь в дополнительном слое ClientDataset.

Привет

2 голосов
/ 01 октября 2010

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

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

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

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

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

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

Мои 2 цента.

1 голос
/ 01 октября 2010

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

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

Поэтому, если вы не хотите использовать промежуточный уровень и все еще использовать компоненты, учитывающие данные, с короткими транзакциями write , вам следует подумать о базе данных, которая поддерживает получение автоинкрементного значения без выполнения INSERT (длямастер стол).Примером является Firebird база данных, и компоненты доступа к данным FibPlus для Firebird полностью поддерживают эту функцию.

0 голосов
/ 01 октября 2010

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

...