Java 如何保存关系数据库中大字符串字段的编辑历史

Java 如何保存关系数据库中大字符串字段的编辑历史,java,database,hibernate,postgresql,version,Java,Database,Hibernate,Postgresql,Version,注意:我认为答案很可能是以设计为中心的,因此基本上与实现无关,但如果有一些特别适合使用这些技术的解决方案,我将在Postgres中使用Java+Hibernate 我有一个表,其中有一个特定的字段,可以容纳大字符串,比如说博客文章,平均长度为+10000个字符 在我的应用程序中,你可以任意多次编辑博客文章,更新后最新版本将始终立即显示。但是,应用程序需要保留这些编辑的完整版本历史记录,以便可以查看 一个明显的策略是保留一个单独的表,比如blog\u post\u history,其中blog p

注意:我认为答案很可能是以设计为中心的,因此基本上与实现无关,但如果有一些特别适合使用这些技术的解决方案,我将在Postgres中使用Java+Hibernate

我有一个表,其中有一个特定的字段,可以容纳大字符串,比如说博客文章,平均长度为+10000个字符

在我的应用程序中,你可以任意多次编辑博客文章,更新后最新版本将始终立即显示。但是,应用程序需要保留这些编辑的完整版本历史记录,以便可以查看

一个明显的策略是保留一个单独的表,比如
blog\u post\u history
,其中blog post行在创建时和对主“live”
blog\u post
表的每次后续更新时都会重复插入,版本号会递增,因此,如果将来需要,这些版本都可以使用。我正在考虑使用Hibernate Envers之类的工具来设置它

然而,存储(可能更重要的是传输)10000字符文本块的多个版本似乎效率非常低,其中每个版本之间的唯一区别可能是修复打字错误、添加一些单词等。由于博客帖子编辑的性质,可能会有许多类似的小增量更改,而不是更少,更大的变化

有更好的方法吗?

我在想,在进行编辑时,只存储当前版本和以前版本之间的增量,然后在请求时通过编程从这些增量重建版本历史,可能是在客户机上,这样通过线路发送的数据就最小化了


我很可能会将最新版本存储为全文,因为我希望优化最频繁的请求,然后存储一系列从当前版本向后的增量,以便在请求时重建历史版本。

我现在正在研究的解决方案,到目前为止效果良好,实现我在问题中提出的设计

我在想,在进行编辑时,只存储当前版本和以前版本之间的增量,然后在请求时通过编程从这些增量重建版本历史,可能是在客户机上,这样通过线路发送的数据就最小化了

我很可能会将最新版本存储为全文,因为我希望优化最频繁的请求,然后存储一系列从当前版本向后的增量,以便在请求时重建历史版本

我将在这里分享我的实施细节

为了创建三角洲和使用重建全文,我使用了奇妙的。您可以阅读实现不可知,以更好地理解下面的代码示例,尽管它还是非常可读的

google diff match补丁有Java和JS实现,因此我可以使用它在服务器上用Java计算增量。我选择将每个增量转换为字符串,这样就可以轻松地将其存储在数据库中,并方便客户端上的JS库使用。下面将对此进行详细介绍

public String getBackwardsDelta(String editedBlogPost, String existingBlogPost) {
    diff_match_patch dmp = new diff_match_patch();
    LinkedList<diff_match_patch.Patch> patches = 
        dmp.patch_make(editedBlogPost, existingBlogPost);
    return dmp.patch_toText(patches);
}
对于前端,我传递最新的博客文章全文,以及一系列表示每次编辑的时间倒转增量,然后在JS浏览器中重建每个版本的全文

要获取库,我使用npm+browserify。作为差异匹配补丁。版本1.0.0是唯一的版本

getTextFromDelta: function(originalText, delta) {
  var DMP = require('diff-match-patch'); // get the constructor function
  var dmp = new DMP();
  var patches = dmp.patch_fromText(delta);
  return dmp.patch_apply(patches, originalText)[0];
}
就这样,它工作得非常好

在存储博客文章的编辑方面,我只使用一个表
blog_POST_edits
,其中我存储了博客文章id,一个编辑时间戳(稍后在客户端重建全文版本时,我使用它来正确排序编辑以生成链),以及
blog_post
表中当前实时博客文章与博客文章的传入编辑版本之间的向后增量

我选择存储一个delta“链”,因为它非常适合我的用例,并且在服务器代码端更简单。这确实意味着为了重建N的M版本,我必须向客户端发送一个由N-(M-1)个delta组成的链,从live blog帖子全文返回到M版本。但是在我的用例中,我每次都要发送整个链,无论如何,这很好


为了使请求特定版本的效率稍好一些,每次编辑时,所有增量都可以从新编辑的博客文章版本重新计算回每个(还原的)版本,但这将意味着服务器上需要更多的工作和复杂性。

我不会回答存储差异或完整更改的问题,即使在我看来,性能测试实际上可以回答什么解决方案更好,因为完整的内容日志意味着更大的数据库,但服务器的工作量更少

相反,我想分享我在postgresql中保存历史的经验。 我在服务器站点上非常成功地完成了这项工作,仅在postgresql上完成,没有编写任何代码。 在Postgresql上使用这组函数、触发器和扩展

它们简单且易于实现,您可以忘记代码中的历史记录,但您只需从日志表中读取,就可以显示内容上的差异

因此,我的应用程序是用php和YII框架编写的,YII框架具有我为数据设计的db方案和结构,只有很少的表作为框架本身的服务(用户、角色和一般日志),这一点很重要,因为如果db中的数据结构太复杂,下面总结的方法仍然有效,但更复杂

安装了postgresql扩展表日志后,您可以在此处找到它

你可以这样做: 首先,必须选择包含所需内容的表(mytable)
getTextFromDelta: function(originalText, delta) {
  var DMP = require('diff-match-patch'); // get the constructor function
  var dmp = new DMP();
  var patches = dmp.patch_fromText(delta);
  return dmp.patch_apply(patches, originalText)[0];
}
CREATE FUNCTION table_log ()
    RETURNS TRIGGER
    AS '$libdir/table_log' LANGUAGE 'C';
CREATE FUNCTION "table_log_restore_table" (VARCHAR, VARCHAR, CHAR, CHAR, CHAR, TIMESTAMPTZ, CHAR, INT, INT)
    RETURNS VARCHAR
    AS '$libdir/table_log', 'table_log_restore_table' LANGUAGE 'C';
CREATE FUNCTION "table_log_restore_table" (VARCHAR, VARCHAR, CHAR, CHAR, CHAR, TIMESTAMPTZ, CHAR, INT)
    RETURNS VARCHAR
    AS '$libdir/table_log', 'table_log_restore_table' LANGUAGE 'C';
CREATE FUNCTION "table_log_restore_table" (VARCHAR, VARCHAR, CHAR, CHAR, CHAR, TIMESTAMPTZ, CHAR)
    RETURNS VARCHAR
    AS '$libdir/table_log', 'table_log_restore_table' LANGUAGE 'C';
CREATE FUNCTION "table_log_restore_table" (VARCHAR, VARCHAR, CHAR, CHAR, CHAR, TIMESTAMPTZ)
    RETURNS VARCHAR
    AS '$libdir/table_log', 'table_log_restore_table' LANGUAGE 'C';