用事务编写Delphi数据库应用程序的优选方法&数据感知组件

时间:2010-09-30 18:01:21

标签: database delphi transactions delphi-7 data-aware

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

我必须编写一个访问InnoDB表的客户端应用程序,并在事务中执行一些主要的详细信息。在对交易进行一些研究之后(从一般的观点来看),我谦虚地得出结论,非数据感知组件和手工编码的SQL将是交易的“完美匹配”;但数据感知组件不会。它们似乎并不是为彼此而制造的。

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

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

BTW,我正在使用Delphi 7,目前正在评估UniDAC作为数据访问库。

谢谢。

修改

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

想象一下有一个包含2个DBGrids的表单。第一个网格是MasterGrid,上面是这些按钮:Add,Edit&删除。第二个网格是DetailGrid。如果用户单击“添加”,则它将如下所示:

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

我知道交易应该尽可能短,但你可以看到上面的交易只是填写表格的用户的速度。

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

编辑2

我感谢你们所有人的亲切参与。我从vcldeveloper中选择答案,因为它是我目前需要的最接近的解决方案。

5 个答案:

答案 0 :(得分:5)

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

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

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

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

答案 1 :(得分:4)

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

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

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

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

这样可以简化您的交易,而无需额外的ClientDataset层。

此致

答案 2 :(得分:2)

为了避免执行大型交易,我使用 DataSetProviders ClientDatasets (甚至是本地)。

考虑将其用作一种缓存,它可以为您提供两全其美的优势。在UI上工作时,您可以使用数据感知控件来简化操作。客户端对数据集的操作由ClientDataSets(数据库缓存类型)“记录”。

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

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

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

您必须阅读有关TClientDataSet以及如何处理OnReconcileError并在将其放入生产环境之前对其进行试验的信息,但它对我来说非常非常有效。

我的2美分。

答案 3 :(得分:1)

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

正如已经回答的那样,一般解决方案是使用中间层(ClientDataSet)。但是您的场景的真正问题在于,如果没有Master.Append和Master.Post,您无法获得Master表的自动增量值,因此您在实际需要之前很久就会启动 write 事务。< / p>

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

答案 4 :(得分:0)

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