2012-04-06 75 views
22

我知道至少有两个字节码增强器在运行时修改“对象模型”以允许事务透明地执行。其中之一是Versant VOD的一部分,我每天都在工作中使用,另一部分是兵马俑的一部分。例如在ORM中可能有不少其他人,但Versant会在我的公司处理此事。有没有对象更改跟踪/版本控制的Java API?

我的问题是,是否有这样一个开源的API,可以独立使用,独立于它设计的产品?你可以说一个“hackable”的API。它应该只跟踪变化,而不是读取访问,这将显着减慢代码。换句话说,它不应该要求明确的读/写锁定。这需要访问执行更改的所有类,而不仅仅是访问数据模型,或者需要在内存中保留某种形式的“先前版本”以进行比较。

我试图解决的问题是,我有“大”(32K到256K)的“NoSQL”数据库中“序列化”的对象图。它们是长寿的,必须定期重新序列化,以便有变化的“历史”。但是,它们的序列化相当昂贵,大部分更改都很小。

我可以每次完全序列化它们并在流上运行二进制比较,但这听起来非常耗费CPU资源。一个更好的解决方案是修改模型上的写操作以协调更改,以便在初始“映像”存储后,只需要存储协议。

我发现了一些关于Apache Commons Beanutils来比较对象的问题,但这对于就地更改没有用;我需要在每个“业务交易”之间对模型进行完整的克隆。

重申,我正在寻找一个“内存中”的API,在同一个JVM中,它不涉及任何外部服务器应用程序。如果可用于Win,Mac & Linux,则涉及本机代码的API可以。该API不必独立地打包;只需从“父项目”中提取它就可以形成独立的API(父项目许可证必须允许这样做)。

我的对象图将涉及许多大型数组,因此需要得到有效支持。

这些更改不仅仅适用于审核,而且可以重播或撤销。更确切地说,通过反序列化的初始图和一系列更改,我应该得到一个完全相同的最终图。另外,从结束图开始,应该可以通过反向应用更改返回到初始图。这使用完全相同的功能,但需要更改协议以保留旧值和新值。

API许可证应与商业用途兼容。

[编辑]到目前为止,我没有得到有用的的答案,它似乎并不像我想要的那样存在。这让我只有一个选择:做到这一点。当我有一个工作实现时,我会在这里发布一个链接作为答案,因为这是我项目中的下一步,如果没有它,我不能前进。

[编辑]我偶然发现这个有点相关的问题:Is there a Java library that can "diff" two Objects?

+0

不完全确定你在做什么。但它的声音类似于颠覆。如果这些对象被序列化为一个文件,那被检入到SVN中,那么更改历史将会在那里。如果您是通过编程访问更改,则不是非常有用。你需要什么? – scorpdaddy 2012-05-03 20:49:25

+0

哈哈!我明白你的意思,但这根本不适合我的用例。首先,我应该都是“内存中”的,而不是基于客户端服务器的,其次,所有RCS在二进制数据上本质上都是不好的,这正是我试图编写的版本。这里没有涉及XML或JSON。我会更新这个问题。 – 2012-05-04 10:37:14

+0

我不知道任何这样的API,但也许你可以按顺序思考。听起来,它实际上是您的应用程序的要求,而不是尝试透明API方法。您可以将初始图形存储一次,然后使用版本和新数据构建一棵树。 – 2012-05-04 11:28:42

回答

8

Kryo v1有一个串行器,它知道序列化的最后一个数据,并且只发出一个增量。阅读时,它知道收到的最后一个数据 并应用增量。增量在字节级完成。 Here是 串行器。大部分工作由this class完成。这可以用于一些有用的方式,例如类似于Quake 3的网络。

这在Kryo v2中被省略了,因为AFAIK它从未被投入使用。另外,它没有一套广泛的测试。它可以通过 移植,并可以做你需要的,或作为你需要的基础。

