【问题标题】:Undo in a transactional database在事务数据库中撤消
【发布时间】:2010-10-01 02:40:09
【问题描述】:

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

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

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

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

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

【问题讨论】:

    标签: undo interface-implementation transactional-database


    【解决方案1】:

    要保持任意回滚到以前的语义,您需要在数据库中实现逻辑删除。其工作原理如下:

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

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

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

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

    【讨论】:

      【解决方案2】:

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

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

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

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

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

      【讨论】:

      • 我知道作为持久存储的数据库是一个实现细节。这就是我试图通过实施撤消向用户隐藏的内容,而不仅仅是提供通过保存提交和通过取消回滚的可能性。但是我可以撤消通过触发器调用存储过程的更新吗?
      【解决方案3】:

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

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

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

        “UPDATE”撤消等同于之前的“UPDATE”

        “删除”撤消等同于第一个 INSERT,然后是所有更新

      • 因为我们不跟踪 超过 3 个月的交易, UNDO 并非总是可用。

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

      【讨论】:

      【解决方案4】:

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

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

      【讨论】:

      • 通常我不想在提交之前返回。我的主要观点是回到最后一次提交之后。从那时起,可能会发生很多数据库操作。很难一一撤消。
      • 您可以允许人们将表单重置为以前的状态,然后尝试将其应用为新的交易;尝试在数据库中实现撤消是非常困难的,而且几乎可以肯定付出的努力超过了它的价值 - 例如,您不知道其他用户可能对数据做了什么
      • 如果一个用户在同一笔交易中有多个打开的表单怎么办?回滚并开始新事务也会损害其他形式。
      • "如果一个用户在同一个交易中有多个打开的表单怎么办?" - 不要让这种情况出现:如果表单应该是独立的,那么它们需要自己的数据库会话。
      • 我没有说他们是独立的。如果存在依赖关系,那么它们需要采用相同的形式(例如客户、订单)?
      【解决方案5】:

      一些(全部?)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;
      

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

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

      • 但我认为不断增加的序列没有任何问题?

      【讨论】:

      • 根据事务隔离级别和客户端之间共享的数据量,这可能会导致一些非常讨厌的锁定问题。
      • Jesse,您的意思是回滚到保存点会导致严重的锁定问题吗?我不知道怎么做,但后来我使用了 Oracle。
      猜你喜欢
      • 2013-10-24
      • 1970-01-01
      • 2011-03-18
      • 1970-01-01
      • 1970-01-01
      • 2012-01-04
      • 2010-09-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多