3

我不知道如何使用事务数据库实现用户友好界面的撤消属性。

一方面,建议用户具有多级(无限)撤消的可能性,如答案中所述。可能有助于解决此问题的模式是MementoCommand

然而,使用包含触发器、不断增长的序列号和不可逆过程的复杂数据库,很难想象撤消操作如何在事务边界之外的不同点工作。换句话说,撤消到最后一次提交的事务只是一个回滚,但是怎么可能回到不同的时刻呢?

更新(基于到目前为止的答案):我不一定希望在修改已经提交时撤消工作,我会专注于具有开放事务的正在运行的应用程序。每当用户单击保存时,这意味着提交,但在保存之前 - 在同一事务期间 - 撤消应该起作用。我知道使用数据库作为持久层只是一个实现细节,用户不应该为此烦恼。但是,如果我们认为“数据库中的撤消和 GUI 中的撤消概念是根本不同的东西”并且我们不对数据库使用撤消,那么无限撤消只是一个流行词。我知道“回滚是......不是用户撤消”。

那么如何在同一个事务中实现“任何更改导致的级联效果”的客户端级撤消呢?

4

5 回答 5

6

在数据库和 GUI 中撤消的想法是根本不同的东西。GUI 将是一个单用户应用程序,与其他组件的交互水平较低;数据库是一个多用户应用程序,其中的更改可能会因任何更改而产生级联效应。

要做的事情是允许用户尝试将先前的状态应用为新事务,这可能会或可能不会起作用;或者只是在提交后没有撤消(类似于保存后没有撤消,这是许多应用程序中的一个选项)。

于 2009-01-13T10:28:45.137 回答
4

一些(全部?)DBMS 支持保存点,允许部分回滚:

savepoint s1;
insert into mytable (id) values (1);
savepoint s2;
insert into mytable (id) values (2);
savepoint s3;
insert into mytable (id) values (3);
rollback to s2;
commit;

在上面的示例中,只保留第一个插入,其他两个将被撤消。

我认为一般来说在提交后尝试撤消是不切实际的,因为你给出的原因*可能还有其他原因。如果在某些情况下是必不可少的,那么您将不得不构建大量代码来执行此操作,并考虑触发器等的影响。

  • 不过,我认为不断增加的序列没有任何问题吗?
于 2009-01-13T10:24:18.280 回答
2

这与 William 的帖子(我实际上投了赞成票)几乎相同,但我试图更详细地指出,为什么需要实现用户撤消(与使用数据库回滚相比)。

了解有关您的应用程序的更多信息会有所帮助,但我认为对于用户(友好的)撤消/重做数据库不是实现该功能的充分层。

  1. 用户想要撤消他所做的操作,与这些操作是否导致没有/一个/多个数据库事务无关
  2. 用户想要撤消他所做的操作(不是其他人的)

从我的角度来看,数据库是实现细节,是一种工具,您可以用作存储数据的程序员。回滚是一种可以帮助您执行此操作的撤消,它不是用户撤消。使用回滚意味着让用户参与他不想知道和不理解(也不必)的事情,这绝不是一个好主意。

正如威廉所发布的那样,您需要在客户端或服务器端实现作为会话的一部分,它跟踪您定义为用户事务的步骤并能够撤消这些步骤。如果在这些用户事务期间进行了数据库事务,则需要其他数据库事务来撤销这些事务(如果可能)。如果无法撤消,请确保提供有价值的反馈,这再次意味着,从业务角度而非数据库角度进行解释。

于 2009-01-13T11:17:59.763 回答
2

我们通过跟踪应用于数据的所有事务(不是所有事务,只是不到 3 个月大的事务)在我们的数据库中开发了这种可能性。基本的想法是能够看到谁在什么时候做了什么。每个由其 GUID 唯一标识的数据库记录可以被视为一个 INSERT、多个 UPDATE 语句和最后一个 DELETE 语句的结果。由于我们跟踪所有这些 SQL 语句,并且由于 INSERT 是全局 INSERTS(所有字段值的跟踪都保存在 INSERT 语句中),因此可以:

  • 知道谁修改了哪个字段以及何时修改:Paul 在形式发票中插入了新行,Bill 重新协商了商品的单价,Pat 修改了最终订购数量等)
  • 使用以下规则“撤消”所有先前的交易:

    “INSERT”撤消是基于唯一标识符的“DELETE”

    'UPDATE' 撤消等同于之前的'UPDATE'

    'DELETE' 撤消等同于第一个 INSERT,然后是所有更新

  • 由于我们不跟踪超过 3 个月的交易,UNDO 并非始终可用。

对这些功能的访问仅限于数据库管理员,因为不允许其他用户在业务规则之外进行任何数据更新(例如:采购订单行上的“撤消”的含义是什么?供应商是否同意?)。说实话,我们很少使用这个选项(一年几次?)

于 2009-01-13T10:35:10.960 回答
1

要维护任意回滚到以前的语义,您需要在数据库中实现逻辑删除。这工作如下:

每条记录都有一个“已删除”标志、版本号和/或“当前指标”标志,具体取决于您需要重建的聪明程度。此外,您需要跨该实体的所有版本的每个实体键,以便您知道哪些记录实际上引用了哪个特定实体。如果您需要知道某个版本适用的时间,您还可以使用“From”和“To”列。

删除记录时,将其标记为“已删除”。当您更改它时,您会创建一个新行并更新旧行以反映其过时情况。使用版本号,您可以找到要回滚到的先前版本。

如果您需要参照完整性(您可能会这样做,即使您认为不需要)并且可以处理额外的 I/O,您还应该有一个父表,其中键作为所有版本的记录的占位符。

在 Oracle 上,聚簇表对此很有用;父表和版本表都可以放在一起,从而最大限度地减少 I/O 的开销。在 SQL Server 上,包含键的覆盖索引(可能聚集在实体键上)将减少额外的 I/O。

于 2009-01-13T11:23:43.903 回答