上面还贴出了JVM串行器mailing list

在对象层面做它会有点棘手。你可以写一些类似于FieldSerializer的东西,它可以同时走两个对象图。虽然这不是一个Kryo序列化器,但它是独立的代码。在每个级别,你可以称之为等于。写一个字节,以便当你阅读时,你知道它是否等于。如果不等于,请使用Kryo写入对象。对于同一个对象,等于多次被调用,特别是对于深度嵌套的对象。

另一种可能的方法是仅对标量和字符串进行上述操作,即仅输出由Output类写入的值。问题是走两个对象图。要使用Kryo,我认为你必须复制所有的序列化程序才能知道另一个对象图。

可能你可以使用Kryo和你自己的Output来收集列表中的值而不是写它们。用它来“序列化”你的旧对象图。现在编写你自己的Output的另一个版本,它接受这个列表并使用它来序列化你的新对象图。每写入一个值,首先检查列表中的下一个对象。如果等于,写一个1.如果不等于,写一个0,然后写入一个值。

通过使用第一个输出两次,一次使用旧的和一次使用新的图形,可以实现更高的空间利用率。现在你有两个值列表。使用这些来写一个表示哪些是相等的位串。这节省了为每个值写入整个字节的空间,但是具有额外列表的开销。最后,写出所有不相等的值。

要完成这个想法,您需要能够反序列化数据。你需要一个你自己的Input类的版本,它接收旧对象图中的值列表。您的输入首先读取位串(或每个值的一个字节)。对于相同的值,它将返回列表中的值而不是从数据中读取值。如果值不相等,它会调用超级方法从数据中读取数据。

我不确定这是否会比在字节级别做更快。如果我不得不猜测我会说这可能会更快。将所有值存储在列表中将会有很多装箱/拆箱,并且即使这些方法没有改变,该方法仍会分配所有字段。我怀疑表现会成为一个问题,所以我可能会选择更简单的方法。很难说这是什么东西...重新生成三角洲的东西或编写自己的输出/输入类。

如果你想回馈给Kryo,那当然会很棒。 :)

+0

这可能是我最好的选择。 :)但是请在这里写一些关于它的东西,所以SO读者不必遵循链接。 – 2012-05-11 08:12:24

+0

只看了一眼Kryo。由于缺乏长期存储设备(模式演变),我以前曾经解雇过它,但V2似乎解决了这个问题。 :) – 2012-05-11 10:59:16

+0

更新了一些更多的信息。 TaggedFieldSerializer是从FieldSerializer架构演进式起步的下一步。之后有CompatibleFieldSerializer。 – NateS 2012-05-11 20:27:56

1

我不知道这样的API,但它不能是复杂的:

一个更好的解决办法是修改API在模型上写入操作来协议更改,以便在初始“映像”被存储后,只需要存储协议。

我要说,你只需要两部分组成:行动与ActionProcessor

你只需要坚持执行的操作的列表(协议)。

interface ActionProcessor{ 
    void perform(Action action); 
    void undoToDate(Date date); 
} 

iterface Action{ 
    Date getDate(); 
    void perform(); 
    void undo(); 
}  
+2

这将是http://prevayler.org/方法。虽然它可以工作,但手动执行每个动作的撤销函数所需的代码量是巨大的,这会导致许多错误,从而导致无法正确撤销。如果一切都失败了,那将是最后的解决方案。其次,一旦你提出了一个新版本的代码,你的Action impl会改变,导致你失去可重复性和“可撤销性”,因为你会记录Action ID及其参数,而不是真正的改变。 – 2012-05-10 11:36:32

1

据我所知,是的GemFire宝石(现在在VmWare)企业产品做类似宝石Smalltalk的面向对象数据库的东西,但随后为Java。詹姆斯福斯特创建了一个关于宝石如何工作的series of videos。我发现他们非常有趣。宝石有一个免费版本来搭建小型(海边网络)系统。