我不知道如何使用事务数据库实现用户友好界面的撤销属性。
一方面,建议用户具有多级(无限)撤销可能性,因为答案中说明here。可能对此问题有帮助的模式有Memento或Command。
但是,使用包含触发器,不断增长的序列号和不可逆转的过程的复杂数据库,很难想象撤消操作如何在不同于事务边界的点处起作用。 换句话说,撤消到最后一次提交的事务只是回滚时,但是如何才能回到不同的时刻呢?
UPDATE (基于目前为止的答案):我不一定希望撤消在修改已经提交时有效,我将专注于一个正在运行的应用程序,其中包含一个打开的事务。每当用户点击保存时,它意味着提交,但在保存之前 - 在同一事务期间 - 撤消应该有效。我知道使用数据库作为持久层只是一个实现细节,用户不应该为此烦恼。但是,如果我们认为“在数据库和GUI中撤消的想法是根本不同的东西”,并且我们不对数据库使用撤销,那么无限撤消只是一个流行语。 我知道“回滚是......不是用户撤消”。
那么如何在同一个事务中给出“由于任何更改而产生的级联效应”来实现客户端级撤销?
答案 0 :(得分:6)
在数据库和GUI中撤消的想法是根本不同的事情; GUI将成为单个用户应用程序,与其他组件的交互水平较低;数据库是一个多用户应用程序,其中更改可能会因任何更改而产生级联效果。
要做的是允许用户尝试将之前的状态应用为新事务,这可能会也可能不会起作用;或者只是在提交后没有撤消(类似于保存后没有撤消,这是许多应用程序中的一个选项)。
答案 1 :(得分: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;
在上面的例子中,只剩下第一个插入,另外两个插入将被撤消。
我认为在提交后尝试撤销一般都不实际,原因是你给了*和其他人。如果它在某些情况下是必不可少的,那么你将需要构建大量代码来实现它,并考虑触发器等的影响。
答案 2 :(得分:2)
我们通过跟踪应用于数据的所有交易(不是所有交易,只是不到3个月的交易)在我们的数据库中开发了这种可能性。基本的想法是能够看到谁做了什么,何时做了什么。然后,可以将每个数据库记录(由其GUID唯一标识)视为一个INSERT,多个UPDATE语句以及最后一个DELETE语句的结果。当我们记录所有这些SQL语句时,并且由于INSERT是全局INSERTS(所有字段值的跟踪保存在INSERT语句中),因此可以:
使用以下规则“撤消”之前的所有交易:
'INSERT'撤消是基于唯一标识符
的'DELETE''UPDATE'撤消相当于之前的'UPDATE'
'DELETE'撤消与第一个INSERT相同,然后是所有更新
因为我们没有跟踪 超过3个月的交易, UNDO并非总是可用。
对这些功能的访问仅限于数据库管理员,因为不允许其他用户在业务规则之外进行任何数据更新(例如:一旦采购订单行上的'撤消'是什么意思采购订单已经供应商同意了?)。说实话,我们很少使用这个选项(一年几次?)
答案 3 :(得分:2)
它几乎和William的帖子一样(我实际投了票),但我试着指出更详细一点,为什么有必要实现用户撤销(与使用数据库回滚相比)。
了解有关您的应用程序的更多信息会很有帮助,但我认为对于用户(友好)撤消/重做数据库并不是实现该功能的适当层。
从我的角度来看,数据库是实现细节,一种工具,用作存储数据的程序员。回滚是一种撤消,可以帮助您这样做,它不是用户撤消。使用回滚意味着让用户参与他不想知道但不理解(并且不必理解)的事情,这绝不是一个好主意。
正如William发布的那样,您需要在客户端或服务器端内部实现作为会话的一部分,该会话跟踪您定义为用户事务的步骤并能够撤消这些步骤。如果在这些用户事务期间进行了数据库事务,则需要其他数据库事务来撤消这些事务(如果可能)。如果无法撤消,请确保提供有价值的反馈,这再次意味着,在业务方面解释它而非数据库方式。
答案 4 :(得分:1)
要保持任意回滚到先前的语义,您需要在数据库中实现逻辑删除。其工作原理如下:
每条记录都有一个'已删除'标志,版本号和/或“当前指标”标志,具体取决于您需要重建的聪明程度。此外,您需要跨该实体的所有版本使用每个实体密钥,以便您知道哪些记录实际引用了哪个特定实体。如果您需要知道版本适用的时间,您还可以使用“发件人”和“收件人”列。
删除记录时,将其标记为“已删除”。更改它时,您将创建一个新行并更新旧行以反映其过时。使用版本号,您可以找到要回滚的前一个号码。
如果您需要参照完整性(您可能会这样做,即使您认为不这样做)并且可以处理额外的I / O,您还应该有一个父表,其中密钥作为所有记录的占位符版本。
在Oracle上,群集表对此非常有用;父表和版本表都可以共存,从而最大限度地减少I / O的开销。在SQL Server上,包含密钥的覆盖索引(可能集群在实体密钥上)将减少额外的I / O.