自制与Java序列化

我有一个需要持久保存在数据库上的POJO,当前设计将其字段指定为单个字符串列,并且不向表中添加其他字段。

意思是,对象需要以某种方式序列化。 所以只是为了基本的实现,我去设计了我自己的对象的序列化forms,这意味着将它的所有字段连接成一个很好的字符串,用我选择的分隔符分隔。 但这是相当丑陋的,并且可能会导致问题,比如说其中一个字段包含我的分隔符。

所以我尝试了基本的Java序列化,但是从我进行的一个基本测试来看,这在某种程度上变成了一个非常昂贵的操作(构建一个ByteArrayOutputStream,一个ObjectOutputStream,依此类推,反序列化)。

那么我的选择是什么? 序列化对象进入数据库的首选方法是什么?

编辑:这将是我项目中非常常见的操作,因此必须将开销保持在最低限度,并且性能至关重要。 此外,第三方解决方案很好,但不相关(并且通常产生我试图避免的开销)

Elliot Rusty Harold写了一篇很好的论据反对在他的XOM库中使用Java Object序列化。 同样的原则适用于您。 内置的Java序列化是特定于Java的,易碎且缓慢的,因此最好避免使用。

您在使用基于字符串的格式时有大致正确的想法。 正如您所说,问题在于您遇到了分隔符的格式化/语法问题。 解决方案是使用已构建的格式来处理此问题。 如果这是标准化格式,那么您还可以使用其他库/语言来操作它。 此外,基于字符串的格式意味着您只需通过观察数据就可以理解它; 二进制格式删除该选项。

XML和JSON是两个很好的选择; 它们是标准化的,基于文本的,灵活的,可读的,并且有很多库支持。 它们的表现也非常出色(有时甚至比Java序列化更快 )。

您可以尝试使用Protocol Buffers ,这是一个来自Google的开源项目,据说它很快(生成比XML更短的序列化forms,并且工作更快)。 它还可以轻柔地处理新字段的添加(插入默认值)。

您需要考虑在解决方案中进行版本控制。 数据不兼容是您遇到涉及使用Object的二进制序列化的任何解决方案时遇到的问题。 如何将较旧的数据行加载到较新版本的对象中?

因此,上面涉及序列化为名称/值对的解决方案是您可能想要使用的方法。

一种解决方案是将版本号包括为字段值之一。 在添加,修改或删除新字段时,可以修改版本。

反序列化数据时,可以为每个版本使用不同的反序列化处理程序,这些处理程序可用于将数据从一个版本转换为另一个版本。

XStream或YAML或OGNL作为简单的序列化技术而浮现在脑海中。 XML是最常见的,但OGNL以最少的元数据提供最大的灵活性。

考虑将数据放在Properties对象中并使用其load()/store()序列化。 这是一种基于文本的技术,因此它在数据库中仍然可读:

 public String getFieldsAsString() { Properties data = new Properties(); data.setProperty( "foo", this.getFoo() ); data.setProperty( "bar", this.getBar() ); ... ByteArrayOutputStream out = new ByteArrayOutputStream(); data.store( out, "" ); return new String( out.toByteArray(), "8859-1" ); //store() always uses this encoding } 

要从字符串加载,请使用新的Properties对象执行类似操作并load()数据。

这比Java序列化更好,因为它非常易读且紧凑。

如果需要支持不同的数据类型(即不仅仅是String),请使用BeanUtils将每个字段转换为字符串表示forms。

如果你的POJO由字符串和原始类型组成,我会说你的初始方法并不是那么糟糕。 您可以强制转义分隔符以防止损坏。 此外,如果您使用Hibernate,则将序列化封装在自定义类型中 。

如果你不介意另一个依赖,那么Hessian应该是一种更有效的序列化Java对象的方法。

标准JavaBeans持久性机制如何:

 java.beans.XMLEncoder java.beans.XMLDecoder 

这些能够从XML创建Java POJO(已经持久化为XML)。 从记忆中看,它看起来像是……

   Hello 1   

您必须提供PersistenceDelegate类,以便它知道如何持久化用户定义的类。 假设您没有删除任何公共方法,它对模式更改具有弹性。

您可以通过外部化对象来优化序列化。 这将使您完全控制其序列化方式并提高流程性能。 这很简单,只要你的POJO很简单(即没有引用其他对象),否则你很容易破坏序列化。

这里的教程

编辑:并不意味着这是首选方法,但如果ti对性能至关重要且您只能在表中使用字符串列,那么您的选项非常有限。

如果您使用的是分隔符,则可以使用您知道在文本中不会出现的字符,例如\ 0或特殊符号http://unicode.org/charts/symbols.html

但是,将数据发送到数据库并将其持久化所花费的时间可能远远大于序列化的成本。 所以我建议从一些简单易读的东西开始(比如XStream)并查看你的应用程序花费大部分时间并优化它的地方。

我有一个需要持久保存在数据库上的POJO,当前设计将其字段指定为单个字符串列,并且不向表中添加其他字段。

你能创建一个新表并将一个外键放入该列中吗??!? :)我怀疑不是,但让我们覆盖所有基地!

序列化:我们最近进行了这样的讨论,以便如果我们的应用程序崩溃,我们可以恢复它与以前相同的状态。 我们基本上将一个持久性事件分派到队列中,然后抓取对象,锁定它,然后将其序列化。 这似乎很快。 您要序列化多少数据? 你可以使任何变量瞬态(即缓存变量)吗? 你能考虑拆分序列化吗? 注意:如果对象更改(锁定)或类更改(不同的序列化ID)会发生什么? 您需要升级序列化为最新类的所有内容。 也许你只需要在一夜之间保存,所以没关系?

XML:您可以使用xstream之类的东西来实现这一目标。 建立自定义是可行的(一个很好的面试问题!),但我可能不会自己做。 何必? 请记住,如果您有循环链接,或者您有多次参考对象。 重建对象并不是那么微不足道。

数据库存储:如果您使用Oracle 10g存储blob,请升级到最新版本,因为c / blob性能会大幅提升。 如果我们谈论大量数据,那么可能压缩输出流?

这是一个实时应用程序,还是会有第二个或两个暂停,您可以安全地持有实际对象? 如果你有时间,那么你可以克隆它,然后将克隆保存在另一个线程上。 持久性是什么? 在交易中完成它是否至关重要?

考虑更改您的架构。 即使您找到将POJO序列化为字符串的快速方法,您如何处理不同的版本? 如何从X-> Y迁移数据库? 或者更糟糕的是A-> D? 我发现我们将序列化对象存储到BLOB字段并且必须跨多个版本迁移客户的问题。

你看过JAXB了吗? 它是一种机制,您可以通过该机制定义从XML架构创建的一组Java对象。 它允许您从对象层次结构编组到XML,或者将XML解组回到对象层次结构中。

我将第二个建议使用JAXB,或者可能是XStream(前者更快,后者更关注对象序列化部分)。 另外,我还会建议使用基于JSON的替代方案Jackson( http://jackson.codehaus.org/Tutorial ),它可以将bean完全序列化/反序列化为JSON文本以存储在列中。

哦,我完全同意,在任何情况下都不要使用Java二进制序列化来进行长期数据存储。 协议缓冲区也是如此; 这两者都太脆弱了(它们更适合于耦合系统之间的数据传输)。

你可以试试Preon 。 Preon的目标是二进制编码数据,Hibernate对关系数据库和JAXB到XML。