自制与Java序列化

时间:2008-10-28 19:22:08

标签: java database serialization

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

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

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

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

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

14 个答案:

答案 0 :(得分:12)

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

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

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

答案 1 :(得分:12)

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

答案 2 :(得分:4)

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

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

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

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

答案 3 :(得分:3)

考虑将数据放在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将每个字段转换为字符串表示形式。

答案 4 :(得分:3)

我认为

XStreamYAMLOGNL是简单的序列化技术。 XML是最常见的,但OGNL使用最少量的元数据提供了最大的灵活性。

答案 5 :(得分:2)

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

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

答案 6 :(得分:2)

标准JavaBeans持久性机制如何:

java.beans.XMLEncoder
java.beans.XMLDecoder

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

<object class="java.util.HashMap">
    <void method="put">
        <string>Hello</string>
        <float>1</float>
    </void>
</object>

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

答案 7 :(得分:1)

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

tutorial here

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

答案 8 :(得分:1)

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

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

答案 9 :(得分:0)

  

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

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

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

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

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

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

答案 10 :(得分:0)

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

答案 11 :(得分:0)

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

答案 12 :(得分:0)

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

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

答案 13 :(得分:0)

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