0

我们正在序列化一些 Data 对象并将它们存储在数据库中,长期以来,数据库中有数百个这样的对象。最近我们需要改变其中一个类的结构。现在反序列化这些对象正在引发错误。

我们之前没有写自定义序列化和反序列化。这里的使用defaultSerialVersionUID也对我们没有帮助,因为它只是1L在序列化它们时的默认设置。

在底层的类结构改变后,有没有更好的方法来反序列化它们?

4

3 回答 3

3

我们正在序列化一些数据对象并将它们存储在数据库中......

那是/是一个大错误,IMO。您避免了必须构建和维护与数据对象相对应的表……但是 Java 对象序列化不是为这种事情而设计的,这种脆弱性是缺点之一。


@EJP 和 @sidoh 的答案提供了一些想法,可以让您摆脱当前的困境,前提是您可以掌握旧版本的课程。有几种方法:

  • 回滚到破坏兼容性的更改之前的代码/模式状态......并以避免错误的方式重做工作。

  • 不要回滚,而是编写一些临时转换器,将当前可读的对象加载为可读形式并更新存储的版本。

前者可能不切实际;例如,如果其他内容发生了太多变化,或者您的代码/数据正在生产中。

后者涉及从版本控制中检索类的旧版本和新版本,并构建一个临时转换器,该转换器加载旧类,转换为新类的实例并保存。这样做的困难在于构建一个可以在同一个 JVM 中同时使用相同类的两个不同版本的 Java 应用程序:

  • 您可以在其类路径上实例化两个具有不同版本的类加载器。但问题是 JVM 会将这两组类视为不同的类型,这将使​​针对这两个版本的静态绑定变得不可能。您可以通过反射来处理这个问题……但它会非常混乱,特别是如果相应的对象 API 很广泛的话。

  • 您可以将其作为一个两阶段的过程来完成。第 1 阶段是使用旧类加载,然后使用(比如说)您写入文件(比如说)的 JSON 序列化这些类。第 2 阶段是读取 JSON,使用它使用新类创建对象,并使用对象序列化对它们进行序列化。


第三种选择是编写一个调整序列化对象的临时转换器。基本上,您需要了解新旧序列化表单之间的差异,然后通过使用一些低级 API 读取/写入序列化对象来重写序列化对象。对此的一种变体是在新版本的类中实现自定义的 readObject 和方法,该类既能理解旧格式,也能理解新格式。但是,鉴于您的旧对象没有任何自定义版本字段,这可能会很棘手。需要注意的是,这种东西超出了序列化规范的范围......


但 IMO 最好的选择是以此为契机停止使用 Java 序列化。使用旧类读取对象,然后将它们写回数据库到常规 SQL 表中,作为 JSON 或 XML blob,或者使用 ORM 映射。

于 2013-04-06T01:47:48.600 回答
2

展望未来,我强烈推荐Thrift 之类的东西。它的开发部分是为了解决这个问题。

至于解决手头的问题,您可以还原更改吗?反序列化对象,将旧数据中的字段放入新对象,并序列化新对象。

于 2013-04-06T00:19:23.347 回答
1

在进行这些更改之前,您应该阅读的第一件事是对象序列化规范的对象版本控制章节。它准确地列出了在保持与现有序列化的兼容性的同时可以做什么和不能做什么。它允许的内容比大多数人想象的要多得多,但它不允许例如继承链中的更改,这听起来像是您所做的。

您必须做的第二件事是取消那些不符合要求的更改。

于 2013-04-06T00:30:17.200 回答