9

使用事务和数据感知组件编写 Delphi 数据库应用程序的最佳方法是什么?

我必须编写一个访问 InnoDB 表的客户端应用程序,并在事务中做一些主从类型的事情。在对事务做了一些研究(从一般的角度)之后,我谦虚地得出结论,非数据感知组件和手工编码的 SQL 将是事务的“完美匹配”;但数据感知组件不会。他们似乎不是为彼此而生的。

我确实需要使用事务,但另一方面,我不能仅仅抛弃数据感知组件,因为它们极大地简化了事情。

有人可以启发我吗?我一直在谷歌搜索它,但我没有找到任何有用的答案。也许是因为我的英语不够好,所以我的关键词有限。

顺便说一句,我正在使用 Delphi 7,目前正在评估 UniDAC 作为数据访问库。

谢谢你。

编辑

描述我的问题的一个方面的示例:

想象一个上面有 2 个 DBGrid 的表单。第一个网格是 MasterGrid,上面是这些按钮:添加、编辑和删除。第二个网格是DetailGrid。如果用户单击添加,那么它会像这样:

  • Connection.StartTransaction
  • Master.Append 然后 Master.Post 然后 Master.Edit (因此主数据集具有自动增量主键,现在可以编辑)
  • 以模态方式显示编辑表单,用户在其中填写主记录,并使用另一个表单添加一些详细记录。
  • 如果用户单击确定,应用程序将执行 Master.Post 和 Connection.Commit。如果用户单击取消,则应用程序将执行 Connection.Rollback。

我知道事务应该尽可能短,但是您可以在上面看到事务仅与用户填写表单的速度一样短。

如果我使用的是非数据感知组件,我会根据用户输入自定义插入 SQL,然后在 StartTransaction 和 Commit 之间执行 SQL。所以我可以实现非常短的交易。

编辑 2

我感谢大家的友好参与。我从 vcldeveloper 中选择答案,因为它是最接近我当前需求的解决方案。

4

5 回答 5

5

我理解你的问题,我想。当打开一个 TADODataSet 时,例如要在表单上编辑 10 行数据,使用数据感知组件,在某些情况下,您可能希望缓存对所有 10 行所做的所有更改(可能还有删除和插入)并将其作为一批提交。您不能在第一次更改时打开事务,因为这会阻止其他用户更改相同的数据。交易应尽可能短。

我在草图场景中所做的是在链中使用以下组件:

TADOConnection >> TADODataSet >> TDataSetProvider >> TClientDataSet >> TDataSource >> TDBEdits 等。

现在所有更改都缓存在 TClientDataSet 中,您可以调用它的方法 ApplyUpdates 在一个快速事务中发布所有更改。请注意,也可以将多个 TADODataSets 和多个 TClientDataSets 用于具有嵌套数据集的 master-detail(-detail-etc) 结构。所有主从更改也可以在一个事务中批量缓存和应用。有关实现此功能的所有详细信息,请参阅其他地方的帮助和资源。一开始并不容易。但如果你想通了,它就很容易,并且提供了大量的可能性。(离线编辑,在应用更改之前检查更改等)

于 2010-09-30T18:58:03.667 回答
4

其他人提到使用 DatasetProvider 和 ClientDataset 的组合进行批量更新,但是如果使用 ADO 或 UniDAC 组件,则不需要额外的 DatasetProvider + ClientDataset 层,因为 ADO 和 UniDAC 都支持批量更新。

对于ADO,您应该将数据集的LockType设置为ltBatchOptimistic。对于UniDAC,您应该将CacheUpdate属性设置为True

此更改使您的数据集缓存您对其内存记录集所做的所有更改,并仅在您调用UpdateBatch方法 (ADO) 或ApplyUpdates方法 (UniDAC) 时将它们一起发送到数据库。

现在您应该做的是让您的用户使用您喜欢的任何数据感知组件在主数据集中插入/编辑记录以及他/她想要在详细数据集中的任何记录。所有更改都将被缓存。当您的用户完成后,您可以开始一个新事务,首先为主数据集调用 UpdateBatch(或在 UniDAC 的情况下为 ApplyUpdate),然后为详细数据集调用,如果一切正常,则提交该事务。

这将使您的交易更短,而不需要额外的 ClientDataset 层。

问候

于 2010-10-01T02:27:22.437 回答
3

为了避免需要执行大型事务,我使用DataSetProvidersClientDatasets(甚至在本地)。

考虑将其用作一种缓存,它可以为您提供两全其美的效果。在处理 UI 时,您可以使用数据感知控件来简化操作。ClientDataSets(一种数据库缓存)“记录”数据集上的用户操作。

当您的用户准备好将更改保存到数据库时(例如,发票数据已全部到位),您为数据集调用ApplyUpdates方法。

在最简单的场景中,所有数据集都处于主从关系(由提供者嵌套),提供者自己启动并提交/回滚事务,因此您自动处于全有或全无的情况。

如果您有更复杂的关系,您可以在开始为每个涉及的 ClientDataSet 集应用更新之前调用 StartTransaction,并在最后根据需要调用 Commit 或 Rollback)。提供者的逻辑是,如果在调用 ApplyUpdates 时连接具有活动事务,那么它对事务不做任何事情,而只是将更改发布到数据库,假设您控制事务。

在将其投入生产环境之前,您必须阅读 TClientDataSet 以及如何处理 OnReconcileError 并试验该技术,但它对我来说非常非常好。

我的 2 美分。

于 2010-09-30T21:52:55.643 回答
1

您绝对正确,事务应该尽可能短,并且在用户填写表单时它不应该一直处于活动状态。

如前所述,一般解决方案是使用中间层(ClientDataSet)。但是您的方案的真正问题是,如果没有 Master.Append 和 Master.Post,您无法获得 Master 表的自动增量值,因此您在实际需要之前很久就开始了写入事务。

因此,如果您不想使用中间层并且仍然使用具有短写入事务的数据感知组件,您应该考虑支持在不执行 INSERT(到主表)的情况下获取自动递增值的数据库。以Firebird数据库为例,Firebird 的FibPlus 数据访问组件完全支持该功能。

于 2010-10-01T00:38:45.700 回答
0

交易应尽可能. 问题是不同的数据库如何处理锁定。只执行行级锁定并且可以立即从锁定中返回而无需等待的数据库发生死锁的可能性要小得多。通常插入问题较少(尽管其他用户在提交之前不会看到新行,具体取决于隔离级别),更新和删除问题更大。过于频繁地提交也可能是“坏的”。缓存更改并在单个操作中应用它们是另一种可能性 - 但您必须处理由于其他用户同时更改记录而导致的问题。没有“更好”的解决方案——一切都取决于实际需求。对于某些应用程序(和某些数据库)来说,只要它们正在更改就保持记录锁定是可以的,而其他应用程序可能不会。批量更新在某些情况下可能没问题,而在其他情况下则不行。

于 2010-09-30T20:59:37.910 回答