发往/来自Mongo的版本化对象/ JSON映射?

时间:2014-07-02 01:50:32

标签: java json mongodb serialization gson

所以我有一个需要存储某些配置信息的应用程序,因此我计划将配置存储为Mongo中的简单JSON文档:

appConfig: {
    fizz: true,
    buzz: 34
}

这可能映射到Java POJO /实体,如:

public class AppConfig {
    private boolean fizz;
    private int buzz;
}

等。通常,对于关系数据库,我使用Hibernate / JPA进行从表数据到/从Java实体的O / R映射。我相信最接近表/ Hibernate的JSON / Mongo伴侣是Morphia / GSON组合:使用Morphia驱动从我的Java应用程序到Mongo的连接,然后使用GSON将O / J映射到J / /来自Java POJO /实体。

这里的问题是,随着时间的推移,我的appConfig文档结构将会发生变化。它可能很简单:

appConfig: {
    fizz: true,
    buzz: 34
    foo: "Hello!"
}

然后需要POJO /实体成为:

public class AppConfig {
    private boolean fizz;
    private int buzz;
    private String foo;
}

但问题是我可能有成千上万的已经存储在Mongo 中的JSON文档中没有foo个属性。在这种特定情况下,显而易见的解决方案是在属性上设置默认值,如:

public class AppConfig {
    private boolean fizz;
    private int buzz;
    private String foo = "Hello!"
}

但实际上,AppConfig文档/架构/结构最终可能会发生很大变化,以至于它的形状或形式与其原始设计完全不同。但是踢球者是:我需要向后兼容,并且最好能够更新/转换文档以匹配适当的新模式/结构。

我的问题:这个"版本文件"问题通常会解决吗?

4 个答案:

答案 0 :(得分:5)

我通常通过向集合中的每个文档添加版本字段来解决此问题。

AppConfig集合中可能包含多个文档:

{
    _id: 1,
    fizz: true,
    buzz: 34
}

{
    _id: 2,
    version: 1,
    fizz: false,
    buzz: 36,
    foo: "Hello!"
}

{
    _id: 3,
    version: 1,
    fizz: true,
    buzz: 42,
    foo: "Goodbye"
}

在上面的例子中,版本1有两个文档,零版本有一个旧文档(在这个模式中,我通常将缺失或null版本字段解释为版本0,因为我总是只添加一次我“通过生产中的文档进行版本控制”。

这种模式的两个原则:

  1. 文档在实际修改后始终保存在最新版本中。
  2. 阅读文档时,如果文档不是最新版本,则会透明地升级到最新版本。
  3. 您可以通过检查版本字段并在版本不够新时执行迁移来执行此操作:

    DBObject update(DBObject document) {
        if (document.getInt("version", 0) < 1) {
            document.put("foo", "Hello!"); //add default value for foo
            document.put("version", 1);
        }
        return document;
    }
    

    此迁移可以非常轻松地添加具有默认值的字段,重命名字段和删除字段。由于它位于应用程序代码中,因此您可以根据需要进行更复杂的计算。

    迁移文档后,您可以通过任何您喜欢的ODM解决方案将其转换为Java对象。此解决方案不再需要担心版本控制,因为它处理的文档都是最新的!

    使用Morphia,可以使用@PreLoad注释完成此操作。

    两个警告:

    1. 有时您可能希望立即将升级后的文档保存回数据库。最常见的原因是迁移成本高昂,迁移不确定或与其他数据库集成,或者您急于升级旧版本。

    2. 添加或重命名在查询中用作条件的字段有点棘手。实际上,您可能需要执行多个查询,并统一结果。

    3. 在我看来,这种模式突出了MongoDB的一大优势:由于文档在应用程序中进行了版本化,因此您可以在应用程序中无缝迁移数据表示,而无需使用SQL所需的任何脱机“迁移阶段”数据库中。

答案 1 :(得分:2)

JSON反序列化器以一种非常简单的方式解决了这个问题(使用JAVA)

让您的POJO /实体与新字段一起成长。当您将JSON从mongo反序列化为实体时 - 所有缺少的字段都将为空。

    mongoDocument v1    :        Entity of v3
    {
      fizz="abc",        -->       fizz      = "abc";
      buzz=123           -->       buzz      = 123;
                         -->       newObj    = null;
                         -->       obj_v3    = null;

   }

如果您希望遗留服务器使用新的数据库对象,您甚至可以反过来使用它:

    mongoDocument v3     :        Entity of v1
    {
      fizz:"abc",        -->       fizz      = "abc";
      buzz:123,          -->       buzz      = 123;
      newObj:"zzz",      -->       
      obj_v3:"b          -->       

   }

取决于它们是否具有字段 - 它将由反序列化器填充。 请记住,布尔值不是最适合这种情况,因为它们可以默认为false(取决于您使用的解串器)。

因此,除非您正在积极地处理对象的版本控制,否则为什么在构建旧的安全服务器实现时会花费开销,只需几次空检查即可处理任何旧对象。

我希望此提案可以帮助您完成设置

答案 2 :(得分:1)

我想下面的线程会帮助你,虽然它不是关于DB中的文档版本,而是使用spring-data-mongodb完成的,

How to add a final field to an existing spring-data-mongodb document collection?

因此,您可以使用Converter实现基于文档中属性的存在为POJO分配值。

答案 3 :(得分:1)

至少,你有几种选择吗啡。您可以使用版本化的类名,然后依靠morphia使用className属性来获取正确的类版本。然后您的应用程序只需将旧对象迁移到新的类定义。另一个选择是使用@PreLoad并按摩来自mongo的DBObject到新形状,然后morphia将DBObject映射到您的班级。使用类上的版本字段,您可以确定在加载数据时要运行的迁移。从那时起,它将看起来像morphia的新形式,并将无缝地映射。将该配置对象保存回mongo后,它将以新的形式出现,下一次加载不需要运行迁移。