在Delphi中是否有一种方法可以缓存主从关系的行,并同时提交主表和子表行?

8
我想在内存中添加一些子行,然后根据父行是否已发布或未发布的情况,在底层SQL数据库中有选择地发布或不发布它们。我不需要完整的ORM,只需要以下内容:
  1. 用户点击“添加医生”按钮,打开“添加医生”对话框。
  2. 在“添加医生”对话框中单击“确定”之前,用户可以添加一个或多个仅保存在内存中的患者。
  3. 用户单击“添加医生”窗口中的“确定”按钮。现在所有患者都已存储,同时还有新医生。
  4. 如果用户在医生窗口上单击“取消”,则所有医生和患者信息都将被丢弃。
尝试想象如何使用Delphi数据感知控件以及TADOQuery或其他ADO对象完成上述操作。如果有一种非ADO特定的方法来做到这一点,那我也感兴趣,我只是提到ADO,因为我目前的应用程序中正在使用MS-SQL Server和ADO。
在我曾经工作过的一个雇主那里,他们有一个名为TMasterDetail的类,专门用于向ADO记录集添加上述内容。它有时有效,但有时会出现一些非常有趣且难以修复的错误。
VCL中是否有任何内置的组件或第三方组件,可以具有执行此技术的强大扩展方式?如果没有,那么我上面所说的需要ORM吗?我认为ORM被很多人认为是“不好”的,但上述内容是一个在无数应用程序中可能发生的自然UI模式。如果我正在使用非ADO非Delphi-db-dataset样式的工作方式,则上述内容几乎不会成为任何持久性层中的问题,但当包含使用标识值链接主和详细信息行的主键的数据库出现时,事情就变得复杂了。
更新:在这种情况下,事务并不理想。(提交/回滚对于我的目的而言太粗糙了。)

我立刻想到的两个选项是:1)使用事务在取消时回滚,或者2)使用带有提供程序的嵌套客户端数据集。 - Ondrej Kelle
想知道为什么事务无法正常工作? - John Easley
我想事务和回滚是一个合理的想法,尽管它可能会变得非常复杂。 - Warren P
1
一些第三方组件在其查询组件中直接实现了缓存更新,例如AnyDac。一旦输入被接受,您可以像使用TClientDataSet和TDataSetProvider一样应用ApplyUpdates。通过CancelUpdates,记录将被放弃; - crefird
那个最后的方法(在DAC层中使用缓存更新)对我来说似乎是最好的。用TClientDataSet自己编写两层持久性似乎对我来说太麻烦了。 - Warren P
显示剩余3条评论
1个回答

3

您有两个不同的问题:

  1. 如何缓存更新?
  2. 如何同时提交相关表的更新。

缓存更新可以通过多种方式实现,最佳方式取决于您的具体情况:

ADO批量更新

由于您已经声明正在使用ADO访问数据,因此这是一个合理的选择。您只需要在打开数据集之前将LockType设置为ltBatchOptimistic并将CursorType设置为ctKeySetctStatic。然后在准备提交时调用TADOCustomDataset.UpdateBatch。

注意:底层OLEDB提供程序必须支持批量更新才能利用此功能。SQL Server提供程序完全支持此功能。

我不知道在持久化数据时强制执行主/细节关系的其他方法,除了在两个数据集上顺序调用UpdateBatch。

Parent.UpdateBatch;
Child.UpdateBatch;

客户端数据集

数据缓存是TClientDataset存在的主要原因之一,同步主/从关系并不困难。

为了实现这一点,您像往常一样在两个数据集组件上定义主/从关系(在您的情况下是ADOQueryADOTable)。然后创建一个单一的提供程序并将其连接到数据集。将一个单一的TClientDataset连接到提供程序,就完成了。 TClientDatset将详细数据集解释为嵌套数据集字段,可以像任何其他数据集一样访问和绑定到数据感知控件。

一旦完成此操作,只需调用TClientDataset.ApplyUpdates,客户端数据集将负责正确排序主/从数据的更新。

ORMs

关于ORMs有很多可以说的内容。太多了,无法放入StackOverflow的答案中,所以我会尽量简短。

最近ORMs受到了一些批评,有些评论家甚至认为它们是反模式。个人认为这有点不公平。对象关系映射是一个非常难以正确解决的问题。ORMs试图通过抽象化在关系表和对象实例之间传输数据的复杂性来帮助解决这个问题。但像软件开发中的其他一切一样,没有银弹,ORMs也不例外。

对于一个没有太多业务规则的简单数据输入应用程序,ORM可能过度了。但随着应用程序变得越来越复杂,ORM开始变得更加吸引人。

在大多数情况下,您将希望使用第三方ORM而不是自己编写。编写一个完全符合您要求的自定义ORM听起来很不错,而且从简单映射开始很容易入手,但很快就会遇到像父/子关系、继承、缓存和缓存失效等问题(相信我,我有经验)。第三方ORM已经遇到了这些问题,并花费了大量资源来解决它们。

对于许多ORM来说,您交换了代码复杂性以获得配置复杂性。它们中的大多数正在积极努力通过转向惯例和策略来减少样板配置。如果您将所有主键命名为Id,而不是为每个类映射每个表的Id列到相应的Id属性,您只需告诉ORM有关此约定,它就会假定它了解的所有表和类都遵循这个约定。您只需要针对不适用该约定的特定情况覆盖约定。我不熟悉Delphi中的所有ORM,因此无法说哪些支持这一点,哪些不支持。

无论如何,您都希望设计应用程序架构,以便尽可能地推迟使用哪个ORM框架(或任何框架)的决定。


将 LockType 和 CursorType 适当地设置为哪些值? - NGLN

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接